From ba5365e9d9b61e0d42dbfa12c016ac6b11fdf440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Wed, 9 Mar 2022 09:47:42 +0100 Subject: [PATCH] [IMP] app: add a setting to share compiled templates between apps --- doc/reference/app.md | 3 +++ src/runtime/template_set.ts | 45 ++++++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/doc/reference/app.md b/doc/reference/app.md index 31a4d059..e6716fd9 100644 --- a/doc/reference/app.md +++ b/doc/reference/app.md @@ -55,6 +55,9 @@ The `config` object is an object with some of the following keys: [`dev` mode](#dev-mode); - **`test (boolean, default=false)`**: `test` mode is the same as `dev` mode, except that Owl will not log a message to warn that Owl is in `dev` mode. +- **`shareTemplates (boolean, default=false)`**: if `true`, each compiled template + will be shared between instances of `App`. Useful for speeding test suites, because + it prevent recompiling the same templates again and again. - **`translatableAttributes (string[])`**: a list of additional attributes that should be translated (see [translations](translations.md)) - **`translateFn (function)`**: a function that will be called by owl to translate diff --git a/src/runtime/template_set.ts b/src/runtime/template_set.ts index 6868e612..2608f4e8 100644 --- a/src/runtime/template_set.ts +++ b/src/runtime/template_set.ts @@ -37,11 +37,17 @@ function parseXML(xml: string): Document { return doc; } +const sharedTemplates: Map< + Function | undefined, + { [key: string]: { [name: string]: TemplateFunction } } +> = new Map(); + export interface TemplateSetConfig { dev?: boolean; translatableAttributes?: string[]; translateFn?: (s: string) => string; templates?: string | Document; + shareTemplates?: boolean; } export class TemplateSet { @@ -51,12 +57,27 @@ export class TemplateSet { dev: boolean; rawTemplates: typeof globalTemplates = Object.create(globalTemplates); templates: { [name: string]: Template } = {}; + templateFunctions: { [name: string]: TemplateFunction } = {}; translateFn?: (s: string) => string; translatableAttributes?: string[]; Portal = Portal; constructor(config: TemplateSetConfig = {}) { this.dev = config.dev || false; + if (config.shareTemplates) { + let cache = sharedTemplates.get(this.translateFn); + if (!cache) { + cache = {}; + sharedTemplates.set(this.translateFn, cache); + } + let key = `${this.dev ? "d" : "p"}${(this.translatableAttributes || []).toString()}`; + let templates = cache[key]; + if (!templates) { + cache[key] = {}; + templates = cache[key]; + } + this.templateFunctions = templates; + } this.translateFn = config.translateFn; this.translatableAttributes = config.translatableAttributes; if (config.templates) { @@ -96,17 +117,21 @@ export class TemplateSet { getTemplate(name: string): Template { if (!(name in this.templates)) { - const rawTemplate = this.rawTemplates[name]; - if (rawTemplate === undefined) { - let extraInfo = ""; - try { - const componentName = getCurrent().component.constructor.name; - extraInfo = ` (for component "${componentName}")`; - } catch {} - throw new OwlError(`Missing template: "${name}"${extraInfo}`); + let templateFn = this.templateFunctions[name]; + if (!templateFn) { + const rawTemplate = this.rawTemplates[name]; + if (rawTemplate === undefined) { + let extraInfo = ""; + try { + const componentName = getCurrent().component.constructor.name; + extraInfo = ` (for component "${componentName}")`; + } catch {} + throw new OwlError(`Missing template: "${name}"${extraInfo}`); + } + const isFn = typeof rawTemplate === "function" && !(rawTemplate instanceof Element); + templateFn = isFn ? rawTemplate : this._compileTemplate(name, rawTemplate); + this.templateFunctions[name] = templateFn; } - const isFn = typeof rawTemplate === "function" && !(rawTemplate instanceof Element); - const templateFn = isFn ? rawTemplate : this._compileTemplate(name, rawTemplate); // first add a function to lazily get the template, in case there is a // recursive call to the template name const templates = this.templates;