From bf3dbcc861367a38131ac0943ad49f60e2474912 Mon Sep 17 00:00:00 2001 From: Felix Arntz Date: Wed, 29 Jan 2025 15:22:25 -0800 Subject: [PATCH] Enhance withSyncEvent implementation and ensure the sync flag is maintained when proxying functions via withScope. --- packages/interactivity/src/utils.ts | 57 ++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/packages/interactivity/src/utils.ts b/packages/interactivity/src/utils.ts index 3ed58bd4c7d28b..57e7c68a456ba6 100644 --- a/packages/interactivity/src/utils.ts +++ b/packages/interactivity/src/utils.ts @@ -30,6 +30,10 @@ declare global { } } +interface SyncAwareFunction extends Function { + sync?: boolean; +} + /** * Executes a callback function after the next frame is rendered. * @@ -135,11 +139,14 @@ export function withScope< ? Promise< Return > : never; export function withScope< Func extends Function >( func: Func ): Func; +export function withScope< Func extends SyncAwareFunction >( func: Func ): Func; export function withScope( func: ( ...args: unknown[] ) => unknown ) { const scope = getScope(); const ns = getNamespace(); + + let wrapped: Function; if ( func?.constructor?.name === 'GeneratorFunction' ) { - return async ( ...args: Parameters< typeof func > ) => { + wrapped = async ( ...args: Parameters< typeof func > ) => { const gen = func( ...args ) as Generator; let value: any; let it: any; @@ -171,17 +178,28 @@ export function withScope( func: ( ...args: unknown[] ) => unknown ) { return value; }; + } else { + wrapped = ( ...args: Parameters< typeof func > ) => { + setNamespace( ns ); + setScope( scope ); + try { + return func( ...args ); + } finally { + resetNamespace(); + resetScope(); + } + }; } - return ( ...args: Parameters< typeof func > ) => { - setNamespace( ns ); - setScope( scope ); - try { - return func( ...args ); - } finally { - resetNamespace(); - resetScope(); - } - }; + + // If function was annotated via `withSyncEvent()`, maintain the annotation. + const syncAware = func as SyncAwareFunction; + if ( syncAware.sync ) { + const syncAwareWrapped = wrapped as SyncAwareFunction; + syncAwareWrapped.sync = true; + return syncAwareWrapped; + } + + return wrapped; } /** @@ -381,16 +399,19 @@ export const isPlainObject = ( * @param callback The event callback. * @return Wrapped event callback. */ -export const withSyncEvent = ( callback: Function ): Function => { +export function withSyncEvent( callback: Function ): SyncAwareFunction { + let wrapped: SyncAwareFunction; + if ( callback?.constructor?.name === 'GeneratorFunction' ) { - const wrapped = function* ( ...args: any[] ) { - yield* callback( ...args ); + wrapped = function* ( this: any, ...args: any[] ) { + yield* callback.apply( this, args ); + }; + } else { + wrapped = function ( this: any, ...args: any[] ) { + return callback.apply( this, args ); }; - wrapped.sync = true; - return wrapped; } - const wrapped = ( ...args: any[] ) => callback( ...args ); wrapped.sync = true; return wrapped; -}; +}