homepage/node_modules/@11ty/eleventy/src/TemplateRender.js
2024-11-03 17:16:20 +01:00

295 lines
7.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import EleventyBaseError from "./Errors/EleventyBaseError.js";
import EleventyExtensionMap from "./EleventyExtensionMap.js";
import TemplateEngineManager from "./Engines/TemplateEngineManager.js";
import CustomEngine from "./Engines/Custom.js";
// import debugUtil from "debug";
// const debug = debugUtil("Eleventy:TemplateRender");
class TemplateRenderConfigError extends EleventyBaseError {}
class TemplateRenderUnknownEngineError extends EleventyBaseError {}
// works with full path names or short engine name
class TemplateRender {
constructor(tmplPath, config) {
if (!tmplPath) {
throw new Error(`TemplateRender requires a tmplPath argument, instead of ${tmplPath}`);
}
if (!config) {
throw new TemplateRenderConfigError("Missing `config` argument.");
}
if (config.constructor.name === "TemplateConfig") {
this.eleventyConfig = config;
this.config = config.getConfig();
} else {
throw new Error("Third argument to TemplateRender must be a TemplateConfig instance.");
}
this.engineNameOrPath = tmplPath;
this.parseMarkdownWith = this.config.markdownTemplateEngine;
this.parseHtmlWith = this.config.htmlTemplateEngine;
}
get dirs() {
return this.eleventyConfig.directories;
}
get inputDir() {
return this.dirs.input;
}
get includesDir() {
return this.dirs.includes;
}
/* Backwards compat */
getIncludesDir() {
return this.includesDir;
}
get config() {
return this._config;
}
set config(config) {
this._config = config;
}
set extensionMap(extensionMap) {
this._extensionMap = extensionMap;
}
get extensionMap() {
if (!this._extensionMap) {
this._extensionMap = new EleventyExtensionMap(this.eleventyConfig);
this._extensionMap.setFormats([]);
}
return this._extensionMap;
}
async getEngineByName(name) {
let engine = await this.extensionMap.engineManager.getEngine(name, this.extensionMap);
engine.eleventyConfig = this.eleventyConfig;
return engine;
}
// Runs once per template
async init(engineNameOrPath) {
let name = engineNameOrPath || this.engineNameOrPath;
this.extensionMap.config = this.eleventyConfig;
let extensionEntry = this.extensionMap.getExtensionEntry(name);
let engineName = extensionEntry?.aliasKey || extensionEntry?.key;
if (TemplateEngineManager.isSimpleAlias(extensionEntry)) {
engineName = extensionEntry?.key;
}
this._engineName = engineName;
if (!extensionEntry || !this._engineName) {
throw new TemplateRenderUnknownEngineError(
`Unknown engine for ${name} (supported extensions: ${this.extensionMap.getReadableFileExtensions()})`,
);
}
this._engine = await this.getEngineByName(this._engineName);
if (this.useMarkdown === undefined) {
this.setUseMarkdown(this._engineName === "md");
}
}
get engineName() {
if (!this._engineName) {
throw new Error("TemplateRender needs a call to the init() method.");
}
return this._engineName;
}
get engine() {
if (!this._engine) {
throw new Error("TemplateRender needs a call to the init() method.");
}
return this._engine;
}
static parseEngineOverrides(engineName) {
if (typeof (engineName || "") !== "string") {
throw new Error("Expected String passed to parseEngineOverrides. Received: " + engineName);
}
let overlappingEngineWarningCount = 0;
let engines = [];
let uniqueLookup = {};
let usingMarkdown = false;
(engineName || "")
.split(",")
.map((name) => {
return name.toLowerCase().trim();
})
.forEach((name) => {
// html is assumed (treated as plaintext by the system)
if (!name || name === "html") {
return;
}
if (name === "md") {
usingMarkdown = true;
return;
}
if (!uniqueLookup[name]) {
engines.push(name);
uniqueLookup[name] = true;
// we already short circuit md and html types above
overlappingEngineWarningCount++;
}
});
if (overlappingEngineWarningCount > 1) {
throw new Error(
`Dont mix multiple templating engines in your front matter overrides (exceptions for HTML and Markdown). You used: ${engineName}`,
);
}
// markdown should always be first
if (usingMarkdown) {
engines.unshift("md");
}
return engines;
}
// used for error logging and console output.
getReadableEnginesList() {
return this.getReadableEnginesListDifferingFromFileExtension() || this.engineName;
}
getReadableEnginesListDifferingFromFileExtension() {
let keyFromFilename = this.extensionMap.getKey(this.engineNameOrPath);
if (this.engine instanceof CustomEngine) {
if (
this.engine.entry &&
this.engine.entry.name &&
keyFromFilename !== this.engine.entry.name
) {
return this.engine.entry.name;
} else {
// We dont have a name for it so we return nothing so we dont misreport (per #2386)
return;
}
}
if (this.engineName === "md" && this.useMarkdown && this.parseMarkdownWith) {
return this.parseMarkdownWith;
}
if (this.engineName === "html" && this.parseHtmlWith) {
return this.parseHtmlWith;
}
// templateEngineOverride in play and template language differs from file extension
if (keyFromFilename !== this.engineName) {
return this.engineName;
}
}
// TODO templateEngineOverride
getPreprocessorEngine() {
if (this.engineName === "md" && this.parseMarkdownWith) {
return this.parseMarkdownWith;
}
if (this.engineName === "html" && this.parseHtmlWith) {
return this.parseHtmlWith;
}
return this.extensionMap.getKey(this.engineNameOrPath);
}
// We pass in templateEngineOverride here because it isnt yet applied to templateRender
getEnginesList(engineOverride) {
if (engineOverride) {
let engines = TemplateRender.parseEngineOverrides(engineOverride).reverse();
return engines.join(",");
}
if (this.engineName === "md" && this.useMarkdown && this.parseMarkdownWith) {
return `${this.parseMarkdownWith},md`;
}
if (this.engineName === "html" && this.parseHtmlWith) {
return this.parseHtmlWith;
}
// templateEngineOverride in play
return this.extensionMap.getKey(this.engineNameOrPath);
}
async setEngineOverride(engineName, bypassMarkdown) {
let engines = TemplateRender.parseEngineOverrides(engineName);
// when overriding, Template Engines with HTML will instead use the Template Engine as primary and output HTML
// So any HTML engine usage here will never use a preprocessor templating engine.
this.setHtmlEngine(false);
if (!engines.length) {
await this.init("html");
return;
}
await this.init(engines[0]);
let usingMarkdown = engines[0] === "md" && !bypassMarkdown;
this.setUseMarkdown(usingMarkdown);
if (usingMarkdown) {
// false means only parse markdown and not with a preprocessor template engine
this.setMarkdownEngine(engines.length > 1 ? engines[1] : false);
}
}
getEngineName() {
return this.engineName;
}
isEngine(engine) {
return this.engineName === engine;
}
setUseMarkdown(useMarkdown) {
this.useMarkdown = !!useMarkdown;
}
// this is only called for templateEngineOverride
setMarkdownEngine(markdownEngine) {
this.parseMarkdownWith = markdownEngine;
}
// this is only called for templateEngineOverride
setHtmlEngine(htmlEngineName) {
this.parseHtmlWith = htmlEngineName;
}
async _testRender(str, data) {
return this.engine._testRender(str, data);
}
async getCompiledTemplate(str) {
// TODO refactor better, move into TemplateEngine logic
if (this.engineName === "md") {
return this.engine.compile(
str,
this.engineNameOrPath,
this.parseMarkdownWith,
!this.useMarkdown,
);
} else if (this.engineName === "html") {
return this.engine.compile(str, this.engineNameOrPath, this.parseHtmlWith);
} else {
return this.engine.compile(str, this.engineNameOrPath);
}
}
}
export default TemplateRender;