From e611c20ab4e22be2fb85b8d57f4e8291f797c154 Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Fri, 12 Nov 2021 12:56:07 +0100 Subject: [PATCH] [IMP] qweb: turn handlers into function expressions only For the sake of consistency with vanilla JS, and to allow some things that were previously not possible. --- src/component/component_node.ts | 2 +- src/component/handler.ts | 11 +- src/qweb/compiler.ts | 30 +---- src/qweb/inline_expressions.ts | 8 ++ .../__snapshots__/basics.test.ts.snap | 6 +- .../__snapshots__/concurrency.test.ts.snap | 5 +- .../__snapshots__/event_handling.test.ts.snap | 12 +- .../higher_order_component.test.ts.snap | 3 +- .../__snapshots__/lifecycle.test.ts.snap | 3 +- .../__snapshots__/refs.test.ts.snap | 3 +- .../__snapshots__/slots.test.ts.snap | 40 +++--- .../__snapshots__/t_call.test.ts.snap | 21 +-- .../__snapshots__/t_component.test.ts.snap | 2 +- .../__snapshots__/t_key.test.ts.snap | 6 +- .../__snapshots__/t_model.test.ts.snap | 28 ++-- .../__snapshots__/t_on.test.ts.snap | 13 +- tests/components/basics.test.ts | 4 +- tests/components/event_handling.test.ts | 10 +- tests/components/t_call.test.ts | 2 +- tests/components/t_component.test.ts | 2 +- tests/components/t_on.test.ts | 9 +- tests/misc/__snapshots__/portal.test.ts.snap | 3 +- .../__snapshots__/event_handling.test.ts.snap | 120 +++++++++++------- tests/qweb/__snapshots__/misc.test.ts.snap | 53 +++++--- tests/qweb/__snapshots__/t_call.test.ts.snap | 66 +++++----- .../__snapshots__/t_debug_log.test.ts.snap | 2 +- tests/qweb/__snapshots__/t_esc.test.ts.snap | 2 +- .../qweb/__snapshots__/t_foreach.test.ts.snap | 4 +- tests/qweb/__snapshots__/t_raw.test.ts.snap | 2 +- tests/qweb/__snapshots__/t_ref.test.ts.snap | 2 +- tests/qweb/__snapshots__/t_set.test.ts.snap | 4 +- tests/qweb/event_handling.test.ts | 24 ++-- tests/qweb/inline_expressions.test.ts | 5 + 33 files changed, 280 insertions(+), 227 deletions(-) diff --git a/src/component/component_node.ts b/src/component/component_node.ts index 4aab5514..aa8e672c 100644 --- a/src/component/component_node.ts +++ b/src/component/component_node.ts @@ -93,7 +93,7 @@ export class ComponentNode implements VNode(); return tokens .map((tok) => { - if (tok.varName) { + if (tok.varName && !tok.isLocal) { if (!mapping.has(tok.varName)) { const varId = this.generateId("v"); mapping.set(tok.varName, varId); @@ -490,11 +489,6 @@ export class QWebCompiler { } generateHandlerCode(rawEvent: string, handler: string): string { - let args: string = ""; - const name: string = handler.replace(/\(.*\)/, function (_args) { - args = _args.slice(1, -1); - return ""; - }); const modifiers = rawEvent .split(".") .slice(1) @@ -503,22 +497,7 @@ export class QWebCompiler { if (modifiers.length) { modifiersCode = `${modifiers.join(",")}, `; } - const isMethodCall = name.match(FNAMEREGEXP); - if (isMethodCall) { - let handlerFn: string; - if (args) { - const argId = this.generateId("arg"); - this.addLine(`const ${argId} = [${compileExpr(args)}];`); - handlerFn = `'${name}', ${argId}`; - } else { - handlerFn = `'${name}'`; - } - return `[${modifiersCode}ctx, ${handlerFn!}]`; - } else { - let code = this.captureExpression(handler); - code = `{const res = (() => { return ${code} })(); if (typeof res === 'function') { res(e) }}`; - return `[${modifiersCode}(e) => ${code}]`; - } + return `[${modifiersCode}${this.captureExpression(handler)}, ctx]`; } compileTDomNode(ast: ASTDomNode, ctx: Context) { @@ -919,7 +898,10 @@ export class QWebCompiler { const id = this.generateId(`callTemplate_`); this.staticCalls.push({ id, template: subTemplate }); block = this.createBlock(block, "multi", ctx); - this.insertBlock(`${id}(ctx, node, ${key})`, block!, { ...ctx, forceNewBlock: !block }); + this.insertBlock(`${id}.call(this, ctx, node, ${key})`, block!, { + ...ctx, + forceNewBlock: !block, + }); } if (ast.body && !ctx.isLast) { this.addLine(`ctx = ctx.__proto__;`); diff --git a/src/qweb/inline_expressions.ts b/src/qweb/inline_expressions.ts index 24a533d8..835e4754 100644 --- a/src/qweb/inline_expressions.ts +++ b/src/qweb/inline_expressions.ts @@ -70,6 +70,7 @@ interface Token { size?: number; varName?: string; replace?: Function; + isLocal?: boolean; } const STATIC_TOKEN_MAP: { [key: string]: TKind } = Object.assign(Object.create(null), { @@ -325,6 +326,13 @@ export function compileExprToArray(expr: string): Token[] { } i++; } + // Mark all variables that have been used locally. + // This assumes the expression has only one scope (incorrect but "good enough for now") + for (const token of tokens) { + if (token.type === "SYMBOL" && localVars.has(token.value)) { + token.isLocal = true; + } + } return tokens; } diff --git a/tests/components/__snapshots__/basics.test.ts.snap b/tests/components/__snapshots__/basics.test.ts.snap index 19d66627..be9545ed 100644 --- a/tests/components/__snapshots__/basics.test.ts.snap +++ b/tests/components/__snapshots__/basics.test.ts.snap @@ -98,7 +98,7 @@ exports[`basics can be clicked on and updated 1`] = ` return function template(ctx, node, key = \\"\\") { let d1 = ctx['state'].counter; const v1 = ctx['state']; - let d2 = [(e) => {const res = (() => { return v1.counter++ })(); if (typeof res === 'function') { res(e) }}]; + let d2 = [()=>v1.counter++, ctx]; return block1([d1, d2]); } }" @@ -157,7 +157,7 @@ exports[`basics can inject values in tagged templates 2`] = ` const callTemplate_2 = getTemplate(\`__template__9\`); return function template(ctx, node, key = \\"\\") { - return callTemplate_2(ctx, node, key + \`__1\`); + return callTemplate_2.call(this, ctx, node, key + \`__1\`); } }" `; @@ -599,7 +599,7 @@ exports[`basics rerendering a widget with a sub widget 1`] = ` return function template(ctx, node, key = \\"\\") { let d1 = ctx['state'].counter; const v1 = ctx['state']; - let d2 = [(e) => {const res = (() => { return v1.counter++ })(); if (typeof res === 'function') { res(e) }}]; + let d2 = [()=>v1.counter++, ctx]; return block1([d1, d2]); } }" diff --git a/tests/components/__snapshots__/concurrency.test.ts.snap b/tests/components/__snapshots__/concurrency.test.ts.snap index 8e839c8a..931f5fb2 100644 --- a/tests/components/__snapshots__/concurrency.test.ts.snap +++ b/tests/components/__snapshots__/concurrency.test.ts.snap @@ -1130,9 +1130,10 @@ exports[`rendering component again in next microtick 2`] = ` return function template(ctx, node, key = \\"\\") { let b2; - let d1 = [ctx, 'onClick']; + const v1 = ctx['onClick']; + let d1 = [v1, ctx]; if (ctx['env'].flag) { - b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx); + b2 = component(\`Child\`, {}, key + \`__2\`, node, ctx); } return block1([d1], [b2]); } diff --git a/tests/components/__snapshots__/event_handling.test.ts.snap b/tests/components/__snapshots__/event_handling.test.ts.snap index 6c7670ca..7291bb07 100644 --- a/tests/components/__snapshots__/event_handling.test.ts.snap +++ b/tests/components/__snapshots__/event_handling.test.ts.snap @@ -23,8 +23,9 @@ exports[`event handling handler receive the event as argument 2`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'inc']; - let b2 = component(\`Child\`, {}, key + \`__1\`, node, ctx); + const v1 = ctx['inc']; + let d1 = [v1, ctx]; + let b2 = component(\`Child\`, {}, key + \`__2\`, node, ctx); let d2 = ctx['state'].value; return block1([d1, d2], [b2]); } @@ -42,7 +43,7 @@ exports[`event handling support for callable expression in event handler 1`] = ` return function template(ctx, node, key = \\"\\") { let d1 = ctx['state'].value; const v1 = ctx['obj']; - let d2 = [(e) => {const res = (() => { return v1.onInput })(); if (typeof res === 'function') { res(e) }}]; + let d2 = [v1.onInput, ctx]; return block1([d1, d2]); } }" @@ -63,8 +64,9 @@ exports[`event handling t-on with handler bound to dynamic argument on a t-forea for (let i1 = 0; i1 < l_block2; i1++) { ctx[\`item\`] = v_block2[i1]; let key1 = ctx['item']; - const arg1 = [ctx['item']]; - let d1 = [ctx, 'onClick', arg1]; + const v1 = ctx['onClick']; + const v2 = ctx['item']; + let d1 = [ev=>v1(v2,ev), ctx]; c_block2[i1] = withKey(block3([d1]), key1); } let b2 = list(c_block2); diff --git a/tests/components/__snapshots__/higher_order_component.test.ts.snap b/tests/components/__snapshots__/higher_order_component.test.ts.snap index 0cd93ca3..8ce47ed9 100644 --- a/tests/components/__snapshots__/higher_order_component.test.ts.snap +++ b/tests/components/__snapshots__/higher_order_component.test.ts.snap @@ -130,7 +130,8 @@ exports[`basics sub widget is interactive 1`] = ` let block1 = createBlock(\`child\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'inc']; + const v1 = ctx['inc']; + let d1 = [v1, ctx]; let d2 = ctx['state'].val; return block1([d1, d2]); } diff --git a/tests/components/__snapshots__/lifecycle.test.ts.snap b/tests/components/__snapshots__/lifecycle.test.ts.snap index 0d0fdbdc..51d76053 100644 --- a/tests/components/__snapshots__/lifecycle.test.ts.snap +++ b/tests/components/__snapshots__/lifecycle.test.ts.snap @@ -540,7 +540,8 @@ exports[`lifecycle hooks onRender 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'increment']; + const v1 = ctx['increment']; + let d1 = [v1, ctx]; let d2 = ctx['state'].value; return block1([d1, d2]); } diff --git a/tests/components/__snapshots__/refs.test.ts.snap b/tests/components/__snapshots__/refs.test.ts.snap index 9b9678be..7fc144f5 100644 --- a/tests/components/__snapshots__/refs.test.ts.snap +++ b/tests/components/__snapshots__/refs.test.ts.snap @@ -43,7 +43,8 @@ exports[`refs refs are properly bound in slots 2`] = ` const slot3 = ctx => (node, key) => { const refs = ctx.__owl__.refs - let d2 = [ctx, 'doSomething']; + const v4 = ctx['doSomething']; + let d2 = [v4, ctx]; let d3 = (el) => refs[\`myButton\`] = el; return block2([d2, d3]); } diff --git a/tests/components/__snapshots__/slots.test.ts.snap b/tests/components/__snapshots__/slots.test.ts.snap index d3424146..f718c7fc 100644 --- a/tests/components/__snapshots__/slots.test.ts.snap +++ b/tests/components/__snapshots__/slots.test.ts.snap @@ -389,9 +389,10 @@ exports[`slots dynamic t-slot call 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'toggle']; - const slot1 = (ctx['current'].slot); - let b2 = toggler(slot1, callSlot(ctx, node, key, slot1)); + const v1 = ctx['toggle']; + let d1 = [v1, ctx]; + const slot2 = (ctx['current'].slot); + let b2 = toggler(slot2, callSlot(ctx, node, key, slot2)); return block1([d1], [b2]); } }" @@ -435,13 +436,14 @@ exports[`slots dynamic t-slot call with default 1`] = ` let block1 = createBlock(\`\`); - const defaultSlot1 = ctx => { + const defaultSlot2 = ctx => { return text(\` owl \`); } return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'toggle']; - let b3 = callSlot(ctx, node, key, (ctx['current'].slot), defaultSlot1, true); + const v1 = ctx['toggle']; + let d1 = [v1, ctx]; + let b3 = callSlot(ctx, node, key, (ctx['current'].slot), defaultSlot2, true); return block1([d1], [b3]); } }" @@ -1124,7 +1126,7 @@ exports[`slots slot and (inline) t-call 3`] = ` let block1 = createBlock(\`
\`); const slot3 = ctx => (node, key) => { - return callTemplate_5(ctx, node, key + \`__4\`); + return callTemplate_5.call(this, ctx, node, key + \`__4\`); } return function template(ctx, node, key = \\"\\") { @@ -1210,7 +1212,7 @@ exports[`slots slot and t-call 3`] = ` let block1 = createBlock(\`
\`); const slot3 = ctx => (node, key) => { - return callTemplate_5(ctx, node, key + \`__4\`); + return callTemplate_5.call(this, ctx, node, key + \`__4\`); } return function template(ctx, node, key = \\"\\") { @@ -1295,14 +1297,15 @@ exports[`slots slot are properly rendered if inner props are changed 3`] = ` let block1 = createBlock(\`
\`); - const slot2 = ctx => (node, key) => { - return component(\`SomeComponent\`, {val: ctx['state'].val}, key + \`__3\`, node, ctx); + const slot3 = ctx => (node, key) => { + return component(\`SomeComponent\`, {val: ctx['state'].val}, key + \`__4\`, node, ctx); } return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'inc']; + const v1 = ctx['inc']; + let d1 = [v1, ctx]; let d2 = ctx['state'].val; - let b3 = assign(component(\`GenericComponent\`, {}, key + \`__1\`, node, ctx), {slots: {'default': slot2(ctx)}}); + let b3 = assign(component(\`GenericComponent\`, {}, key + \`__2\`, node, ctx), {slots: {'default': slot3(ctx)}}); return block1([d1, d2], [b3]); } }" @@ -1333,7 +1336,8 @@ exports[`slots slot content is bound to caller 2`] = ` let block1 = createBlock(\`\`); const slot2 = ctx => (node, key) => { - let d1 = [ctx, 'inc']; + const v3 = ctx['inc']; + let d1 = [v3, ctx]; return block1([d1]); } @@ -1434,7 +1438,7 @@ exports[`slots slot preserves properly parented relationship, even through t-cal let block1 = createBlock(\`
\`); const slot3 = ctx => (node, key) => { - return callTemplate_5(ctx, node, key + \`__4\`); + return callTemplate_5.call(this, ctx, node, key + \`__4\`); } return function template(ctx, node, key = \\"\\") { @@ -1503,7 +1507,8 @@ exports[`slots slots are rendered with proper context 2`] = ` let block2 = createBlock(\`\`); const slot3 = ctx => (node, key) => { - let d2 = [ctx, 'doSomething']; + const v4 = ctx['doSomething']; + let d2 = [v4, ctx]; return block2([d2]); } @@ -1984,7 +1989,7 @@ exports[`slots t-slot in recursive templates 2`] = ` ctx[isBoundary] = 1; setContextValue(ctx, \\"name\\", ctx['item'].name); setContextValue(ctx, \\"items\\", ctx['item'].children); - b6 = callTemplate_5(ctx, node, key + \`__4__\${key1}\`); + b6 = callTemplate_5.call(this, ctx, node, key + \`__4__\${key1}\`); ctx = ctx.__proto__; } c_block3[i1] = withKey(multi([b5, b6]), key1); @@ -2113,7 +2118,8 @@ exports[`slots t-slot scope context 2`] = ` let block1 = createBlock(\`
\`); const slot2 = ctx => (node, key) => { - let d1 = [ctx, 'onClick']; + const v3 = ctx['onClick']; + let d1 = [v3, ctx]; let b2 = callSlot(ctx, node, key, 'default'); return block1([d1], [b2]); } diff --git a/tests/components/__snapshots__/t_call.test.ts.snap b/tests/components/__snapshots__/t_call.test.ts.snap index 3ec2cecc..3f045a5f 100644 --- a/tests/components/__snapshots__/t_call.test.ts.snap +++ b/tests/components/__snapshots__/t_call.test.ts.snap @@ -52,7 +52,8 @@ exports[`t-call handlers are properly bound through a t-call 1`] = ` let block1 = createBlock(\`

lucas

\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'update']; + const v1 = ctx['update']; + let d1 = [v1, ctx]; return block1([d1]); } }" @@ -68,7 +69,7 @@ exports[`t-call handlers are properly bound through a t-call 2`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let b2 = callTemplate_2(ctx, node, key + \`__1\`); + let b2 = callTemplate_2.call(this, ctx, node, key + \`__1\`); let d1 = ctx['counter']; return block1([d1], [b2]); } @@ -84,8 +85,8 @@ exports[`t-call handlers with arguments are properly bound through a t-call 1`] let block1 = createBlock(\`

lucas

\`); return function template(ctx, node, key = \\"\\") { - const arg1 = [ctx['a']]; - let d1 = [ctx, 'update', arg1]; + const v1 = ctx['a']; + let d1 = [()=>this.update(v1), ctx]; return block1([d1]); } }" @@ -101,7 +102,7 @@ exports[`t-call handlers with arguments are properly bound through a t-call 2`] let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let b2 = callTemplate_2(ctx, node, key + \`__1\`); + let b2 = callTemplate_2.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } }" @@ -143,7 +144,7 @@ exports[`t-call parent is set within t-call 3`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let b2 = callTemplate_2(ctx, node, key + \`__1\`); + let b2 = callTemplate_2.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } }" @@ -183,7 +184,7 @@ exports[`t-call parent is set within t-call with no parentNode 3`] = ` const callTemplate_2 = getTemplate(\`__template__9\`); return function template(ctx, node, key = \\"\\") { - return callTemplate_2(ctx, node, key + \`__1\`); + return callTemplate_2.call(this, ctx, node, key + \`__1\`); } }" `; @@ -216,9 +217,9 @@ exports[`t-call sub components in two t-calls 2`] = ` return function template(ctx, node, key = \\"\\") { let b2,b3; if (ctx['state'].val===1) { - b2 = callTemplate_2(ctx, node, key + \`__1\`); + b2 = callTemplate_2.call(this, ctx, node, key + \`__1\`); } else { - let b4 = callTemplate_4(ctx, node, key + \`__3\`); + let b4 = callTemplate_4.call(this, ctx, node, key + \`__3\`); b3 = block3([], [b4]); } return multi([b2, b3]); @@ -284,7 +285,7 @@ exports[`t-call t-call in t-foreach and children component 3`] = ` ctx[\`val_index\`] = i1; ctx[\`val_value\`] = k_block2[i1]; let key1 = ctx['val']; - c_block2[i1] = withKey(callTemplate_2(ctx, node, key + \`__1__\${key1}\`), key1); + c_block2[i1] = withKey(callTemplate_2.call(this, ctx, node, key + \`__1__\${key1}\`), key1); } let b2 = list(c_block2); return block1([], [b2]); diff --git a/tests/components/__snapshots__/t_component.test.ts.snap b/tests/components/__snapshots__/t_component.test.ts.snap index 90fa231e..79241e2c 100644 --- a/tests/components/__snapshots__/t_component.test.ts.snap +++ b/tests/components/__snapshots__/t_component.test.ts.snap @@ -139,7 +139,7 @@ exports[`t-component modifying a sub widget 1`] = ` return function template(ctx, node, key = \\"\\") { let d1 = ctx['state'].counter; const v1 = ctx['state']; - let d2 = [(e) => {const res = (() => { return v1.counter++ })(); if (typeof res === 'function') { res(e) }}]; + let d2 = [()=>v1.counter++, ctx]; return block1([d1, d2]); } }" diff --git a/tests/components/__snapshots__/t_key.test.ts.snap b/tests/components/__snapshots__/t_key.test.ts.snap index 0202ed22..cd25411a 100644 --- a/tests/components/__snapshots__/t_key.test.ts.snap +++ b/tests/components/__snapshots__/t_key.test.ts.snap @@ -177,12 +177,12 @@ exports[`t-key t-key on multiple Components with t-call 1 3`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; setContextValue(ctx, \\"key\\", ctx['key1']); - let b2 = callTemplate_2(ctx, node, key + \`__1\`); + let b2 = callTemplate_2.call(this, ctx, node, key + \`__1\`); ctx = ctx.__proto__; ctx = Object.create(ctx); ctx[isBoundary] = 1; setContextValue(ctx, \\"key\\", ctx['key2']); - let b3 = callTemplate_4(ctx, node, key + \`__3\`); + let b3 = callTemplate_4.call(this, ctx, node, key + \`__3\`); return block1([], [b2, b3]); } }" @@ -229,7 +229,7 @@ exports[`t-key t-key on multiple Components with t-call 2 3`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let b2 = callTemplate_2(ctx, node, key + \`__1\`); + let b2 = callTemplate_2.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } }" diff --git a/tests/components/__snapshots__/t_model.test.ts.snap b/tests/components/__snapshots__/t_model.test.ts.snap index 975f9d35..5850dc8e 100644 --- a/tests/components/__snapshots__/t_model.test.ts.snap +++ b/tests/components/__snapshots__/t_model.test.ts.snap @@ -117,10 +117,11 @@ exports[`t-model directive can also define t-on directive on same event, part 1 let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'onInput']; - const bExpr1 = ctx['state']; + const v1 = ctx['onInput']; + let d1 = [v1, ctx]; + const bExpr2 = ctx['state']; let d2 = ctx['state']['text']; - let d3 = [(ev) => { bExpr1['text'] = ev.target.value; }]; + let d3 = [(ev) => { bExpr2['text'] = ev.target.value; }]; return block1([d1, d2, d3]); } }" @@ -135,18 +136,21 @@ exports[`t-model directive can also define t-on directive on same event, part 2 let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'onClick']; - const bExpr1 = ctx['state']; - let d2 = ctx['state']['choice'] === 'One'; - let d3 = [(ev) => { bExpr1['choice'] = ev.target.value; }]; - let d4 = [ctx, 'onClick']; + const v1 = ctx['onClick']; + let d1 = [v1, ctx]; const bExpr2 = ctx['state']; + let d2 = ctx['state']['choice'] === 'One'; + let d3 = [(ev) => { bExpr2['choice'] = ev.target.value; }]; + const v3 = ctx['onClick']; + let d4 = [v3, ctx]; + const bExpr4 = ctx['state']; let d5 = ctx['state']['choice'] === 'Two'; - let d6 = [(ev) => { bExpr2['choice'] = ev.target.value; }]; - let d7 = [ctx, 'onClick']; - const bExpr3 = ctx['state']; + let d6 = [(ev) => { bExpr4['choice'] = ev.target.value; }]; + const v5 = ctx['onClick']; + let d7 = [v5, ctx]; + const bExpr6 = ctx['state']; let d8 = ctx['state']['choice'] === 'Three'; - let d9 = [(ev) => { bExpr3['choice'] = ev.target.value; }]; + let d9 = [(ev) => { bExpr6['choice'] = ev.target.value; }]; return block1([d1, d2, d3, d4, d5, d6, d7, d8, d9]); } }" diff --git a/tests/components/__snapshots__/t_on.test.ts.snap b/tests/components/__snapshots__/t_on.test.ts.snap index ce02e7ad..a1f34bce 100644 --- a/tests/components/__snapshots__/t_on.test.ts.snap +++ b/tests/components/__snapshots__/t_on.test.ts.snap @@ -20,7 +20,7 @@ exports[`t-on t-on expression captured in t-foreach 1`] = ` let key1 = ctx['val']; const v1 = ctx['otherState']; const v2 = ctx['iter']; - let d1 = [(e) => {const res = (() => { return v1.vals.push(v2+'_'+v2) })(); if (typeof res === 'function') { res(e) }}]; + let d1 = [()=>v1.vals.push(v2+'_'+v2), ctx]; setContextValue(ctx, \\"iter\\", ctx['iter']+1); c_block2[i1] = withKey(block3([d1]), key1); } @@ -50,7 +50,7 @@ exports[`t-on t-on expression in t-foreach 1`] = ` let d2 = ctx['val']+''; const v1 = ctx['otherState']; const v2 = ctx['val']; - let d3 = [(e) => {const res = (() => { return v1.vals.push(v2) })(); if (typeof res === 'function') { res(e) }}]; + let d3 = [()=>v1.vals.push(v2), ctx]; c_block2[i1] = withKey(block3([d1, d2, d3]), key1); } let b2 = list(c_block2); @@ -84,7 +84,7 @@ exports[`t-on t-on expression in t-foreach with t-set 1`] = ` const v1 = ctx['otherState']; const v2 = ctx['val']; const v3 = ctx['bossa']; - let d3 = [(e) => {const res = (() => { return v1.vals.push(v2+'_'+v3) })(); if (typeof res === 'function') { res(e) }}]; + let d3 = [()=>v1.vals.push(v2+'_'+v3), ctx]; c_block2[i1] = withKey(block3([d1, d2, d3]), key1); } let b2 = list(c_block2); @@ -111,8 +111,8 @@ exports[`t-on t-on method call in t-foreach 1`] = ` let key1 = ctx['val']; let d1 = ctx['val_index']; let d2 = ctx['val']+''; - const arg1 = [ctx['val']]; - let d3 = [ctx, 'addVal', arg1]; + const v1 = ctx['val']; + let d3 = [()=>this.addVal(v1), ctx]; c_block2[i1] = withKey(block3([d1, d2, d3]), key1); } let b2 = list(c_block2); @@ -130,7 +130,8 @@ exports[`t-on t-on on destroyed components 1`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'onClick']; + const v1 = ctx['onClick']; + let d1 = [v1, ctx]; return block1([d1]); } }" diff --git a/tests/components/basics.test.ts b/tests/components/basics.test.ts index 698738ec..9620a3e6 100644 --- a/tests/components/basics.test.ts +++ b/tests/components/basics.test.ts @@ -370,7 +370,7 @@ describe("basics", () => { test("can be clicked on and updated", async () => { class Counter extends Component { static template = xml` -
`; +
`; state = useState({ counter: 0, }); @@ -387,7 +387,7 @@ describe("basics", () => { test("rerendering a widget with a sub widget", async () => { class Counter extends Component { static template = xml` -
`; +
`; state = useState({ counter: 0, }); diff --git a/tests/components/event_handling.test.ts b/tests/components/event_handling.test.ts index 995f19da..99e91dc0 100644 --- a/tests/components/event_handling.test.ts +++ b/tests/components/event_handling.test.ts @@ -52,22 +52,24 @@ describe("event handling", () => { }); test("t-on with handler bound to dynamic argument on a t-foreach", async () => { - expect.assertions(3); + let onClickArgs: [number, MouseEvent] | null = null; class Parent extends Component { static template = xml`
-
+
`; items = [1, 2, 3, 4]; onClick(n: number, ev: MouseEvent) { - expect(n).toBe(1); - expect(ev).toBeInstanceOf(MouseEvent); + onClickArgs = [n, ev]; } } await mount(Parent, fixture); + expect(onClickArgs).toBeNull(); (fixture.querySelector(".item")).click(); + expect(onClickArgs![0]).toBe(1); + expect(onClickArgs![1]).toBeInstanceOf(MouseEvent); }); }); diff --git a/tests/components/t_call.test.ts b/tests/components/t_call.test.ts index 1e24ef3a..82a23d86 100644 --- a/tests/components/t_call.test.ts +++ b/tests/components/t_call.test.ts @@ -145,7 +145,7 @@ describe("t-call", () => { }); test("handlers with arguments are properly bound through a t-call", async () => { - const sub = xml`

lucas

`; + const sub = xml`

lucas

`; let value: any = null; diff --git a/tests/components/t_component.test.ts b/tests/components/t_component.test.ts index 2fdacf8e..c3a9ff63 100644 --- a/tests/components/t_component.test.ts +++ b/tests/components/t_component.test.ts @@ -170,7 +170,7 @@ describe("t-component", () => { test("modifying a sub widget", async () => { class Counter extends Component { static template = xml` -
`; +
`; state = useState({ counter: 0, }); diff --git a/tests/components/t_on.test.ts b/tests/components/t_on.test.ts index f380cd9a..2b4158bb 100644 --- a/tests/components/t_on.test.ts +++ b/tests/components/t_on.test.ts @@ -47,7 +47,7 @@ describe("t-on", () => {
: - +
`; @@ -73,7 +73,7 @@ describe("t-on", () => {
: - +
`; @@ -97,13 +97,14 @@ describe("t-on", () => {
: - +
`; state = useState({ values: ["a", "b"] }); otherState = { vals: new Array() }; addVal(val: string) { + debugger; this.otherState.vals.push(val); } } @@ -124,7 +125,7 @@ describe("t-on", () => {
- +
diff --git a/tests/misc/__snapshots__/portal.test.ts.snap b/tests/misc/__snapshots__/portal.test.ts.snap index b7dfa754..83e1a870 100644 --- a/tests/misc/__snapshots__/portal.test.ts.snap +++ b/tests/misc/__snapshots__/portal.test.ts.snap @@ -9,7 +9,8 @@ exports[`Portal Portal composed with t-slot 1`] = ` let block1 = createBlock(\`
child2
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'onCustom']; + const v1 = ctx['onCustom']; + let d1 = [v1, ctx]; return block1([d1]); } }" diff --git a/tests/qweb/__snapshots__/event_handling.test.ts.snap b/tests/qweb/__snapshots__/event_handling.test.ts.snap index 922e4646..b1667691 100644 --- a/tests/qweb/__snapshots__/event_handling.test.ts.snap +++ b/tests/qweb/__snapshots__/event_handling.test.ts.snap @@ -9,7 +9,8 @@ exports[`t-on can bind event handler 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'add']; + const v1 = ctx['add']; + let d1 = [v1, ctx]; return block1([d1]); } }" @@ -24,8 +25,8 @@ exports[`t-on can bind handlers with arguments 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - const arg1 = [5]; - let d1 = [ctx, 'add', arg1]; + const v1 = ctx['add']; + let d1 = [()=>v1(5), ctx]; return block1([d1]); } }" @@ -40,8 +41,8 @@ exports[`t-on can bind handlers with empty object 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - const arg1 = [{}]; - let d1 = [ctx, 'doSomething', arg1]; + const v1 = ctx['doSomething']; + let d1 = [()=>v1({}), ctx]; return block1([d1]); } }" @@ -56,8 +57,8 @@ exports[`t-on can bind handlers with empty object (with non empty inner string) let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - const arg1 = [{}]; - let d1 = [ctx, 'doSomething', arg1]; + const v1 = ctx['doSomething']; + let d1 = [()=>v1({}), ctx]; return block1([d1]); } }" @@ -79,8 +80,9 @@ exports[`t-on can bind handlers with empty object (with non empty inner string) ctx[\`action\`] = v_block2[i1]; ctx[\`action_index\`] = i1; let key1 = ctx['action_index']; - const arg1 = [ctx['action']]; - let d1 = [ctx, 'activate', arg1]; + const v1 = ctx['activate']; + const v2 = ctx['action']; + let d1 = [()=>v1(v2), ctx]; c_block2[i1] = withKey(block3([d1]), key1); } let b2 = list(c_block2); @@ -98,8 +100,8 @@ exports[`t-on can bind handlers with object arguments 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - const arg1 = [{val:5}]; - let d1 = [ctx, 'add', arg1]; + const v1 = ctx['add']; + let d1 = [()=>v1({val:5}), ctx]; return block1([d1]); } }" @@ -114,8 +116,10 @@ exports[`t-on can bind two event handlers 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'handleClick']; - let d2 = [ctx, 'handleDblClick']; + const v1 = ctx['handleClick']; + let d1 = [v1, ctx]; + const v2 = ctx['handleDblClick']; + let d2 = [v2, ctx]; return block1([d1, d2]); } }" @@ -130,7 +134,8 @@ exports[`t-on handler is bound to proper owner 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'add']; + const v1 = ctx['add']; + let d1 = [v1, ctx]; return block1([d1]); } }" @@ -150,7 +155,8 @@ exports[`t-on handler is bound to proper owner, part 2 1`] = ` for (let i1 = 0; i1 < l_block1; i1++) { ctx[\`value\`] = v_block1[i1]; let key1 = ctx['value']; - let d1 = [ctx, 'add']; + const v1 = ctx['add']; + let d1 = [v1, ctx]; c_block1[i1] = withKey(block2([d1]), key1); } return list(c_block1); @@ -167,7 +173,8 @@ exports[`t-on handler is bound to proper owner, part 3 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'add']; + const v1 = ctx['add']; + let d1 = [v1, ctx]; return block1([d1]); } }" @@ -181,7 +188,7 @@ exports[`t-on handler is bound to proper owner, part 3 2`] = ` const callTemplate_2 = getTemplate(\`sub\`); return function template(ctx, node, key = \\"\\") { - return callTemplate_2(ctx, node, key + \`__1\`); + return callTemplate_2.call(this, ctx, node, key + \`__1\`); } }" `; @@ -195,7 +202,8 @@ exports[`t-on handler is bound to proper owner, part 4 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'add']; + const v1 = ctx['add']; + let d1 = [v1, ctx]; return block1([d1]); } }" @@ -218,7 +226,7 @@ exports[`t-on handler is bound to proper owner, part 4 2`] = ` ctx[\`value_index\`] = i1; ctx[\`value_value\`] = k_block1[i1]; let key1 = ctx['value']; - c_block1[i1] = withKey(callTemplate_2(ctx, node, key + \`__1__\${key1}\`), key1); + c_block1[i1] = withKey(callTemplate_2.call(this, ctx, node, key + \`__1__\${key1}\`), key1); } return list(c_block1); } @@ -234,7 +242,8 @@ exports[`t-on receive event in first argument 1`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'add']; + const v1 = ctx['add']; + let d1 = [v1, ctx]; return block1([d1]); } }" @@ -249,8 +258,10 @@ exports[`t-on t-on modifiers (native listener) basic support for native listener let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'divClicked']; - let d2 = [ctx, 'btnClicked']; + const v1 = ctx['divClicked']; + let d1 = [v1, ctx]; + const v2 = ctx['btnClicked']; + let d2 = [v2, ctx]; return block1([d1, d2]); } }" @@ -265,7 +276,8 @@ exports[`t-on t-on modifiers (native listener) t-on combined with t-esc 1`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'onClick']; + const v1 = ctx['onClick']; + let d1 = [v1, ctx]; let d2 = ctx['text']; return block1([d1, d2]); } @@ -281,7 +293,8 @@ exports[`t-on t-on modifiers (native listener) t-on combined with t-raw 1`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'onClick']; + const v1 = ctx['onClick']; + let d1 = [v1, ctx]; let b2 = html(ctx['html']); return block1([d1], [b2]); } @@ -297,8 +310,10 @@ exports[`t-on t-on modifiers (native listener) t-on with .capture modifier 1`] = let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [\\"capture\\", ctx, 'onCapture']; - let d2 = [ctx, 'doSomething']; + const v1 = ctx['onCapture']; + let d1 = [\\"capture\\", v1, ctx]; + const v2 = ctx['doSomething']; + let d2 = [v2, ctx]; return block1([d1, d2]); } }" @@ -313,7 +328,7 @@ exports[`t-on t-on modifiers (native listener) t-on with empty handler (only mod let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [\\"prevent\\", (e) => {const res = (() => { return })(); if (typeof res === 'function') { res(e) }}]; + let d1 = [\\"prevent\\", , ctx]; return block1([d1]); } }" @@ -328,7 +343,8 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and self modifi let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [\\"prevent\\",\\"self\\", ctx, 'onClick']; + const v1 = ctx['onClick']; + let d1 = [\\"prevent\\",\\"self\\", v1, ctx]; return block1([d1]); } }" @@ -343,9 +359,12 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent and/or stop mod let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [\\"prevent\\", ctx, 'onClickPrevented']; - let d2 = [\\"stop\\", ctx, 'onClickStopped']; - let d3 = [\\"prevent\\",\\"stop\\", ctx, 'onClickPreventedAndStopped']; + const v1 = ctx['onClickPrevented']; + let d1 = [\\"prevent\\", v1, ctx]; + const v2 = ctx['onClickStopped']; + let d2 = [\\"stop\\", v2, ctx]; + const v3 = ctx['onClickPreventedAndStopped']; + let d3 = [\\"prevent\\",\\"stop\\", v3, ctx]; return block1([d1, d2, d3]); } }" @@ -366,8 +385,9 @@ exports[`t-on t-on modifiers (native listener) t-on with prevent modifier in t-f for (let i1 = 0; i1 < l_block2; i1++) { ctx[\`project\`] = v_block2[i1]; let key1 = ctx['project']; - const arg1 = [ctx['project'].id]; - let d1 = [\\"prevent\\", ctx, 'onEdit', arg1]; + const v1 = ctx['onEdit']; + const v2 = ctx['project']; + let d1 = [\\"prevent\\", ev=>v1(v2.id,ev), ctx]; let d2 = ctx['project'].name; c_block2[i1] = withKey(block3([d1, d2]), key1); } @@ -386,7 +406,8 @@ exports[`t-on t-on modifiers (native listener) t-on with self and prevent modifi let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [\\"self\\",\\"prevent\\", ctx, 'onClick']; + const v1 = ctx['onClick']; + let d1 = [\\"self\\",\\"prevent\\", v1, ctx]; return block1([d1]); } }" @@ -401,8 +422,10 @@ exports[`t-on t-on modifiers (native listener) t-on with self modifier 1`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'onClick']; - let d2 = [\\"self\\", ctx, 'onClickSelf']; + const v1 = ctx['onClick']; + let d1 = [v1, ctx]; + const v2 = ctx['onClickSelf']; + let d2 = [\\"self\\", v2, ctx]; return block1([d1, d2]); } }" @@ -417,8 +440,10 @@ exports[`t-on t-on modifiers (synthetic listener) basic support for synthetic 1` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let d1 = [\\"synthetic\\", ctx, 'divClicked']; - let d2 = [\\"synthetic\\", ctx, 'btnClicked']; + const v1 = ctx['divClicked']; + let d1 = [\\"synthetic\\", v1, ctx]; + const v2 = ctx['btnClicked']; + let d2 = [\\"synthetic\\", v2, ctx]; return block1([d1, d2]); } }" @@ -434,7 +459,7 @@ exports[`t-on t-on with inline statement (function call) 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['state']; - let d1 = [(e) => {const res = (() => { return v1.incrementCounter(2) })(); if (typeof res === 'function') { res(e) }}]; + let d1 = [()=>v1.incrementCounter(2), ctx]; return block1([d1]); } }" @@ -450,7 +475,7 @@ exports[`t-on t-on with inline statement 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['state']; - let d1 = [(e) => {const res = (() => { return v1.counter++ })(); if (typeof res === 'function') { res(e) }}]; + let d1 = [()=>v1.counter++, ctx]; return block1([d1]); } }" @@ -466,7 +491,7 @@ exports[`t-on t-on with inline statement, part 2 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['state']; - let d1 = [(e) => {const res = (() => { return v1.flag=!v1.flag })(); if (typeof res === 'function') { res(e) }}]; + let d1 = [()=>v1.flag=!v1.flag, ctx]; return block1([d1]); } }" @@ -483,7 +508,7 @@ exports[`t-on t-on with inline statement, part 3 1`] = ` return function template(ctx, node, key = \\"\\") { const v1 = ctx['state']; const v2 = ctx['someFunction']; - let d1 = [(e) => {const res = (() => { return v1.n=v2(3) })(); if (typeof res === 'function') { res(e) }}]; + let d1 = [()=>v1.n=v2(3), ctx]; return block1([d1]); } }" @@ -498,7 +523,8 @@ exports[`t-on t-on with t-call 1`] = ` let block1 = createBlock(\`

lucas

\`); return function template(ctx, node, key = \\"\\") { - let d1 = [ctx, 'update']; + const v1 = ctx['update']; + let d1 = [v1, ctx]; return block1([d1]); } }" @@ -514,7 +540,7 @@ exports[`t-on t-on with t-call 2`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let b2 = callTemplate_2(ctx, node, key + \`__1\`); + let b2 = callTemplate_2.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } }" @@ -529,8 +555,8 @@ exports[`t-on t-on, with arguments and t-call 1`] = ` let block1 = createBlock(\`

lucas

\`); return function template(ctx, node, key = \\"\\") { - const arg1 = [ctx['value']]; - let d1 = [ctx, 'update', arg1]; + const v1 = ctx['value']; + let d1 = [()=>this.update(v1), ctx]; return block1([d1]); } }" @@ -546,7 +572,7 @@ exports[`t-on t-on, with arguments and t-call 2`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - let b2 = callTemplate_2(ctx, node, key + \`__1\`); + let b2 = callTemplate_2.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } }" diff --git a/tests/qweb/__snapshots__/misc.test.ts.snap b/tests/qweb/__snapshots__/misc.test.ts.snap index 729d1342..36964cba 100644 --- a/tests/qweb/__snapshots__/misc.test.ts.snap +++ b/tests/qweb/__snapshots__/misc.test.ts.snap @@ -158,20 +158,20 @@ exports[`misc global 4`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; setContextValue(ctx, \\"foo\\", 'aaa'); - let b6 = callTemplate_2(ctx, node, key + \`__1__\${key1}\`); + let b6 = callTemplate_2.call(this, ctx, node, key + \`__1__\${key1}\`); ctx = ctx.__proto__; - let b7 = callTemplate_4(ctx, node, key + \`__3__\${key1}\`); + let b7 = callTemplate_4.call(this, ctx, node, key + \`__3__\${key1}\`); setContextValue(ctx, \\"foo\\", 'bbb'); - let b8 = callTemplate_6(ctx, node, key + \`__5__\${key1}\`); + let b8 = callTemplate_6.call(this, ctx, node, key + \`__5__\${key1}\`); let b5 = multi([b6, b7, b8]); ctx[zero] = b5; - let b9 = callTemplate_8(ctx, node, key + \`__7__\${key1}\`); + let b9 = callTemplate_8.call(this, ctx, node, key + \`__7__\${key1}\`); ctx = ctx.__proto__; c_block2[i1] = withKey(multi([b4, b9]), key1); } ctx = ctx.__proto__; let b2 = list(c_block2); - let b10 = callTemplate_10(ctx, node, key + \`__9\`); + let b10 = callTemplate_10.call(this, ctx, node, key + \`__9\`); return block1([], [b2, b10]); } }" @@ -182,7 +182,7 @@ exports[`misc other complex template 1`] = ` ) { let { text, createBlock, list, multi, html, toggler, component } = bdom; let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers; - const callTemplate_3 = getTemplate(\`LOAD_INFOS_TEMPLATE\`); + const callTemplate_14 = getTemplate(\`LOAD_INFOS_TEMPLATE\`); let block1 = createBlock(\`