diff --git a/src/app/app.ts b/src/app/app.ts index f671d9c9..36773e98 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -15,6 +15,7 @@ export interface AppConfig { env?: Env; translatableAttributes?: string[]; translateFn?: (s: string) => string; + templates?: string | Document; } export const DEV_MSG = `Owl is running in 'dev' mode. @@ -49,6 +50,9 @@ export class App extends TemplateSet { if (config.translatableAttributes) { this.translatableAttributes = config.translatableAttributes; } + if (config.templates) { + this.addTemplates(config.templates); + } return this; } diff --git a/src/app/template_set.ts b/src/app/template_set.ts index 7978174e..029484a4 100644 --- a/src/app/template_set.ts +++ b/src/app/template_set.ts @@ -7,6 +7,36 @@ const bdom = { text, createBlock, list, multi, html, toggler, component }; export const globalTemplates: { [key: string]: string | Node } = {}; +function parseXML(xml: string): Document { + const parser = new DOMParser(); + + const doc = parser.parseFromString(xml, "text/xml"); + if (doc.getElementsByTagName("parsererror").length) { + let msg = "Invalid XML in template."; + const parsererrorText = doc.getElementsByTagName("parsererror")[0].textContent; + if (parsererrorText) { + msg += "\nThe parser has produced the following error message:\n" + parsererrorText; + const re = /\d+/g; + const firstMatch = re.exec(parsererrorText); + if (firstMatch) { + const lineNumber = Number(firstMatch[0]); + const line = xml.split("\n")[lineNumber - 1]; + const secondMatch = re.exec(parsererrorText); + if (line && secondMatch) { + const columnIndex = Number(secondMatch[0]) - 1; + if (line[columnIndex]) { + msg += + `\nThe error might be located at xml line ${lineNumber} column ${columnIndex}\n` + + `${line}\n${"-".repeat(columnIndex - 1)}^`; + } + } + } + } + throw new Error(msg); + } + return doc; +} + export class TemplateSet { rawTemplates: typeof globalTemplates = Object.create(globalTemplates); templates: { [name: string]: Template } = {}; @@ -33,7 +63,11 @@ export class TemplateSet { } addTemplates(xml: string | Document, options: { allowDuplicate?: boolean } = {}) { - xml = xml instanceof Document ? xml : new DOMParser().parseFromString(xml, "text/xml"); + if (!xml) { + // empty string + return; + } + xml = xml instanceof Document ? xml : parseXML(xml); for (const template of xml.querySelectorAll("[t-name]")) { const name = template.getAttribute("t-name")!; template.removeAttribute("t-name"); diff --git a/tests/compiler/error_handling.test.ts b/tests/compiler/error_handling.test.ts index fdf98816..4371b08c 100644 --- a/tests/compiler/error_handling.test.ts +++ b/tests/compiler/error_handling.test.ts @@ -21,12 +21,12 @@ describe("error handling", () => { expect(() => context.addTemplate("test", "
")).toThrow("already defined"); }); - // test("addTemplates throw if parser error", () => { - // const context = new TestContext(); - // expect(() => { - // context.addTemplates(">"); - // }).toThrow("Invalid XML in template"); - // }); + test("addTemplates throw if parser error", () => { + const context = new TestContext(); + expect(() => { + context.addTemplates(">"); + }).toThrow("Invalid XML in template"); + }); test("nice error when t-on is evaluated with a missing event", () => { expect(() => renderToString(`
`)).toThrow(