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(\`