diff options
Diffstat (limited to 'node_modules/@11ty/eleventy/src/Engines/TemplateEngineManager.js')
| -rw-r--r-- | node_modules/@11ty/eleventy/src/Engines/TemplateEngineManager.js | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/node_modules/@11ty/eleventy/src/Engines/TemplateEngineManager.js b/node_modules/@11ty/eleventy/src/Engines/TemplateEngineManager.js new file mode 100644 index 0000000..913a803 --- /dev/null +++ b/node_modules/@11ty/eleventy/src/Engines/TemplateEngineManager.js @@ -0,0 +1,193 @@ +import debugUtil from "debug"; +import EleventyBaseError from "../Errors/EleventyBaseError.js"; + +const debug = debugUtil("Eleventy:TemplateEngineManager"); + +class TemplateEngineManager { + constructor(eleventyConfig) { + if (!eleventyConfig || eleventyConfig.constructor.name !== "TemplateConfig") { + throw new EleventyBaseError("Missing or invalid `config` argument."); + } + this.eleventyConfig = eleventyConfig; + + this.engineCache = {}; + this.importCache = {}; + } + + get config() { + return this.eleventyConfig.getConfig(); + } + + static isAlias(entry) { + if (entry.aliasKey) { + return true; + } + + return entry.key !== entry.extension; + } + + static isSimpleAlias(entry) { + if (!this.isAlias(entry)) { + return false; + } + + // has keys other than key, extension, and aliasKey + return ( + Object.keys(entry).some((key) => { + return key !== "key" && key !== "extension" && key !== "aliasKey"; + }) === false + ); + } + + get keyToClassNameMap() { + if (!this._keyToClassNameMap) { + this._keyToClassNameMap = { + md: "Markdown", + html: "Html", + njk: "Nunjucks", + liquid: "Liquid", + "11ty.js": "JavaScript", + }; + + // Custom entries *can* overwrite default entries above + if ("extensionMap" in this.config) { + for (let entry of this.config.extensionMap) { + // either the key does not already exist or it is not a simple alias and is an override: https://v3.11ty.dev/docs/languages/custom/#overriding-an-existing-template-language + let existingTarget = this._keyToClassNameMap[entry.key]; + let isAlias = TemplateEngineManager.isAlias(entry); + + if (!existingTarget && isAlias) { + throw new Error( + `An attempt to alias ${entry.aliasKey} to ${entry.key} was made, but ${entry.key} is not a recognized template syntax.`, + ); + } + + if (isAlias) { + // only `key` and `extension`, not `compile` or other options + if (!TemplateEngineManager.isSimpleAlias(entry)) { + this._keyToClassNameMap[entry.aliasKey] = "Custom"; + } else { + this._keyToClassNameMap[entry.aliasKey] = this._keyToClassNameMap[entry.key]; + } + } else { + // not an alias, so `key` and `extension` are the same here. + // *can* override a built-in extension! + this._keyToClassNameMap[entry.key] = "Custom"; + } + } + } + } + + return this._keyToClassNameMap; + } + + reset() { + this.engineCache = {}; + } + + getClassNameFromTemplateKey(key) { + return this.keyToClassNameMap[key]; + } + + hasEngine(name) { + return !!this.getClassNameFromTemplateKey(name); + } + + async getEngineClassByExtension(extension) { + if (this.importCache[extension]) { + return this.importCache[extension]; + } + + let promise; + + // We include these as raw strings (and not more readable variables) so they’re parsed by a bundler. + if (extension === "md") { + promise = import("./Markdown.js").then((mod) => mod.default); + } else if (extension === "html") { + promise = import("./Html.js").then((mod) => mod.default); + } else if (extension === "njk") { + promise = import("./Nunjucks.js").then((mod) => mod.default); + } else if (extension === "liquid") { + promise = import("./Liquid.js").then((mod) => mod.default); + } else if (extension === "11ty.js") { + promise = import("./JavaScript.js").then((mod) => mod.default); + } else { + promise = this.getCustomEngineClass(); + } + + this.importCache[extension] = promise; + + return promise; + } + + async getCustomEngineClass() { + if (!this._CustomEngine) { + this._CustomEngine = import("./Custom.js").then((mod) => mod.default); + } + return this._CustomEngine; + } + + async #getEngine(name, extensionMap) { + let cls = await this.getEngineClassByExtension(name); + let instance = new cls(name, this.eleventyConfig); + instance.extensionMap = extensionMap; + instance.engineManager = this; + + let extensionEntry = extensionMap.getExtensionEntry(name); + + // Override a built-in extension (md => md) + // If provided a "Custom" engine using addExtension, but that engine's instance is *not* custom, + // The user must be overriding a built-in engine i.e. addExtension('md', { ...overrideBehavior }) + let className = this.getClassNameFromTemplateKey(name); + + if (className === "Custom" && instance.constructor.name !== "CustomEngine") { + let CustomEngine = await this.getCustomEngineClass(); + let overrideCustomEngine = new CustomEngine(name, this.eleventyConfig); + + // Keep track of the "default" engine 11ty would normally use + // This allows the user to access the default engine in their override + overrideCustomEngine.setDefaultEngine(instance); + + instance = overrideCustomEngine; + // Alias to a built-in extension (11ty.tsx => 11ty.js) + } else if ( + instance.constructor.name === "CustomEngine" && + TemplateEngineManager.isAlias(extensionEntry) + ) { + // add defaultRenderer for complex aliases with their own compile functions. + let originalEngineInstance = await this.getEngine(extensionEntry.key, extensionMap); + instance.setDefaultEngine(originalEngineInstance); + } + + return instance; + } + + isEngineRemovedFromCore(name) { + return ["ejs", "hbs", "mustache", "haml", "pug"].includes(name) && !this.hasEngine(name); + } + + async getEngine(name, extensionMap) { + // Bundled engine deprecation + if (this.isEngineRemovedFromCore(name)) { + throw new Error( + `Per the 11ty Community Survey (2023), the "${name}" template language was moved from core to an officially supported plugin in v3.0. These plugins live here: https://github.com/11ty/eleventy-plugin-template-languages and are documented on their respective template language docs at https://v3.11ty.dev/docs/languages/ You are also empowered to implement *any* template language yourself using https://v3.11ty.dev/docs/languages/custom/`, + ); + } + + if (!this.hasEngine(name)) { + throw new Error(`Template Engine ${name} does not exist in getEngine()`); + } + // TODO these cached engines should be based on extensions not name, then we can remove the error in + // "Double override (not aliases) throws an error" test in TemplateRenderCustomTest.js + if (!this.engineCache[name]) { + debug("Engine cache miss %o (should only happen once per engine type)", name); + // Make sure cache key is based on name and not path + // Custom class is used for all plugins, cache once per plugin + this.engineCache[name] = this.#getEngine(name, extensionMap); + } + + return this.engineCache[name]; + } +} + +export default TemplateEngineManager; |
