diff --git a/src/app/template_set.ts b/src/app/template_set.ts
index 887b5e92..7978174e 100644
--- a/src/app/template_set.ts
+++ b/src/app/template_set.ts
@@ -5,10 +5,10 @@ import { UTILS } from "./template_helpers";
const bdom = { text, createBlock, list, multi, html, toggler, component };
-export const globalTemplates: { [key: string]: string } = {};
+export const globalTemplates: { [key: string]: string | Node } = {};
export class TemplateSet {
- rawTemplates: { [name: string]: string } = Object.create(globalTemplates);
+ rawTemplates: typeof globalTemplates = Object.create(globalTemplates);
templates: { [name: string]: Template } = {};
translateFn?: (s: string) => string;
translatableAttributes?: string[];
@@ -25,13 +25,22 @@ export class TemplateSet {
this.utils = Object.assign({}, UTILS, { getTemplate, call });
}
- addTemplate(name: string, template: string, options: { allowDuplicate?: boolean } = {}) {
+ addTemplate(name: string, template: string | Node, options: { allowDuplicate?: boolean } = {}) {
if (name in this.rawTemplates && !options.allowDuplicate) {
throw new Error(`Template ${name} already defined`);
}
this.rawTemplates[name] = template;
}
+ addTemplates(xml: string | Document, options: { allowDuplicate?: boolean } = {}) {
+ xml = xml instanceof Document ? xml : new DOMParser().parseFromString(xml, "text/xml");
+ for (const template of xml.querySelectorAll("[t-name]")) {
+ const name = template.getAttribute("t-name")!;
+ template.removeAttribute("t-name");
+ this.addTemplate(name, template, options);
+ }
+ }
+
getTemplate(name: string): Template {
if (!(name in this.templates)) {
const rawTemplate = this.rawTemplates[name];
diff --git a/src/compiler/index.ts b/src/compiler/index.ts
index ff4ffb04..c577be59 100644
--- a/src/compiler/index.ts
+++ b/src/compiler/index.ts
@@ -9,13 +9,17 @@ export type TemplateFunction = (blocks: any, utils: any) => Template;
interface CompileOptions extends Config {
name?: string;
}
-export function compile(template: string, options: CompileOptions = {}): TemplateFunction {
+let nextId = 1;
+export function compile(template: string | Node, options: CompileOptions = {}): TemplateFunction {
// parsing
const ast = parse(template);
// some work
- const hasSafeContext = !template.includes("t-set") && !template.includes("t-call");
- const name = options.name || (template.length > 250 ? template.slice(0, 250) + "..." : template);
+ const hasSafeContext =
+ template instanceof Node
+ ? !(template instanceof Element) || template.querySelector("[t-set], [t-call]") === null
+ : !template.includes("t-set") && !template.includes("t-call");
+ const name = options.name || `template_${nextId++}`;
// code generation
const codeGenerator = new CodeGenerator(name, ast, { ...options, hasSafeContext });
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 0ead2822..64515576 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -173,11 +173,10 @@ interface ParsingContext {
inPreTag: boolean;
}
-export function parse(xml: string): AST {
- const template = `${xml}`;
- const doc = parseXML(template);
+export function parse(xml: string | Node): AST {
+ const node = xml instanceof Element ? xml : parseXML(`${xml}`).firstChild!;
const ctx = { inPreTag: false };
- const ast = parseNode(doc.firstChild!, ctx);
+ const ast = parseNode(node, ctx);
if (!ast) {
return { type: ASTType.Text, value: "" };
}
diff --git a/tests/compiler/__snapshots__/template_set.test.ts.snap b/tests/compiler/__snapshots__/template_set.test.ts.snap
new file mode 100644
index 00000000..937611f6
--- /dev/null
+++ b/tests/compiler/__snapshots__/template_set.test.ts.snap
@@ -0,0 +1,91 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`loading templates can initialize qweb with a string 1`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component } = bdom;
+ let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
+
+ let block1 = createBlock(\`
jupiler
\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ return block1();
+ }
+}"
+`;
+
+exports[`loading templates can initialize qweb with an XMLDocument 1`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component } = bdom;
+ let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
+
+ let block1 = createBlock(\`jupiler
\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ return block1();
+ }
+}"
+`;
+
+exports[`loading templates can load a few templates from a xml string 1`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component } = bdom;
+ let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
+
+ let block2 = createBlock(\`ok\`);
+ let block3 = createBlock(\`foo\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ let b2 = block2();
+ let b3 = block3();
+ return multi([b2, b3]);
+ }
+}"
+`;
+
+exports[`loading templates can load a few templates from a xml string 2`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component } = bdom;
+ let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
+
+ let block1 = createBlock(\`\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ return block1();
+ }
+}"
+`;
+
+exports[`loading templates can load a few templates from an XMLDocument 1`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component } = bdom;
+ let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
+
+ let block2 = createBlock(\`ok\`);
+ let block3 = createBlock(\`foo\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ let b2 = block2();
+ let b3 = block3();
+ return multi([b2, b3]);
+ }
+}"
+`;
+
+exports[`loading templates can load a few templates from an XMLDocument 2`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component } = bdom;
+ let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue, toNumber } = helpers;
+
+ let block1 = createBlock(\`\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ return block1();
+ }
+}"
+`;
diff --git a/tests/compiler/simple_templates.test.ts b/tests/compiler/simple_templates.test.ts
index a431adaf..cb6cfd4e 100644
--- a/tests/compiler/simple_templates.test.ts
+++ b/tests/compiler/simple_templates.test.ts
@@ -138,51 +138,3 @@ describe("simple templates, mostly static", () => {
expect(renderToString(template, { v: "from context" })).toBe("text from context");
});
});
-
-describe("loading templates", () => {
- test.skip("can initialize qweb with a string", () => {
- /* const templates = `
-
- jupiler
- `;
- const qweb = new QWeb({ templates });
- expect(renderToString(qweb, "hey")).toBe("jupiler
");*/
- });
-
- test.skip("can load a few templates from a xml string", () => {
- /*const data = `
-
-
- okfoo
-
-
- `;
- qweb.addTemplates(data);
- const result = renderToString(qweb, "main");
- expect(result).toBe("");*/
- });
-
- test.skip("does not crash if string does not have templates", () => {
- /*const data = "";
- qweb.addTemplates(data);
- expect(Object.keys(qweb.templates)).toEqual([]);*/
- });
-});
-
-describe("global template registration", () => {
- test.skip("can register template globally", () => {
- // expect.assertions(5);
- // let qweb = new QWeb();
- // try {
- // qweb.render("mytemplate");
- // } catch (e) {
- // expect(e.message).toMatch("Template mytemplate does not exist");
- // }
- // expect(qweb.templates.mytemplate).toBeUndefined();
- // QWeb.registerTemplate("mytemplate", "global
");
- // expect(qweb.templates.mytemplate).toBeDefined();
- // const vnode = qweb.render("mytemplate");
- // expect(vnode.sel).toBe("div");
- // expect((vnode as any).children[0].text).toBe("global");
- });
-});
diff --git a/tests/compiler/template_set.test.ts b/tests/compiler/template_set.test.ts
new file mode 100644
index 00000000..46f36d9d
--- /dev/null
+++ b/tests/compiler/template_set.test.ts
@@ -0,0 +1,70 @@
+import { snapshotEverything, TestContext } from "../helpers";
+
+snapshotEverything();
+
+describe("loading templates", () => {
+ test("can initialize qweb with a string", () => {
+ const templates = `
+
+ jupiler
+ `;
+ const context = new TestContext();
+ context.addTemplates(templates);
+ expect(context.renderToString("hey")).toBe("jupiler
");
+ });
+
+ test("can initialize qweb with an XMLDocument", () => {
+ const data = `
+
+ jupiler
+ `;
+ const xml = new DOMParser().parseFromString(data, "text/xml");
+ const context = new TestContext();
+ context.addTemplates(xml);
+ expect(context.renderToString("hey")).toBe("jupiler
");
+ });
+
+ test("can load a few templates from a xml string", () => {
+ const data = `
+
+
+ okfoo
+
+
+ `;
+ const context = new TestContext();
+ context.addTemplates(data);
+ const result = context.renderToString("main");
+ expect(result).toBe("");
+ });
+
+ test("can load a few templates from an XMLDocument", () => {
+ const data = `
+
+
+ okfoo
+
+
+ `;
+ const xml = new DOMParser().parseFromString(data, "text/xml");
+ const context = new TestContext();
+ context.addTemplates(xml);
+ const result = context.renderToString("main");
+ expect(result).toBe("");
+ });
+
+ test("does not crash if string does not have templates", () => {
+ const data = "";
+ const context = new TestContext();
+ context.addTemplates(data);
+ expect(Object.keys(context.rawTemplates)).toEqual([]);
+ });
+
+ test("does not crash if XMLDocument does not have templates", () => {
+ const data = "";
+ const xml = new DOMParser().parseFromString(data, "text/xml");
+ const context = new TestContext();
+ context.addTemplates(xml);
+ expect(Object.keys(context.rawTemplates)).toEqual([]);
+ });
+});
diff --git a/tests/helpers.ts b/tests/helpers.ts
index 862cdcd5..9a3d6fbb 100644
--- a/tests/helpers.ts
+++ b/tests/helpers.ts
@@ -38,7 +38,7 @@ export function makeTestFixture() {
return fixture;
}
-export function snapshotTemplateCode(template: string, options?: CodeGenOptions) {
+export function snapshotTemplateCode(template: string | Node, options?: CodeGenOptions) {
expect(compileTemplate(template, options).toString()).toMatchSnapshot();
}