diff --git a/src/qweb/expression_parser.ts b/src/qweb/expression_parser.ts
index 92e0fc3a..a1b5f779 100644
--- a/src/qweb/expression_parser.ts
+++ b/src/qweb/expression_parser.ts
@@ -57,6 +57,7 @@ type TKind =
| "RIGHT_PAREN"
| "COMMA"
| "VALUE"
+ | "TEMPLATE_STRING"
| "SYMBOL"
| "OPERATOR"
| "COLON";
@@ -67,6 +68,7 @@ interface Token {
originalValue?: string;
size?: number;
varName?: string;
+ replace?: Function;
}
const STATIC_TOKEN_MAP: { [key: string]: TKind } = Object.assign(Object.create(null), {
@@ -89,7 +91,7 @@ type Tokenizer = (expr: string) => Token | false;
let tokenizeString: Tokenizer = function (expr) {
let s = expr[0];
let start = s;
- if (s !== "'" && s !== '"') {
+ if (s !== "'" && s !== '"' && s !== "`") {
return false;
}
let i = 1;
@@ -111,6 +113,17 @@ let tokenizeString: Tokenizer = function (expr) {
throw new Error("Invalid expression");
}
s += start;
+ if (start === "`") {
+ return {
+ type: "TEMPLATE_STRING",
+ value: s,
+ replace(replacer) {
+ return s.replace(/\$\{(.*?)\}/g, (match, group) => {
+ return "${" + replacer(group) + "}";
+ });
+ },
+ };
+ }
return { type: "VALUE", value: s };
};
@@ -267,6 +280,9 @@ export function compileExprToArray(expr: string, scope: { [key: string]: QWebVar
}
}
}
+ if (token.type === "TEMPLATE_STRING") {
+ token.value = token.replace((expr) => compileExpr(expr, scope));
+ }
if (nextToken && nextToken.type === "OPERATOR" && nextToken.value === "=>") {
if (token.type === "RIGHT_PAREN") {
let j = i - 1;
diff --git a/tests/qweb/__snapshots__/qweb.test.ts.snap b/tests/qweb/__snapshots__/qweb.test.ts.snap
index e061f6d8..4d1f07ee 100644
--- a/tests/qweb/__snapshots__/qweb.test.ts.snap
+++ b/tests/qweb/__snapshots__/qweb.test.ts.snap
@@ -1236,6 +1236,55 @@ exports[`static templates empty div 1`] = `
}"
`;
+exports[`static templates inline template string in t-esc 1`] = `
+"function anonymous(context, extra
+) {
+ // Template name: \\"test\\"
+ let scope = Object.create(context);
+ let result;
+ let h = this.h;
+ let _1 = \`text\`;
+ if (_1 != null) {
+ let vn2 = {text: _1};
+ result = vn2
+ }
+ return result;
+}"
+`;
+
+exports[`static templates inline template string with content in t-esc 1`] = `
+"function anonymous(context, extra
+) {
+ // Template name: \\"test\\"
+ let scope = Object.create(context);
+ let result;
+ let h = this.h;
+ scope.v = 1;
+ let _1 = \`text\${scope.v}\`;
+ if (_1 != null) {
+ let vn2 = {text: _1};
+ result = vn2
+ }
+ return result;
+}"
+`;
+
+exports[`static templates inline template string with variable in context 1`] = `
+"function anonymous(context, extra
+) {
+ // Template name: \\"test\\"
+ let scope = Object.create(context);
+ let result;
+ let h = this.h;
+ let _1 = \`text \${scope['v']}\`;
+ if (_1 != null) {
+ let vn2 = {text: _1};
+ result = vn2
+ }
+ return result;
+}"
+`;
+
exports[`static templates properly handle comments 1`] = `
"function anonymous(context, extra
) {
diff --git a/tests/qweb/qweb.test.ts b/tests/qweb/qweb.test.ts
index aaac2895..2615e711 100644
--- a/tests/qweb/qweb.test.ts
+++ b/tests/qweb/qweb.test.ts
@@ -31,6 +31,21 @@ describe("static templates", () => {
expect(renderToString(qweb, "test", { text: "hello vdom" })).toBe("hello vdom");
});
+ test("inline template string in t-esc", () => {
+ qweb.addTemplate("test", '');
+ expect(renderToString(qweb, "test")).toBe("text");
+ });
+
+ test("inline template string with content in t-esc", () => {
+ qweb.addTemplate("test", '');
+ expect(renderToString(qweb, "test")).toBe("text1");
+ });
+
+ test("inline template string with variable in context", () => {
+ qweb.addTemplate("test", '');
+ expect(renderToString(qweb, "test", { v: "from context" })).toBe("text from context");
+ });
+
test("simple string, with some dynamic value", () => {
qweb.addTemplate("test", 'hello ');
expect(renderToString(qweb, "test", { text: "vdom" })).toBe("hello vdom");
diff --git a/tests/qweb/qweb_expressions.test.ts b/tests/qweb/qweb_expressions.test.ts
index 2a494e50..f47856a5 100644
--- a/tests/qweb/qweb_expressions.test.ts
+++ b/tests/qweb/qweb_expressions.test.ts
@@ -206,4 +206,10 @@ describe("expression evaluation", () => {
expect(compileExpr("{a,b}", {})).toBe("{a:scope['a'],b:scope['b']}");
expect(compileExpr("{a,b:3,c}", {})).toBe("{a:scope['a'],b:3,c:scope['c']}");
});
+
+ test("template strings", () => {
+ expect(compileExpr("`hey`", {})).toBe("`hey`");
+ expect(compileExpr("`hey ${you}`", {})).toBe("`hey ${scope['you']}`");
+ expect(compileExpr("`hey ${1 + 2}`", {})).toBe("`hey ${1+2}`");
+ });
});
diff --git a/tools/playground/app.js b/tools/playground/app.js
index 79b60377..973eba65 100644
--- a/tools/playground/app.js
+++ b/tools/playground/app.js
@@ -72,7 +72,7 @@ if __name__ == "__main__":
* Make an iframe, with all the js, css and xml properly injected.
*/
function makeCodeIframe(js, css, xml) {
- const sanitizedXML = xml.replace(//g, "");
+ const sanitizedXML = xml.replace(//g, "").replace(/`/g, '\\\`');
// create iframe