From fe046f4500d5e1e71f37dfd439db852e5a8d8feb Mon Sep 17 00:00:00 2001 From: Astrian Zheng Date: Wed, 21 May 2025 16:07:42 +1000 Subject: [PATCH] refactor: move arrow function handler logic to a dedicated utility function --- src/main.ts | 67 +---------------------- src/utils/index.ts | 2 + src/utils/processTemplateMarco.ts | 23 ++++++++ src/utils/setupArrowFunctionHandler.ts | 76 ++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 66 deletions(-) create mode 100644 src/utils/setupArrowFunctionHandler.ts diff --git a/src/main.ts b/src/main.ts index 86115ba..cdcbf00 100644 --- a/src/main.ts +++ b/src/main.ts @@ -137,7 +137,6 @@ export default (options: ComponentOptions) => { utils.processTemplateMacros(rootElement, this, { updateTextNode: this._updateTextNode.bind(this), setupAttributeBinding: this._setupAttributeBinding.bind(this), - setupArrowFunctionHandler: this._setupArrowFunctionHandler.bind(this), setupExpressionHandler: this._setupExpressionHandler.bind(this), setupFunctionCallHandler: this._setupFunctionCallHandler.bind(this), stateToElementsMap: this._stateToElementsMap, @@ -156,6 +155,7 @@ export default (options: ComponentOptions) => { this._extractStatePathsFromExpression.bind(this), states: this._states, triggerFunc: this.triggerFunc.bind(this), + setupArrowFunctionHandler: utils.setupArrowFunctionHandler.bind(this), }) } @@ -243,71 +243,6 @@ export default (options: ComponentOptions) => { ) } - // Handle arrow function - private _setupArrowFunctionHandler( - element: Element, - eventName: string, - handlerValue: string, - ) { - element.addEventListener(eventName, (event: Event) => { - try { - // Arrow function parsing - const splitted = handlerValue.split('=>') - if (splitted.length !== 2) { - throw new Error(`Invalid arrow function syntax: ${handlerValue}`) - } - const paramsStr = (() => { - if (splitted[0].includes('(')) return splitted[0].trim() - return `(${splitted[0].trim()})` - })() - const bodyStr = splitted[1].trim() - - // Check if the function body is wrapped in {} - const isMultiline = bodyStr.startsWith('{') && bodyStr.endsWith('}') - - // If it is a multiline function body, remove the outer braces - if (isMultiline) { - // Remove the outer braces - let bodyStr = handlerValue.split('=>')[1].trim() - bodyStr = bodyStr.substring(1, bodyStr.length - 1) - - // Build code for multiline arrow function - const functionCode = ` - return function${paramsStr} { - ${bodyStr} - } - ` - - // Create context object - const context = this._createHandlerContext(event, element) - - // Create and call function - const handlerFn = new Function(functionCode).call(null) - handlerFn.apply(context, [event]) - } else { - // Single line arrow function, directly return expression result - const functionCode = ` - return function${paramsStr} { - return ${bodyStr} - } - ` - - // Create context object - const context = this._createHandlerContext(event, element) - - // Create and call function - const handlerFn = new Function(functionCode).call(null) - handlerFn.apply(context, [event]) - } - } catch (err) { - console.error( - `Error executing arrow function handler: ${handlerValue}`, - err, - ) - } - }) - } - // Create handler context private _createHandlerContext(event: Event, element: Element) { // Basic context, including state diff --git a/src/utils/index.ts b/src/utils/index.ts index 46c846c..3d5f07c 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,9 +1,11 @@ import parseTemplate from './parseTemplate' import processTemplateMacros from './processTemplateMarco' import triggerDomUpdates from './triggerDomUpdates' +import setupArrowFunctionHandler from './setupArrowFunctionHandler' export default { parseTemplate, processTemplateMacros, triggerDomUpdates, + setupArrowFunctionHandler, } diff --git a/src/utils/processTemplateMarco.ts b/src/utils/processTemplateMarco.ts index b7f8e3e..74b0f09 100644 --- a/src/utils/processTemplateMarco.ts +++ b/src/utils/processTemplateMarco.ts @@ -13,6 +13,19 @@ export default function processTemplateMacros( element: Element, eventName: string, handlerValue: string, + ops: { + createHandlerContext: ( + event: Event, + element: Element, + ) => { + states: Record + stateToElementsMap: Record> + statesListeners: Record void> + setState: (keyPath: string, value: unknown) => void + getState: (keyPath: string) => unknown + triggerFunc: (eventName: string, ...args: unknown[]) => void + } + }, ) => void setupFunctionCallHandler: ( element: Element, @@ -153,6 +166,16 @@ export default function processTemplateMacros( currentElementNode, eventName, handlerValue, + { + createHandlerContext: (_event: Event, _element: Element) => ({ + states: options.states, + stateToElementsMap: options.stateToElementsMap, + statesListeners: options.stateListeners, + setState: context.setState.bind(context), + getState: context.getState.bind(context), + triggerFunc: options.triggerFunc.bind(context), + }), + }, ) } else if (handlerValue.includes('(') && handlerValue.includes(')')) { // Handle function call: @click="increment(5)" diff --git a/src/utils/setupArrowFunctionHandler.ts b/src/utils/setupArrowFunctionHandler.ts new file mode 100644 index 0000000..a7eb8e1 --- /dev/null +++ b/src/utils/setupArrowFunctionHandler.ts @@ -0,0 +1,76 @@ +export default function setupArrowFunctionHandler( + element: Element, + eventName: string, + handlerValue: string, + ops: { + createHandlerContext: ( + event: Event, + element: Element, + ) => { + states: Record + stateToElementsMap: Record> + statesListeners: Record void> + setState: (keyPath: string, value: unknown) => void + getState: (keyPath: string) => unknown + triggerFunc: (eventName: string, ...args: unknown[]) => void + } + }, +) { + element.addEventListener(eventName, (event: Event) => { + try { + // Arrow function parsing + const splitted = handlerValue.split('=>') + if (splitted.length !== 2) { + throw new Error(`Invalid arrow function syntax: ${handlerValue}`) + } + const paramsStr = (() => { + if (splitted[0].includes('(')) return splitted[0].trim() + return `(${splitted[0].trim()})` + })() + const bodyStr = splitted[1].trim() + + // Check if the function body is wrapped in {} + const isMultiline = bodyStr.startsWith('{') && bodyStr.endsWith('}') + + // If it is a multiline function body, remove the outer braces + if (isMultiline) { + // Remove the outer braces + let bodyStr = handlerValue.split('=>')[1].trim() + bodyStr = bodyStr.substring(1, bodyStr.length - 1) + + // Build code for multiline arrow function + const functionCode = ` + return function${paramsStr} { + ${bodyStr} + } + ` + + // Create context object + const context = ops.createHandlerContext(event, element) + + // Create and call function + const handlerFn = new Function(functionCode).call(null) + handlerFn.apply(context, [event]) + } else { + // Single line arrow function, directly return expression result + const functionCode = ` + return function${paramsStr} { + return ${bodyStr} + } + ` + + // Create context object + const context = ops.createHandlerContext(event, element) + + // Create and call function + const handlerFn = new Function(functionCode).call(null) + handlerFn.apply(context, [event]) + } + } catch (err) { + console.error( + `Error executing arrow function handler: ${handlerValue}`, + err, + ) + } + }) +}