import debugUtil from "debug"; import EleventyBaseError from "../Errors/EleventyBaseError.js"; class TemplateEngineConfigError extends EleventyBaseError {} const debug = debugUtil("Eleventy:TemplateEngine"); const AMENDED_INSTANCES = new Set(); export default class TemplateEngine { #extensionMap; #engineManager; #benchmarks; constructor(name, eleventyConfig) { this.name = name; this.engineLib = null; if (!eleventyConfig) { throw new TemplateEngineConfigError("Missing `eleventyConfig` argument."); } this.eleventyConfig = eleventyConfig; } get cacheable() { return false; } get dirs() { return this.eleventyConfig.directories; } get inputDir() { return this.dirs.input; } get includesDir() { return this.dirs.includes; } get config() { if (this.eleventyConfig.constructor.name !== "TemplateConfig") { throw new Error("Expecting a TemplateConfig instance."); } return this.eleventyConfig.getConfig(); } get benchmarks() { if (!this.#benchmarks) { this.#benchmarks = { aggregate: this.config.benchmarkManager.get("Aggregate"), }; } return this.#benchmarks; } get engineManager() { return this.#engineManager; } set engineManager(manager) { this.#engineManager = manager; } get extensionMap() { if (!this.#extensionMap) { throw new Error("Internal error: missing `extensionMap` in TemplateEngine."); } return this.#extensionMap; } set extensionMap(map) { this.#extensionMap = map; } get extensions() { if (!this._extensions) { this._extensions = this.extensionMap.getExtensionsFromKey(this.name); } return this._extensions; } get extensionEntries() { if (!this._extensionEntries) { this._extensionEntries = this.extensionMap.getExtensionEntriesFromKey(this.name); } return this._extensionEntries; } getName() { return this.name; } // Backwards compat getIncludesDir() { return this.includesDir; } /** * @protected */ setEngineLib(engineLib, isOverrideViaSetLibrary = false) { this.engineLib = engineLib; // Run engine amendments (via issue #2438) // Issue #3816: this isn’t ideal but there is no other way to reset a markdown instance if it was also overridden by addLibrary if (AMENDED_INSTANCES.has(engineLib)) { return; } if (isOverrideViaSetLibrary) { AMENDED_INSTANCES.add(engineLib); } debug( "Running amendLibrary for %o (number of amendments: %o)", this.name, this.config.libraryAmendments[this.name]?.length, ); for (let amendment of this.config.libraryAmendments[this.name] || []) { // TODO it’d be nice if this were async friendly amendment(engineLib); } } getEngineLib() { return this.engineLib; } async _testRender(str, data) { // @ts-ignore let fn = await this.compile(str); return fn(data); } useJavaScriptImport() { return false; } // JavaScript files defer to the module loader rather than read the files to strings needsToReadFileContents() { return true; } getExtraDataFromFile() { return {}; } getCompileCacheKey(str, inputPath) { // Changing to use inputPath and contents, using only file contents (`str`) caused issues when two // different files had identical content (2.0.0-canary.16) // Caches are now segmented based on inputPath so using inputPath here is superfluous (2.0.0-canary.19) // But we do want a non-falsy value here even if `str` is an empty string. return { useCache: true, key: inputPath + str, }; } get defaultTemplateFileExtension() { return "html"; } // Whether or not to wrap in Eleventy layouts useLayouts() { return true; } /** @returns {boolean|undefined} */ permalinkNeedsCompilation(str) { return this.needsCompilation(); } // whether or not compile is needed or can we return the plaintext? needsCompilation(str) { return true; } /** * Make sure compile is implemented downstream. * @abstract * @return {Promise} */ async compile() { throw new Error("compile() must be implemented by engine"); } // See https://v3.11ty.dev/docs/watch-serve/#watch-javascript-dependencies static shouldSpiderJavaScriptDependencies() { return false; } hasDependencies(inputPath) { if (this.config.uses.getDependencies(inputPath) === false) { return false; } return true; } isFileRelevantTo(inputPath, comparisonFile) { return this.config.uses.isFileRelevantTo(inputPath, comparisonFile); } }