From 01580f2fbca7c06f2e376aae29d08f30cd276a97 Mon Sep 17 00:00:00 2001 From: Mathieu Duckerts-Antoine Date: Tue, 7 Oct 2025 15:30:54 +0200 Subject: [PATCH 1/2] [IMP] qweb: Make t-call parametric, enable lazy XML evaluation The goal is to apply to OWL the same type of changes that were made in odoo/odoo#197296. Note that we have a stricter version here. In case some attrs are found on a t-call, the only parameters given to the template called are the one listed or found in the t-call-context. --- src/compiler/code_generator.ts | 67 +- src/compiler/parser.ts | 39 +- .../compiler/__snapshots__/misc.test.ts.snap | 4 +- .../__snapshots__/t_call.test.ts.snap | 54 +- .../__snapshots__/t_call2.test.ts.snap | 1395 +++++++++++++++++ .../compiler/__snapshots__/t_esc.test.ts.snap | 4 +- .../compiler/__snapshots__/t_out.test.ts.snap | 10 +- tests/compiler/parser.test.ts | 25 +- tests/compiler/t_call2.test.ts | 560 +++++++ .../__snapshots__/t_call.test.ts.snap | 2 +- 10 files changed, 2098 insertions(+), 62 deletions(-) create mode 100644 tests/compiler/__snapshots__/t_call2.test.ts.snap create mode 100644 tests/compiler/t_call2.test.ts diff --git a/src/compiler/code_generator.ts b/src/compiler/code_generator.ts index 8c696e940..22b4d4284 100644 --- a/src/compiler/code_generator.ts +++ b/src/compiler/code_generator.ts @@ -32,6 +32,8 @@ import { } from "./parser"; import { OwlError } from "../common/owl_error"; +const zero = Symbol("zero"); + type BlockType = "block" | "text" | "multi" | "list" | "html" | "comment"; const whitespaceRE = /\s+/g; @@ -279,7 +281,7 @@ export class CodeGenerator { translatableAttributes: string[] = TRANSLATABLE_ATTRS; ast: AST; staticDefs: { id: string; expr: string }[] = []; - slotNames: Set = new Set(); + slotNames: Set = new Set(); helpers: Set = new Set(); constructor(ast: AST, options: CodeGenOptions) { @@ -791,12 +793,22 @@ export class CodeGenerator { return block!.varName; } + compileZero() { + this.helpers.add("zero"); + const isMultiple = this.slotNames.has(zero); + this.slotNames.add(zero); + let key = this.target.loopLevel ? `key${this.target.loopLevel}` : "key"; + if (isMultiple) { + key = this.generateComponentKey(key); + } + return `ctx[zero]?.(node, ${key})`; + } + compileTEsc(ast: ASTTEsc, ctx: Context): string { let { block, forceNewBlock } = ctx; let expr: string; if (ast.expr === "0") { - this.helpers.add("zero"); - expr = `ctx[zero]`; + expr = this.compileZero(); } else { expr = compileExpr(ast.expr); if (ast.defaultValue) { @@ -824,8 +836,7 @@ export class CodeGenerator { block = this.createBlock(block, "html", ctx); let blockStr; if (ast.expr === "0") { - this.helpers.add("zero"); - blockStr = `ctx[zero]`; + blockStr = this.compileZero(); } else if (ast.body) { let bodyValue = null; bodyValue = BlockDescription.nextBlockId; @@ -1044,8 +1055,12 @@ export class CodeGenerator { compileTCall(ast: ASTTCall, ctx: Context): string { let { block, forceNewBlock } = ctx; + + const attrs: string[] = ast.attrs + ? this.formatPropObject(ast.attrs, ast.attrsTranslationCtx, ctx.translationCtx) + : []; let ctxVar = ctx.ctxVar || "ctx"; - if (ast.context) { + if (!ast.attrs && ast.context) { ctxVar = generateId("ctx"); this.addLine(`let ${ctxVar} = ${compileExpr(ast.context)};`); } @@ -1056,17 +1071,35 @@ export class CodeGenerator { } block = this.createBlock(block, "multi", ctx); if (ast.body) { - this.addLine(`${ctxVar} = Object.create(${ctxVar});`); - this.addLine(`${ctxVar}[isBoundary] = 1;`); - this.helpers.add("isBoundary"); - const subCtx = createContext(ctx, { ctxVar }); - const bl = this.compileMulti({ type: ASTType.Multi, content: ast.body }, subCtx); - if (bl) { + if (ast.attrs) { + const ctxStr = generateId("ctx"); + this.helpers.add("capture"); + this.define(ctxStr, `capture(ctx)`); + const name = this.compileInNewTarget("callBody", ast.body, ctx); + const zeroStr = generateId("lazyBlock"); + this.define(zeroStr, `${name}.bind(this, Object.create(${ctxStr}))`); this.helpers.add("zero"); - this.addLine(`${ctxVar}[zero] = ${bl};`); + attrs.push(`[zero]: ${zeroStr}`); + } else { + this.addLine(`${ctxVar} = Object.create(${ctxVar});`); + this.addLine(`${ctxVar}[isBoundary] = 1;`); + this.helpers.add("isBoundary"); + const subCtx = createContext(ctx, { ctxVar }); + const bl = this.compileAST(ast.body, subCtx); + if (bl) { + this.helpers.add("zero"); + this.addLine(`${ctxVar}[zero] = () => ${bl};`); + } } } + let ctxString = `{${attrs.join(", ")}}`; + if (ast.attrs && ast.context) { + const dynCtxVar = generateId("ctx"); + this.addLine(`const ${dynCtxVar} = ${compileExpr(ast.context)};`); + ctxString = `Object.assign({}, ${dynCtxVar}${attrs.length ? ", " + ctxString : ""})`; + } + const ctxExpr = ast.attrs ? ctxString : ctxVar; const key = this.generateComponentKey(); if (isDynamic) { const templateVar = generateId("template"); @@ -1074,20 +1107,20 @@ export class CodeGenerator { this.staticDefs.push({ id: "call", expr: `app.callTemplate.bind(app)` }); } this.define(templateVar, subTemplate); - this.insertBlock(`call(this, ${templateVar}, ${ctxVar}, node, ${key})`, block!, { + this.insertBlock(`call(this, ${templateVar}, ${ctxExpr}, node, ${key})`, block!, { ...ctx, forceNewBlock: !block, }); } else { const id = generateId(`callTemplate_`); this.staticDefs.push({ id, expr: `app.getTemplate(${subTemplate})` }); - this.insertBlock(`${id}.call(this, ${ctxVar}, node, ${key})`, block!, { + this.insertBlock(`${id}.call(this, ${ctxExpr}, node, ${key})`, block!, { ...ctx, forceNewBlock: !block, }); } - if (ast.body && !ctx.isLast) { - this.addLine(`${ctxVar} = ${ctxVar}.__proto__;`); + if (!ast.attrs && ast.body && !ctx.isLast) { + this.addLine(`${ctxExpr} = ${ctxExpr}.__proto__;`); } return block.varName; } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index aff0bc862..7e31c1f9b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -126,7 +126,9 @@ export interface ASTTKey extends BaseAST { export interface ASTTCall extends BaseAST { type: ASTType.TCall; name: string; - body: AST[] | null; + attrs: Attrs | null; + attrsTranslationCtx: Attrs | null; + body: AST | null; context: string | null; } @@ -642,9 +644,35 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null { node.removeAttribute("t-call"); node.removeAttribute("t-call-context"); + let attrs: Attrs | null = null; + let attrsTranslationCtx: Attrs | null = null; + for (let attributeName of node.getAttributeNames()) { + const value = node.getAttribute(attributeName)!; + if (attributeName.startsWith("t-translation-context-")) { + const attrName = attributeName.slice(22); + attrsTranslationCtx = attrsTranslationCtx || {}; + attrsTranslationCtx[attrName] = value; + } else { + attrs = attrs || {}; + attrs[attributeName] = value; + } + } + if (node.tagName !== "t") { + if (attrs) { + throw new OwlError( + `Directive 't-call' with params can only be used on nodes (used on a <${node.tagName}>)` + ); + } const ast = parseNode(node, ctx); - const tcall: AST = { type: ASTType.TCall, name: subTemplate, body: null, context }; + const tcall: AST = { + type: ASTType.TCall, + name: subTemplate, + attrs: null, + attrsTranslationCtx: null, + body: null, + context, + }; if (ast && ast.type === ASTType.DomNode) { ast.content = [tcall]; return ast; @@ -664,12 +692,13 @@ function parseTCall(node: Element, ctx: ParsingContext): AST | null { }; } } - const body = parseChildren(node, ctx); - + const body = parseChildNodes(node, ctx); return { type: ASTType.TCall, name: subTemplate, - body: body.length ? body : null, + attrs, + attrsTranslationCtx, + body, context, }; } diff --git a/tests/compiler/__snapshots__/misc.test.ts.snap b/tests/compiler/__snapshots__/misc.test.ts.snap index 2e4969c0b..11be0ea05 100644 --- a/tests/compiler/__snapshots__/misc.test.ts.snap +++ b/tests/compiler/__snapshots__/misc.test.ts.snap @@ -118,7 +118,7 @@ exports[`misc global 1`] = ` setContextValue(ctx, \\"foo\\", 'bbb'); const b9 = callTemplate_3.call(this, ctx, node, key + \`__3__\${key1}\`); const b6 = multi([b7, b8, b9]); - ctx[zero] = b6; + ctx[zero] = () => b6; const b5 = callTemplate_4.call(this, ctx, node, key + \`__4__\${key1}\`); ctx = ctx.__proto__; c_block2[i1] = withKey(multi([b4, b5]), key1); @@ -156,7 +156,7 @@ exports[`misc global 3`] = ` return function template(ctx, node, key = \\"\\") { let attr1 = 'agüero'; - const b2 = ctx[zero]; + const b2 = ctx[zero]?.(node, key); return block1([attr1], [b2]); } }" diff --git a/tests/compiler/__snapshots__/t_call.test.ts.snap b/tests/compiler/__snapshots__/t_call.test.ts.snap index 1d2fbefc8..fd81ff11a 100644 --- a/tests/compiler/__snapshots__/t_call.test.ts.snap +++ b/tests/compiler/__snapshots__/t_call.test.ts.snap @@ -71,7 +71,7 @@ exports[`t-call (template calling) call with several sub nodes on same line 1`] const b5 = text(\` \`); const b6 = block6(); const b3 = multi([b4, b5, b6]); - ctx[zero] = b3; + ctx[zero] = () => b3; const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } @@ -87,7 +87,7 @@ exports[`t-call (template calling) call with several sub nodes on same line 2`] let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - const b2 = ctx[zero]; + const b2 = ctx[zero]?.(node, key); return block1([], [b2]); } }" @@ -111,7 +111,7 @@ exports[`t-call (template calling) cascading t-call t-out='0' 1`] = ` const b5 = text(\` \`); const b6 = block6(); const b3 = multi([b4, b5, b6]); - ctx[zero] = b3; + ctx[zero] = () => b3; const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } @@ -132,9 +132,9 @@ exports[`t-call (template calling) cascading t-call t-out='0' 2`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; const b4 = block4(); - const b5 = ctx[zero]; + const b5 = ctx[zero]?.(node, key); const b3 = multi([b4, b5]); - ctx[zero] = b3; + ctx[zero] = () => b3; const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } @@ -155,9 +155,9 @@ exports[`t-call (template calling) cascading t-call t-out='0' 3`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; const b4 = block4(); - const b5 = ctx[zero]; + const b5 = ctx[zero]?.(node, key); const b3 = multi([b4, b5]); - ctx[zero] = b3; + ctx[zero] = () => b3; const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } @@ -173,7 +173,7 @@ exports[`t-call (template calling) cascading t-call t-out='0' 4`] = ` let block1 = createBlock(\`
cascade 2
\`); return function template(ctx, node, key = \\"\\") { - const b2 = ctx[zero]; + const b2 = ctx[zero]?.(node, key); return block1([], [b2]); } }" @@ -196,7 +196,7 @@ exports[`t-call (template calling) cascading t-call t-out='0', without external const b4 = text(\` \`); const b5 = block5(); const b2 = multi([b3, b4, b5]); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -215,9 +215,9 @@ exports[`t-call (template calling) cascading t-call t-out='0', without external ctx = Object.create(ctx); ctx[isBoundary] = 1; const b3 = block3(); - const b4 = ctx[zero]; + const b4 = ctx[zero]?.(node, key); const b2 = multi([b3, b4]); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -236,9 +236,9 @@ exports[`t-call (template calling) cascading t-call t-out='0', without external ctx = Object.create(ctx); ctx[isBoundary] = 1; const b3 = block3(); - const b4 = ctx[zero]; + const b4 = ctx[zero]?.(node, key); const b2 = multi([b3, b4]); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -254,7 +254,7 @@ exports[`t-call (template calling) cascading t-call t-out='0', without external return function template(ctx, node, key = \\"\\") { const b2 = block2(); - const b3 = ctx[zero]; + const b3 = ctx[zero]?.(node, key); return multi([b2, b3]); } }" @@ -350,10 +350,10 @@ exports[`t-call (template calling) nested t-calls with magic variable 0 1`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; const b3 = block3(); - ctx[zero] = b3; + ctx[zero] = () => b3; const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`); ctx = ctx.__proto__; - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_2.call(this, ctx, node, key + \`__2\`); } }" @@ -367,7 +367,7 @@ exports[`t-call (template calling) nested t-calls with magic variable 0 2`] = ` return function template(ctx, node, key = \\"\\") { const b2 = text(\`grandchild\`); - const b3 = ctx[zero]; + const b3 = ctx[zero]?.(node, key); return multi([b2, b3]); } }" @@ -380,7 +380,7 @@ exports[`t-call (template calling) nested t-calls with magic variable 0 3`] = ` let { zero } = helpers; return function template(ctx, node, key = \\"\\") { - return ctx[zero]; + return ctx[zero]?.(node, key); } }" `; @@ -590,7 +590,7 @@ exports[`t-call (template calling) root t-call with body: t-foreach 1`] = ` } ctx = ctx.__proto__; const b2 = list(c_block2); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -622,7 +622,7 @@ exports[`t-call (template calling) root t-call with body: t-if false 1`] = ` b3 = text(\`zero\`); } const b2 = multi([b3]); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -654,7 +654,7 @@ exports[`t-call (template calling) root t-call with body: t-if true 1`] = ` b3 = text(\`zero\`); } const b2 = multi([b3]); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -682,7 +682,7 @@ exports[`t-call (template calling) root t-call with body: t-out with default 1`] ctx = Object.create(ctx); ctx[isBoundary] = 1; const b2 = safeOutput(ctx['nothing']); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -839,7 +839,7 @@ exports[`t-call (template calling) t-call with body content as root of a templat ctx = Object.create(ctx); ctx[isBoundary] = 1; const b2 = block2(); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -854,7 +854,7 @@ exports[`t-call (template calling) t-call with body content as root of a templat let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - const b2 = ctx[zero]; + const b2 = ctx[zero]?.(node, key); return block1([], [b2]); } }" @@ -1204,7 +1204,7 @@ exports[`t-call (template calling) with unused body 1`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; const b2 = text(\`WHEEE\`); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -1265,7 +1265,7 @@ exports[`t-call (template calling) with used body 1`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; const b2 = text(\`ok\`); - ctx[zero] = b2; + ctx[zero] = () => b2; return callTemplate_1.call(this, ctx, node, key + \`__1\`); } }" @@ -1280,7 +1280,7 @@ exports[`t-call (template calling) with used body 2`] = ` let block1 = createBlock(\`

\`); return function template(ctx, node, key = \\"\\") { - let txt1 = ctx[zero]; + let txt1 = ctx[zero]?.(node, key); return block1([txt1]); } }" diff --git a/tests/compiler/__snapshots__/t_call2.test.ts.snap b/tests/compiler/__snapshots__/t_call2.test.ts.snap new file mode 100644 index 000000000..d62d8f828 --- /dev/null +++ b/tests/compiler/__snapshots__/t_call2.test.ts.snap @@ -0,0 +1,1395 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`t-call v_2 (template calling) basic caller 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`_basic-callee\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + const b2 = callTemplate_1.call(this, {'v_2': 1}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) basic caller 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`ok\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`t-call v_2 (template calling) basic caller, no parent node 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`_basic-callee\`); + + return function template(ctx, node, key = \\"\\") { + return callTemplate_1.call(this, {'v_2': 1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) basic caller, no parent node 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`ok\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`t-call v_2 (template calling) call with several sub nodes on same line 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + let block4 = createBlock(\`hey\`); + let block6 = createBlock(\`yay\`); + + function callBody1(ctx, node, key = \\"\\") { + const b4 = block4(); + const b5 = text(\` \`); + const b6 = block6(); + return multi([b4, b5, b6]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + const b2 = callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) call with several sub nodes on same line 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + const b2 = ctx[zero]?.(node, key); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) cascading t-call t-out='0' 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`subTemplate\`); + + let block1 = createBlock(\`
\`); + let block4 = createBlock(\`hey\`); + let block6 = createBlock(\`yay\`); + + function callBody1(ctx, node, key = \\"\\") { + const b4 = block4(); + const b5 = text(\` \`); + const b6 = block6(); + return multi([b4, b5, b6]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + const b2 = callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) cascading t-call t-out='0' 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`subSubTemplate\`); + + let block1 = createBlock(\`
\`); + let block4 = createBlock(\`cascade 0\`); + + function callBody1(ctx, node, key = \\"\\") { + const b4 = block4(); + const b5 = ctx[zero]?.(node, key); + return multi([b4, b5]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + const b2 = callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) cascading t-call t-out='0' 3`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`finalTemplate\`); + + let block1 = createBlock(\`
\`); + let block4 = createBlock(\`cascade 1\`); + + function callBody1(ctx, node, key = \\"\\") { + const b4 = block4(); + const b5 = ctx[zero]?.(node, key); + return multi([b4, b5]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + const b2 = callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) cascading t-call t-out='0' 4`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + let block1 = createBlock(\`
cascade 2
\`); + + return function template(ctx, node, key = \\"\\") { + const b2 = ctx[zero]?.(node, key); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) cascading t-call t-out='0', without external divs 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`subTemplate\`); + + let block3 = createBlock(\`hey\`); + let block5 = createBlock(\`yay\`); + + function callBody1(ctx, node, key = \\"\\") { + const b3 = block3(); + const b4 = text(\` \`); + const b5 = block5(); + return multi([b3, b4, b5]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) cascading t-call t-out='0', without external divs 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`subSubTemplate\`); + + let block3 = createBlock(\`cascade 0\`); + + function callBody1(ctx, node, key = \\"\\") { + const b3 = block3(); + const b4 = ctx[zero]?.(node, key); + return multi([b3, b4]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) cascading t-call t-out='0', without external divs 3`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`finalTemplate\`); + + let block3 = createBlock(\`cascade 1\`); + + function callBody1(ctx, node, key = \\"\\") { + const b3 = block3(); + const b4 = ctx[zero]?.(node, key); + return multi([b3, b4]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) cascading t-call t-out='0', without external divs 4`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + let block2 = createBlock(\`cascade 2\`); + + return function template(ctx, node, key = \\"\\") { + const b2 = block2(); + const b3 = ctx[zero]?.(node, key); + return multi([b2, b3]); + } +}" +`; + +exports[`t-call v_2 (template calling) does not inherit context 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { isBoundary, withDefault, setContextValue } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"foo\\", 1); + setContextValue(ctx, \\"bar\\", 2); + const b2 = callTemplate_1.call(this, {'v_2': 1, bar: ctx['bar']}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) does not inherit context 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + const b2 = text(ctx['foo']); + const b3 = text(ctx['bar']); + return multi([b2, b3]); + } +}" +`; + +exports[`t-call v_2 (template calling) dynamic t-call 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const call = app.callTemplate.bind(app); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + const template1 = (ctx['template']); + const b2 = call(this, template1, {'v_2': 1, val: ctx['val']}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) dynamic t-call 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['val']; + return block1([txt1]); + } +}" +`; + +exports[`t-call v_2 (template calling) dynamic t-call 3`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['val']; + return block1([txt1]); + } +}" +`; + +exports[`t-call v_2 (template calling) nested t-calls with magic variable 0 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`grandchild\`); + const callTemplate_2 = app.getTemplate(\`child\`); + + let block3 = createBlock(\`

Some content...

\`); + + function callBody1(ctx, node, key = \\"\\") { + const ctx2 = capture(ctx); + const lazyBlock1 = callBody2.bind(this, Object.create(ctx2)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } + + function callBody2(ctx, node, key = \\"\\") { + return block3(); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock2 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_2.call(this, {'v_2': 1, [zero]: lazyBlock2}, node, key + \`__2\`); + } +}" +`; + +exports[`t-call v_2 (template calling) nested t-calls with magic variable 0 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + return function template(ctx, node, key = \\"\\") { + const b2 = text(\`grandchild\`); + const b3 = ctx[zero]?.(node, key); + return multi([b2, b3]); + } +}" +`; + +exports[`t-call v_2 (template calling) nested t-calls with magic variable 0 3`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + return function template(ctx, node, key = \\"\\") { + return ctx[zero]?.(node, key); + } +}" +`; + +exports[`t-call v_2 (template calling) recursive template, part 1 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`recursive\`); + + let block1 = createBlock(\`
hey
\`); + + return function template(ctx, node, key = \\"\\") { + let b2; + if (false) { + b2 = callTemplate_1.call(this, {'v_2': 1}, node, key + \`__1\`); + } + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) recursive template, part 2 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`nodeTemplate\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + const b2 = callTemplate_1.call(this, {'v_2': 1, node: ctx['root']}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) recursive template, part 2 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { prepareList, withKey } = helpers; + const callTemplate_1 = app.getTemplate(\`nodeTemplate\`); + + let block1 = createBlock(\`

\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['node'].val; + ctx = Object.create(ctx); + const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['node'].children||[]);; + for (let i1 = 0; i1 < l_block2; i1++) { + ctx[\`subtree\`] = k_block2[i1]; + ctx[\`subtree_first\`] = i1 === 0; + ctx[\`subtree_last\`] = i1 === k_block2.length - 1; + ctx[\`subtree_index\`] = i1; + ctx[\`subtree_value\`] = v_block2[i1]; + const key1 = ctx['subtree_index']; + c_block2[i1] = withKey(callTemplate_1.call(this, {'v_2': 1, node: ctx['subtree']}, node, key + \`__1__\${key1}\`), key1); + } + const b2 = list(c_block2); + return block1([txt1], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) recursive template, part 3 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`nodeTemplate\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + const b2 = callTemplate_1.call(this, {'v_2': 1, node: ctx['root']}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) recursive template, part 3 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { prepareList, withKey } = helpers; + const callTemplate_1 = app.getTemplate(\`nodeTemplate\`); + + let block1 = createBlock(\`

\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['node'].val; + ctx = Object.create(ctx); + const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['node'].children||[]);; + for (let i1 = 0; i1 < l_block2; i1++) { + ctx[\`subtree\`] = k_block2[i1]; + ctx[\`subtree_first\`] = i1 === 0; + ctx[\`subtree_last\`] = i1 === k_block2.length - 1; + ctx[\`subtree_index\`] = i1; + ctx[\`subtree_value\`] = v_block2[i1]; + const key1 = ctx['subtree_index']; + c_block2[i1] = withKey(callTemplate_1.call(this, {'v_2': 1, node: ctx['subtree']}, node, key + \`__1__\${key1}\`), key1); + } + const b2 = list(c_block2); + return block1([txt1], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) recursive template, part 4: with t-set recursive index 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`nodeTemplate\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + const b2 = callTemplate_1.call(this, {'v_2': 1, recursive_idx: 1, node: ctx['root']}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) recursive template, part 4: with t-set recursive index 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { isBoundary, withDefault, setContextValue, prepareList, withKey } = helpers; + const callTemplate_1 = app.getTemplate(\`nodeTemplate\`); + + let block1 = createBlock(\`

\`); + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"recursive_idx\\", ctx['recursive_idx']+1); + let txt1 = ctx['node'].val; + let txt2 = ctx['recursive_idx']; + ctx = Object.create(ctx); + const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['node'].children||[]);; + for (let i1 = 0; i1 < l_block2; i1++) { + ctx[\`subtree\`] = k_block2[i1]; + ctx[\`subtree_first\`] = i1 === 0; + ctx[\`subtree_last\`] = i1 === k_block2.length - 1; + ctx[\`subtree_index\`] = i1; + ctx[\`subtree_value\`] = v_block2[i1]; + const key1 = ctx['subtree_index']; + c_block2[i1] = withKey(callTemplate_1.call(this, {'v_2': 1, node: ctx['subtree'], recursive_idx: ctx['recursive_idx']}, node, key + \`__1__\${key1}\`), key1); + } + const b2 = list(c_block2); + return block1([txt1, txt2], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) root t-call with body: t-foreach 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, prepareList, withKey, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`subTemplate\`); + + function callBody1(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + const [k_block2, v_block2, l_block2, c_block2] = prepareList([1]);; + for (let i1 = 0; i1 < l_block2; i1++) { + ctx[\`i\`] = k_block2[i1]; + const key1 = ctx['i']; + c_block2[i1] = withKey(text(\`1\`), key1); + } + ctx = ctx.__proto__; + return list(c_block2); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) root t-call with body: t-foreach 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`sub\`); + } +}" +`; + +exports[`t-call v_2 (template calling) root t-call with body: t-if false 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`subTemplate\`); + + function callBody1(ctx, node, key = \\"\\") { + let b3; + if (false) { + b3 = text(\`zero\`); + } + return multi([b3]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) root t-call with body: t-if false 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`sub\`); + } +}" +`; + +exports[`t-call v_2 (template calling) root t-call with body: t-if true 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`subTemplate\`); + + function callBody1(ctx, node, key = \\"\\") { + let b3; + if (true) { + b3 = text(\`zero\`); + } + return multi([b3]); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) root t-call with body: t-if true 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`sub\`); + } +}" +`; + +exports[`t-call v_2 (template calling) root t-call with body: t-out with default 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, safeOutput, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`subTemplate\`); + + function callBody1(ctx, node, key = \\"\\") { + return safeOutput(ctx['nothing']); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) root t-call with body: t-out with default 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`sub\`); + } +}" +`; + +exports[`t-call v_2 (template calling) scoped parameters 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, isBoundary, withDefault, setContextValue, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + + function callBody1(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"foo\\", 42); + return text(''); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + const b2 = callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + let txt1 = ctx['foo']; + return block1([txt1], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) scoped parameters 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(\`ok\`); + } +}" +`; + +exports[`t-call v_2 (template calling) scoped parameters, part 2 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { isBoundary, withDefault, setContextValue, capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + + function callBody1(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"foo\\", 42); + return text(ctx['foo']); + } + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"foo\\", 11); + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + const b2 = callTemplate_1.call(this, {'v_2': 1, foo: 43, [zero]: lazyBlock1}, node, key + \`__1\`); + let txt1 = ctx['foo']; + return block1([txt1], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) scoped parameters, part 2 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + return function template(ctx, node, key = \\"\\") { + const b2 = text(ctx['foo']); + const b3 = ctx[zero]?.(node, key); + return multi([b2, b3]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call on a div with t-call-context 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + let ctx1 = ctx['obj']; + const b2 = callTemplate_1.call(this, ctx1, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call on a div with t-call-context 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['value']; + return block1([txt1]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with attributes 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`sub\`); + + return function template(ctx, node, key = \\"\\") { + return callTemplate_1.call(this, {'v_2': 1, 'v1': ctx['val1'], 'v2': ctx['val2']}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with attributes 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['v1']; + let txt2 = ctx['v2']; + return block1([txt1, txt2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with attributes and t-call-context 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`sub\`); + + return function template(ctx, node, key = \\"\\") { + const ctx1 = ctx['obj']; + return callTemplate_1.call(this, Object.assign({}, ctx1, {'v_2': 1, 'v1': ctx['val1'], 'v2': ctx['val2']}), node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with attributes and t-call-context 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['v1']; + let txt2 = ctx['v2']; + let txt3 = ctx['v3']; + let txt4 = ctx['v4']; + return block1([txt1, txt2, txt3, txt4]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with attributes and t-out='0' 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + function callBody1(ctx, node, key = \\"\\") { + return text(\`Hello\`); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with attributes and t-out='0' 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['v_2']; + const b2 = ctx[zero]?.(node, key); + return block1([txt1], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with body content as root of a template 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`antony\`); + + let block2 = createBlock(\`

antony

\`); + + function callBody1(ctx, node, key = \\"\\") { + return block2(); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with body content as root of a template 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + const b2 = ctx[zero]?.(node, key); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with t-if 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + let b2; + if (ctx['flag']) { + b2 = callTemplate_1.call(this, {'v_2': 1}, node, key + \`__1\`); + } + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with t-if 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`ok\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with t-set inside and body text content 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { isBoundary, withDefault, setContextValue } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"val\\", \`yip yip\`); + const b2 = callTemplate_1.call(this, {'v_2': 1, val: ctx['val']}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with t-set inside and body text content 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`

\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['val']; + return block1([txt1]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with t-set inside and outside 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { prepareList, isBoundary, withDefault, setContextValue, withKey } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + ctx = Object.create(ctx); + const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['list']);; + for (let i1 = 0; i1 < l_block2; i1++) { + ctx[\`v\`] = k_block2[i1]; + ctx[\`v_first\`] = i1 === 0; + ctx[\`v_last\`] = i1 === k_block2.length - 1; + ctx[\`v_index\`] = i1; + ctx[\`v_value\`] = v_block2[i1]; + const key1 = ctx['v_index']; + setContextValue(ctx, \\"val\\", ctx['v'].val); + c_block2[i1] = withKey(callTemplate_1.call(this, {'v_2': 1, 'val3': ctx['val']*3}, node, key + \`__1__\${key1}\`), key1); + } + const b2 = list(c_block2); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with t-set inside and outside 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['val3']; + return block1([txt1]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with t-set inside and outside. 2 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { isBoundary, withDefault, setContextValue } = helpers; + const callTemplate_1 = app.getTemplate(\`main\`); + + let block1 = createBlock(\`

\`); + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"w\\", 'fromwrapper'); + const b2 = callTemplate_1.call(this, {'v_2': 1, w: ctx['w'], list: ctx['list']}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with t-set inside and outside. 2 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { prepareList, isBoundary, withDefault, setContextValue, withKey } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + ctx = Object.create(ctx); + const [k_block2, v_block2, l_block2, c_block2] = prepareList(ctx['list']);; + for (let i1 = 0; i1 < l_block2; i1++) { + ctx[\`v\`] = k_block2[i1]; + ctx[\`v_first\`] = i1 === 0; + ctx[\`v_last\`] = i1 === k_block2.length - 1; + ctx[\`v_index\`] = i1; + ctx[\`v_value\`] = v_block2[i1]; + const key1 = ctx['v_index']; + setContextValue(ctx, \\"val\\", ctx['v'].val); + c_block2[i1] = withKey(callTemplate_1.call(this, {'v_2': 1, 'val3': ctx['val']*3, w: ctx['w']}, node, key + \`__1__\${key1}\`), key1); + } + const b2 = list(c_block2); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call with t-set inside and outside. 2 3`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block2 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['val3']; + const b2 = block2([txt1]); + const b3 = text(ctx['w']); + return multi([b2, b3]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call, conditional and t-set in t-call body 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { isBoundary, withDefault, setContextValue } = helpers; + const callTemplate_1 = app.getTemplate(\`callee1\`); + const callTemplate_2 = app.getTemplate(\`callee2\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + let b2, b3; + setContextValue(ctx, \\"v1\\", 'elif'); + if (ctx['v1']==='if') { + b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`); + } else if (ctx['v1']==='elif') { + ctx = Object.create(ctx); + ctx[isBoundary] = 1; + setContextValue(ctx, \\"v\\", 'success'); + b3 = callTemplate_2.call(this, ctx, node, key + \`__2\`); + ctx = ctx.__proto__; + } + return block1([], [b2, b3]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call, conditional and t-set in t-call body 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
callee1
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call, conditional and t-set in t-call body 3`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
callee2
\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['v']; + return block1([txt1]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call-context 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + const callTemplate_1 = app.getTemplate(\`sub\`); + + return function template(ctx, node, key = \\"\\") { + const ctx1 = ctx['obj']; + return callTemplate_1.call(this, Object.assign({}, ctx1, {'v_2': 1}), node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call-context 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['value']; + return block1([txt1]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call-context and value in body 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, isBoundary, withDefault, setContextValue, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + function callBody1(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"value2\\", ctx['aaron']); + return text(''); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + const ctx2 = ctx['obj']; + return callTemplate_1.call(this, Object.assign({}, ctx2, {'v_2': 1, [zero]: lazyBlock1}), node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call-context and value in body 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['value1']; + let txt2 = ctx['value2']; + return block1([txt1, txt2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-esc inside t-call, with t-set outside 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { isBoundary, withDefault, setContextValue } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"v\\", \`Hi\`); + const b2 = callTemplate_1.call(this, {'v_2': 1}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) t-esc inside t-call, with t-set outside 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['v']; + return block1([txt1]); + } +}" +`; + +exports[`t-call v_2 (template calling) values set in body are in call context 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, isBoundary, withDefault, setContextValue, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + let block1 = createBlock(\`\`); + + function callBody1(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"foo\\", 'ko'); + return text(''); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + const b2 = callTemplate_1.call(this, {'v_2': 1, foo: 'ok', [zero]: lazyBlock1}, node, key + \`__1\`); + return block1([], [b2]); + } +}" +`; + +exports[`t-call v_2 (template calling) values set in body are in call context 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + return function template(ctx, node, key = \\"\\") { + return text(ctx['foo']); + } +}" +`; + +exports[`t-call v_2 (template calling) with unused body 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + function callBody1(ctx, node, key = \\"\\") { + return text(\`WHEEE\`); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) with unused body 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
ok
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`t-call v_2 (template calling) with unused setbody 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, isBoundary, withDefault, setContextValue, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + function callBody1(ctx, node, key = \\"\\") { + ctx = Object.create(ctx); + ctx[isBoundary] = 1 + setContextValue(ctx, \\"qux\\", 3); + return text(''); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) with unused setbody 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
ok
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + +exports[`t-call v_2 (template calling) with used body 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + function callBody1(ctx, node, key = \\"\\") { + return text(\`ok\`); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {'v_2': 1, [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) with used body 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + let block1 = createBlock(\`

\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx[zero]?.(node, key); + return block1([txt1]); + } +}" +`; diff --git a/tests/compiler/__snapshots__/t_esc.test.ts.snap b/tests/compiler/__snapshots__/t_esc.test.ts.snap index f623df5f4..1507b4136 100644 --- a/tests/compiler/__snapshots__/t_esc.test.ts.snap +++ b/tests/compiler/__snapshots__/t_esc.test.ts.snap @@ -222,7 +222,7 @@ exports[`t-esc t-esc=0 is escaped 1`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; const b3 = block3(); - ctx[zero] = b3; + ctx[zero] = () => b3; const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } @@ -238,7 +238,7 @@ exports[`t-esc t-esc=0 is escaped 2`] = ` let block1 = createBlock(\`\`); return function template(ctx, node, key = \\"\\") { - let txt1 = ctx[zero]; + let txt1 = ctx[zero]?.(node, key); return block1([txt1]); } }" diff --git a/tests/compiler/__snapshots__/t_out.test.ts.snap b/tests/compiler/__snapshots__/t_out.test.ts.snap index 12e077344..d26161bd6 100644 --- a/tests/compiler/__snapshots__/t_out.test.ts.snap +++ b/tests/compiler/__snapshots__/t_out.test.ts.snap @@ -41,7 +41,7 @@ exports[`t-out multiple calls to t-out 1`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; const b3 = block3(); - ctx[zero] = b3; + ctx[zero] = () => b3; const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } @@ -57,8 +57,8 @@ exports[`t-out multiple calls to t-out 2`] = ` let block1 = createBlock(\`
Greeter
\`); return function template(ctx, node, key = \\"\\") { - const b2 = ctx[zero]; - const b3 = ctx[zero]; + const b2 = ctx[zero]?.(node, key); + const b3 = ctx[zero]?.(node, key + \`__1\`); return block1([], [b2, b3]); } }" @@ -108,7 +108,7 @@ exports[`t-out t-out 0 1`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; const b3 = block3(); - ctx[zero] = b3; + ctx[zero] = () => b3; const b2 = callTemplate_1.call(this, ctx, node, key + \`__1\`); return block1([], [b2]); } @@ -124,7 +124,7 @@ exports[`t-out t-out 0 2`] = ` let block1 = createBlock(\`
\`); return function template(ctx, node, key = \\"\\") { - const b2 = ctx[zero]; + const b2 = ctx[zero]?.(node, key); return block1([], [b2]); } }" diff --git a/tests/compiler/parser.test.ts b/tests/compiler/parser.test.ts index c75a5c336..c1453bb86 100644 --- a/tests/compiler/parser.test.ts +++ b/tests/compiler/parser.test.ts @@ -1082,6 +1082,8 @@ describe("qweb parser", () => { body: { type: ASTType.TCall, name: "blap", + attrs: null, + attrsTranslationCtx: null, body: null, context: null, }, @@ -1120,6 +1122,8 @@ describe("qweb parser", () => { expect(parse(``)).toEqual({ type: ASTType.TCall, name: "blabla", + attrs: null, + attrsTranslationCtx: null, body: null, context: null, }); @@ -1129,8 +1133,10 @@ describe("qweb parser", () => { expect(parse(`ok`)).toEqual({ type: ASTType.TCall, name: "sub", + attrs: null, + attrsTranslationCtx: null, context: null, - body: [{ type: ASTType.Text, value: "ok" }], + body: { type: ASTType.Text, value: "ok" }, }); }); @@ -1138,6 +1144,8 @@ describe("qweb parser", () => { expect(parse(``)).toEqual({ type: ASTType.TCall, name: "blabla", + attrs: null, + attrsTranslationCtx: null, body: null, context: "someContext", }); @@ -1158,6 +1166,8 @@ describe("qweb parser", () => { { type: ASTType.TCall, name: "blabla", + attrs: null, + attrsTranslationCtx: null, body: null, context: null, }, @@ -1174,6 +1184,8 @@ describe("qweb parser", () => { content: { type: ASTType.TCall, name: "blabla", + attrs: null, + attrsTranslationCtx: null, body: null, context: null, }, @@ -1493,7 +1505,7 @@ describe("qweb parser", () => { scope: null, }, }, - type: 11, + type: ASTType.TComponent, }); }); @@ -1697,7 +1709,14 @@ describe("qweb parser", () => { on: null, slots: { default: { - content: { body: null, name: "subTemplate", type: ASTType.TCall, context: null }, + content: { + body: null, + name: "subTemplate", + type: ASTType.TCall, + attrs: null, + attrsTranslationCtx: null, + context: null, + }, attrs: null, attrsTranslationCtx: null, scope: null, diff --git a/tests/compiler/t_call2.test.ts b/tests/compiler/t_call2.test.ts new file mode 100644 index 000000000..4984f1ba9 --- /dev/null +++ b/tests/compiler/t_call2.test.ts @@ -0,0 +1,560 @@ +import { snapshotEverything, TestContext } from "../helpers"; + +snapshotEverything(); + +// ----------------------------------------------------------------------------- +// t-call +// ----------------------------------------------------------------------------- + +describe("t-call v_2 (template calling)", () => { + test("basic caller", () => { + const context = new TestContext(); + context.addTemplate("_basic-callee", `ok`); + context.addTemplate("caller", `
`); + + expect(context.renderToString("caller")).toBe("
ok
"); + }); + + test("basic caller, no parent node", () => { + const context = new TestContext(); + context.addTemplate("_basic-callee", `ok`); + context.addTemplate("caller", ``); + + expect(context.renderToString("caller")).toBe("ok"); + }); + + test("t-esc inside t-call, with t-set outside", () => { + const context = new TestContext(); + const main = `
Hi
`; + context.addTemplate("main", main); + context.addTemplate("sub", ``); + + expect(context.renderToString("main")).toBe("
"); + }); + + test("t-call with t-if", () => { + const context = new TestContext(); + const main = '
'; + context.addTemplate("main", main); + context.addTemplate("sub", "ok"); + + expect(context.renderToString("main", { flag: true })).toBe("
ok
"); + }); + + test("t-call not allowed on a non t node", () => { + const context = new TestContext(); + const main = '
'; + context.addTemplate("main", main); + context.addTemplate("sub", "ok"); + + expect(() => context.renderToString("main")).toThrow( + "Directive 't-call' with params can only be used on nodes (used on a
)" + ); + }); + + test("with unused body", () => { + const context = new TestContext(); + const sub = "
ok
"; + const main = 'WHEEE'; + context.addTemplate("sub", sub); + context.addTemplate("main", main); + + expect(context.renderToString("main")).toBe("
ok
"); + }); + + test("with unused setbody", () => { + const context = new TestContext(); + const sub = "
ok
"; + const main = ``; + context.addTemplate("sub", sub); + context.addTemplate("main", main); + + expect(context.renderToString("main")).toBe("
ok
"); + }); + + test("with used body", () => { + const context = new TestContext(); + const sub = '

'; + const main = 'ok'; + context.addTemplate("sub", sub); + context.addTemplate("main", main); + + expect(context.renderToString("main")).toBe("

ok

"); + }); + + test("values set in body are in call context", () => { + const context = new TestContext(); + const sub = ''; + const main = ``; + context.addTemplate("sub", sub); + context.addTemplate("main", main); + + expect(context.renderToString("main")).toBe("ok"); + }); + + test("does not inherit context", () => { + const context = new TestContext(); + const sub = ''; + const main = `
`; + context.addTemplate("sub", sub); + context.addTemplate("main", main); + + expect(context.renderToString("main")).toBe("
2
"); + }); + + test("scoped parameters", () => { + const context = new TestContext(); + const sub = "ok"; + const main = ` +
+ + + + +
`; + context.addTemplate("sub", sub); + context.addTemplate("main", main); + + expect(context.renderToString("main")).toBe("
ok
"); + }); + + test("scoped parameters, part 2", () => { + const context = new TestContext(); + const sub = ''; + const main = ` +
+ + + + + + +
`; + context.addTemplate("sub", sub); + context.addTemplate("main", main); + + expect(context.renderToString("main")).toBe("
434211
"); + }); + + test("call with several sub nodes on same line", () => { + const context = new TestContext(); + const sub = ` +
+ +
`; + const main = ` +
+ + hey yay + +
`; + context.addTemplate("sub", sub); + context.addTemplate("main", main); + + const expected = "
hey yay
"; + expect(context.renderToString("main")).toBe(expected); + }); + + test("cascading t-call t-out='0'", () => { + const context = new TestContext(); + const finalTemplate = ` +
+ cascade 2 + +
`; + + const subSubTemplate = ` +
+ + cascade 1 + + +
`; + + const subTemplate = ` +
+ + cascade 0 + + +
`; + + const main = ` +
+ + hey yay + +
`; + + context.addTemplate("finalTemplate", finalTemplate); + context.addTemplate("subSubTemplate", subSubTemplate); + context.addTemplate("subTemplate", subTemplate); + context.addTemplate("main", main); + + const expected = + "
cascade 2cascade 1cascade 0hey yay
"; + expect(context.renderToString("main")).toBe(expected); + }); + + test("cascading t-call t-out='0', without external divs", () => { + const context = new TestContext(); + const finalTemplate = ` + cascade 2 + `; + + const subSubTemplate = ` + + cascade 1 + + `; + + const subTemplate = ` + + cascade 0 + + `; + + const main = ` + + hey yay + `; + + context.addTemplate("finalTemplate", finalTemplate); + context.addTemplate("subSubTemplate", subSubTemplate); + context.addTemplate("subTemplate", subTemplate); + context.addTemplate("main", main); + + const expected = + "cascade 2cascade 1cascade 0hey yay"; + expect(context.renderToString("main")).toBe(expected); + }); + + test("recursive template, part 1", () => { + const context = new TestContext(); + const recursive = ` +
+ hey + + + +
`; + + context.addTemplate("recursive", recursive); + + const expected = "
hey
"; + expect(context.renderToString("recursive")).toBe(expected); + }); + + test("recursive template, part 2", () => { + const context = new TestContext(); + const Parent = ` +
+ +
`; + + const nodeTemplate = ` +
+

+ + + +
`; + + context.addTemplate("Parent", Parent); + context.addTemplate("nodeTemplate", nodeTemplate); + + const root = { val: "a", children: [{ val: "b" }, { val: "c" }] }; + const expected = "

a

b

c

"; + expect(context.renderToString("Parent", { root })).toBe(expected); + }); + + test("recursive template, part 3", () => { + const context = new TestContext(); + const Parent = ` +
+ +
`; + + const nodeTemplate = ` +
+

+ + + +
`; + + context.addTemplate("Parent", Parent); + context.addTemplate("nodeTemplate", nodeTemplate); + + const root = { val: "a", children: [{ val: "b", children: [{ val: "d" }] }, { val: "c" }] }; + const expected = + "

a

b

d

c

"; + expect(context.renderToString("Parent", { root })).toBe(expected); + }); + + test("recursive template, part 4: with t-set recursive index", () => { + const context = new TestContext(); + const Parent = ` +
+ +
`; + + const nodeTemplate = ` +
+ +

+ + + +
`; + + context.addTemplate("Parent", Parent); + context.addTemplate("nodeTemplate", nodeTemplate); + + const root = { + val: "a", + children: [{ val: "b", children: [{ val: "c", children: [{ val: "d" }] }] }], + }; + const expected = + "

a 2

b 3

c 4

d 5

"; + expect(context.renderToString("Parent", { root })).toBe(expected); + }); + + test("t-call, conditional and t-set in t-call body", () => { + const context = new TestContext(); + const callee1 = `
callee1
`; + const callee2 = `
callee2
`; + const caller = ` +
+ + + + + +
`; + + context.addTemplate("callee1", callee1); + context.addTemplate("callee2", callee2); + context.addTemplate("caller", caller); + + const expected = `
callee2 success
`; + expect(context.renderToString("caller")).toBe(expected); + }); + + test("t-call with t-set inside and outside", () => { + const context = new TestContext(); + const main = ` +
+ + + + +
`; + const sub = ` + + + `; + + context.addTemplate("main", main); + context.addTemplate("sub", sub); + + const expected = "
369
"; + const ctx = { list: [{ val: 1 }, { val: 2 }, { val: 3 }] }; + expect(context.renderToString("main", ctx)).toBe(expected); + }); + + test("t-call with t-set inside and outside. 2", () => { + const context = new TestContext(); + const main = ` +
+ + + + +
`; + const sub = ` + + + + `; + const wrapper = `

`; + + context.addTemplate("main", main); + context.addTemplate("sub", sub); + context.addTemplate("wrapper", wrapper); + + const expected = + "

3fromwrapper6fromwrapper9fromwrapper

"; + const ctx = { list: [{ val: 1 }, { val: 2 }, { val: 3 }] }; + expect(context.renderToString("wrapper", ctx)).toBe(expected); + }); + + test("t-call with t-set inside and body text content", () => { + const context = new TestContext(); + const main = ` +
+ yip yip + + +
`; + const sub = `

`; + + context.addTemplate("main", main); + context.addTemplate("sub", sub); + + const expected = "

yip yip

"; + expect(context.renderToString("main")).toBe(expected); + }); + + test("t-call with body content as root of a template", () => { + const context = new TestContext(); + const antony = ``; + const main = `

antony

`; + context.addTemplate("antony", antony); + context.addTemplate("main", main); + const expected = "

antony

"; + expect(context.renderToString("main")).toBe(expected); + }); + + test("root t-call with body: t-if true", () => { + const context = new TestContext(); + const subTemplate = `sub`; + const main = `zero`; + context.addTemplate("subTemplate", subTemplate); + context.addTemplate("main", main); + const expected = "sub"; + expect(context.renderToString("main")).toBe(expected); + }); + + test("root t-call with body: t-if false", () => { + const context = new TestContext(); + const subTemplate = `sub`; + const main = `zero`; + context.addTemplate("subTemplate", subTemplate); + context.addTemplate("main", main); + const expected = "sub"; + expect(context.renderToString("main")).toBe(expected); + }); + + test("root t-call with body: t-out with default", () => { + const context = new TestContext(); + const subTemplate = `sub`; + const main = `default`; + context.addTemplate("subTemplate", subTemplate); + context.addTemplate("main", main); + const expected = "sub"; + expect(context.renderToString("main")).toBe(expected); + }); + + test("root t-call with body: t-foreach", () => { + const context = new TestContext(); + const subTemplate = `sub`; + const main = ` + 1 + `; + context.addTemplate("subTemplate", subTemplate); + context.addTemplate("main", main); + const expected = "sub"; + expect(context.renderToString("main")).toBe(expected); + }); + + test("dynamic t-call", () => { + const context = new TestContext(); + const foo = ``; + const bar = ``; + const main = `
`; + + context.addTemplate("foo", foo); + context.addTemplate("bar", bar); + context.addTemplate("main", main); + + const expected1 = "
foo
"; + expect(context.renderToString("main", { template: "foo", val: "foo" })).toBe(expected1); + const expected2 = "
quux
"; + expect(context.renderToString("main", { template: "bar", val: "quux" })).toBe(expected2); + }); + + test("t-call-context", () => { + const context = new TestContext(); + context.addTemplate("sub", ``); + context.addTemplate("main", ``); + + expect(context.renderToString("main", { obj: { value: 123 } })).toBe("123"); + }); + + test("t-call on a div with t-call-context", () => { + const context = new TestContext(); + context.addTemplate("sub", ``); + context.addTemplate("main", `
`); + + expect(context.renderToString("main", { obj: { value: 123 } })).toBe( + "
123
" + ); + }); + + test("t-call-context and value in body", () => { + const context = new TestContext(); + context.addTemplate("sub", ``); + context.addTemplate( + "main", + ` + + + ` + ); + + expect(context.renderToString("main", { obj: { value1: 123 }, aaron: "lucas" })).toBe( + "123" + ); + }); + + test("nested t-calls with magic variable 0", () => { + const context = new TestContext(); + context.addTemplate("grandchild", `grandchild`); + context.addTemplate("child", ``); + context.addTemplate( + "main", + ` + + +

Some content...

+
+
` + ); + + expect(context.renderToString("main")).toBe("grandchild

Some content...

"); + }); + + test("t-call with attributes", () => { + const context = new TestContext(); + context.addTemplate("sub", ``); + context.addTemplate("main", ``); + + expect(context.renderToString("main", { val1: "abc", val2: "def" })).toBe( + "abcdef" + ); + }); + + test("t-call with attributes and t-call-context", () => { + const context = new TestContext(); + context.addTemplate( + "sub", + `` + ); + context.addTemplate( + "main", + `` + ); + + expect( + context.renderToString("main", { obj: { v3: "ghi", v4: "jkl" }, val1: "abc", val2: "def" }) + ).toBe("abcdefghijkl"); + }); + + test("t-call with attributes and t-out='0'", () => { + const context = new TestContext(); + context.addTemplate("sub", ``); + context.addTemplate("main", `Hello`); + expect(context.renderToString("main")).toBe("1Hello"); + }); +}); diff --git a/tests/components/__snapshots__/t_call.test.ts.snap b/tests/components/__snapshots__/t_call.test.ts.snap index 5eb05c16a..05b9164e1 100644 --- a/tests/components/__snapshots__/t_call.test.ts.snap +++ b/tests/components/__snapshots__/t_call.test.ts.snap @@ -11,7 +11,7 @@ exports[`t-call dynamic t-call 1`] = ` ctx = Object.create(ctx); ctx[isBoundary] = 1; const b2 = text(\` owl \`); - ctx[zero] = b2; + ctx[zero] = () => b2; const template1 = (ctx['current'].template); return call(this, template1, ctx, node, key + \`__1\`); } From b97573c744509a240ecb72e1a97bf4f9122f7647 Mon Sep 17 00:00:00 2001 From: Mathieu Duckerts-Antoine Date: Thu, 23 Oct 2025 10:10:19 +0200 Subject: [PATCH 2/2] [WIP] fix problems with translation contexts + test --- src/compiler/parser.ts | 4 +-- .../__snapshots__/t_call2.test.ts.snap | 35 +++++++++++++++++++ tests/compiler/t_call2.test.ts | 14 ++++++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 7e31c1f9b..bf759b7bb 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -259,10 +259,10 @@ function parseNode(node: Node, ctx: ParsingContext): AST | null { parseTForEach(node, ctx) || parseTIf(node, ctx) || parseTPortal(node, ctx) || - parseTCall(node, ctx) || - parseTCallBlock(node, ctx) || parseTTranslation(node, ctx) || parseTTranslationContext(node, ctx) || + parseTCall(node, ctx) || + parseTCallBlock(node, ctx) || parseTKey(node, ctx) || parseTEscNode(node, ctx) || parseTOutNode(node, ctx) || diff --git a/tests/compiler/__snapshots__/t_call2.test.ts.snap b/tests/compiler/__snapshots__/t_call2.test.ts.snap index d62d8f828..f3ec3da06 100644 --- a/tests/compiler/__snapshots__/t_call2.test.ts.snap +++ b/tests/compiler/__snapshots__/t_call2.test.ts.snap @@ -772,6 +772,41 @@ exports[`t-call v_2 (template calling) scoped parameters, part 2 2`] = ` }" `; +exports[`t-call v_2 (template calling) t-call and translation contexts 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { capture, zero } = helpers; + const callTemplate_1 = app.getTemplate(\`sub\`); + + function callBody1(ctx, node, key = \\"\\") { + return text(\`jeu\`); + } + + return function template(ctx, node, key = \\"\\") { + const ctx1 = capture(ctx); + const lazyBlock1 = callBody1.bind(this, Object.create(ctx1)); + return callTemplate_1.call(this, {title: 'title', [zero]: lazyBlock1}, node, key + \`__1\`); + } +}" +`; + +exports[`t-call v_2 (template calling) t-call and translation contexts 2`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + let { zero } = helpers; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + let txt1 = ctx['title']; + let txt2 = ctx[zero]?.(node, key); + return block1([txt1, txt2]); + } +}" +`; + exports[`t-call v_2 (template calling) t-call on a div with t-call-context 1`] = ` "function anonymous(app, bdom, helpers ) { diff --git a/tests/compiler/t_call2.test.ts b/tests/compiler/t_call2.test.ts index 4984f1ba9..26f005d5b 100644 --- a/tests/compiler/t_call2.test.ts +++ b/tests/compiler/t_call2.test.ts @@ -557,4 +557,18 @@ describe("t-call v_2 (template calling)", () => { context.addTemplate("main", `Hello`); expect(context.renderToString("main")).toBe("1Hello"); }); + + test("t-call and translation contexts", () => { + const translateFn = jest.fn((expr: string, translationCtx: string) => + translationCtx === "fr" ? "jeu" : translationCtx === "pt" ? "título" : expr + ); + + const context = new TestContext({ translateFn }); + context.addTemplate("sub", ``); + context.addTemplate("main", `game`); + + expect(context.renderToString("main")).toBe( + "títulojeu" + ); + }); });