From 385e118e583815bc8191391b5280894cdc4d9feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Tue, 7 Jun 2022 17:25:23 +0200 Subject: [PATCH] [FIX] compiler: add support for #{...} in string interpolation --- doc/reference/templates.md | 5 +++-- src/compiler/code_generator.ts | 15 +++++++++------ src/compiler/inline_expressions.ts | 15 ++++++++++----- .../__snapshots__/attributes.test.ts.snap | 14 ++++++++++++++ .../compiler/__snapshots__/t_ref.test.ts.snap | 18 +++++++++++++++++- tests/compiler/attributes.test.ts | 6 ++++++ tests/compiler/t_ref.test.ts | 9 +++++++++ 7 files changed, 68 insertions(+), 14 deletions(-) diff --git a/doc/reference/templates.md b/doc/reference/templates.md index a2f400c3..f0af6699 100644 --- a/doc/reference/templates.md +++ b/doc/reference/templates.md @@ -290,10 +290,11 @@ If an expression evaluates to a falsy value, it will not be set at all: It is sometimes convenient to format an attribute with string interpolation. In that case, the `t-attf-` directive can be used. It is useful when we need to mix -literal and dynamic elements, such as css classes. +literal and dynamic elements, such as css classes. The dynamic elements can be +specified with either `{{...}}` or `#{...}`: ```xml -
+
``` diff --git a/src/compiler/code_generator.ts b/src/compiler/code_generator.ts index 766b4d06..f29d6ce9 100644 --- a/src/compiler/code_generator.ts +++ b/src/compiler/code_generator.ts @@ -1,4 +1,10 @@ -import { compileExpr, compileExprToArray, interpolate, INTERP_REGEXP } from "./inline_expressions"; +import { + compileExpr, + compileExprToArray, + interpolate, + INTERP_REGEXP, + replaceDynamicParts, +} from "./inline_expressions"; import { AST, ASTComment, @@ -606,11 +612,8 @@ export class CodeGenerator { this.target.hasRef = true; const isDynamic = INTERP_REGEXP.test(ast.ref); if (isDynamic) { - const str = ast.ref.replace( - INTERP_REGEXP, - (expr) => "${" + this.captureExpression(expr.slice(2, -2), true) + "}" - ); - const idx = block!.insertData(`(el) => refs[\`${str}\`] = el`, "ref"); + const str = replaceDynamicParts(ast.ref, (expr) => this.captureExpression(expr, true)); + const idx = block!.insertData(`(el) => refs[${str}] = el`, "ref"); attrs["block-ref"] = String(idx); } else { let name = ast.ref; diff --git a/src/compiler/inline_expressions.ts b/src/compiler/inline_expressions.ts index e4df91ac..cca8c2f4 100644 --- a/src/compiler/inline_expressions.ts +++ b/src/compiler/inline_expressions.ts @@ -355,15 +355,20 @@ export function compileExpr(expr: string): string { .join(""); } -export const INTERP_REGEXP = /\{\{.*?\}\}/g; -const INTERP_GROUP_REGEXP = /\{\{.*?\}\}/g; +export const INTERP_REGEXP = /\{\{.*?\}\}|\#\{.*?\}/g; -export function interpolate(s: string): string { +export function replaceDynamicParts(s: string, replacer: (s: string) => string) { let matches = s.match(INTERP_REGEXP); if (matches && matches[0].length === s.length) { - return `(${compileExpr(s.slice(2, -2))})`; + return `(${replacer(s.slice(2, matches[0][0] === "{" ? -2 : -1))})`; } - let r = s.replace(INTERP_GROUP_REGEXP, (s) => "${" + compileExpr(s.slice(2, -2)) + "}"); + let r = s.replace( + INTERP_REGEXP, + (s) => "${" + replacer(s.slice(2, s[0] === "{" ? -2 : -1)) + "}" + ); return "`" + r + "`"; } +export function interpolate(s: string): string { + return replaceDynamicParts(s, compileExpr); +} diff --git a/tests/compiler/__snapshots__/attributes.test.ts.snap b/tests/compiler/__snapshots__/attributes.test.ts.snap index 381886e8..941acce0 100644 --- a/tests/compiler/__snapshots__/attributes.test.ts.snap +++ b/tests/compiler/__snapshots__/attributes.test.ts.snap @@ -387,6 +387,20 @@ exports[`attributes static attributes with dashes 1`] = ` }" `; +exports[`attributes string interpolation, alternate syntax 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + let attr1 = \`b\${ctx['value']}r\`; + return block1([attr1]); + } +}" +`; + exports[`attributes t-att-class and class should combine together 1`] = ` "function anonymous(app, bdom, helpers ) { diff --git a/tests/compiler/__snapshots__/t_ref.test.ts.snap b/tests/compiler/__snapshots__/t_ref.test.ts.snap index adf155b6..db417dd1 100644 --- a/tests/compiler/__snapshots__/t_ref.test.ts.snap +++ b/tests/compiler/__snapshots__/t_ref.test.ts.snap @@ -16,6 +16,22 @@ exports[`t-ref can get a dynamic ref on a node 1`] = ` }" `; +exports[`t-ref can get a dynamic ref on a node, alternate syntax 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, component, comment } = bdom; + + let block1 = createBlock(\`
\`); + + return function template(ctx, node, key = \\"\\") { + const refs = ctx.__owl__.refs; + const v1 = ctx['id']; + let ref1 = (el) => refs[\`myspan\${v1}\`] = el; + return block1([ref1]); + } +}" +`; + exports[`t-ref can get a ref on a node 1`] = ` "function anonymous(app, bdom, helpers ) { @@ -99,7 +115,7 @@ exports[`t-ref refs in a loop 1`] = ` const key1 = ctx['item']; const tKey_1 = ctx['item']; const v1 = ctx['item']; - let ref1 = (el) => refs[\`\${v1}\`] = el; + let ref1 = (el) => refs[(v1)] = el; let txt1 = ctx['item']; c_block2[i1] = withKey(block3([ref1, txt1]), tKey_1 + key1); } diff --git a/tests/compiler/attributes.test.ts b/tests/compiler/attributes.test.ts index 79374134..72c01869 100644 --- a/tests/compiler/attributes.test.ts +++ b/tests/compiler/attributes.test.ts @@ -131,6 +131,12 @@ describe("attributes", () => { expect(result).toBe(`
`); }); + test("string interpolation, alternate syntax", () => { + const template = `
`; + const result = renderToString(template, { value: "a" }); + expect(result).toBe(`
`); + }); + test("t-attf-class", () => { const template = `
`; const result = renderToString(template); diff --git a/tests/compiler/t_ref.test.ts b/tests/compiler/t_ref.test.ts index cba367f4..0c4e1383 100644 --- a/tests/compiler/t_ref.test.ts +++ b/tests/compiler/t_ref.test.ts @@ -27,6 +27,15 @@ describe("t-ref", () => { expect(refs.myspan3.tagName).toBe("SPAN"); }); + test("can get a dynamic ref on a node, alternate syntax", () => { + const template = `
`; + const refs: any = {}; + const bdom = renderToBdom(template, { id: 3, __owl__: { refs } }); + expect(refs).toEqual({}); + mount(bdom, document.createElement("div")); + expect(refs.myspan3.tagName).toBe("SPAN"); + }); + test("refs in a loop", () => { const template = `