207 lines
5.3 KiB
JavaScript
Raw Normal View History

2019-11-26 14:50:43 -08:00
const TemplatePath = require("./TemplatePath");
const TemplateEngine = require("./Engines/TemplateEngine");
const EleventyBaseError = require("./EleventyBaseError");
const EleventyExtensionMap = require("./EleventyExtensionMap");
const config = require("./Config");
// const debug = require("debug")("Eleventy:TemplateRender");
class TemplateRenderUnknownEngineError extends EleventyBaseError {}
// works with full path names or short engine name
class TemplateRender {
constructor(tmplPath, inputDir, extensionMap) {
if (!tmplPath) {
throw new Error(
`TemplateRender requires a tmplPath argument, instead of ${tmplPath}`
);
}
this.path = tmplPath;
this.extensionMap = extensionMap;
// optional
this.includesDir = this._normalizeIncludesDir(inputDir);
this.parseMarkdownWith = this.config.markdownTemplateEngine;
this.parseHtmlWith = this.config.htmlTemplateEngine;
this.init(tmplPath);
this.useMarkdown = this.engineName === "md";
}
get config() {
if (!this._config) {
this._config = config.getConfig();
}
return this._config;
}
set config(config) {
this._config = config;
if (this.engine) {
this.engine.config = config;
}
}
init(engineNameOrPath) {
this.engineName = this.cleanupEngineName(engineNameOrPath);
if (!this.engineName) {
throw new TemplateRenderUnknownEngineError(
`Unknown engine for ${engineNameOrPath}`
);
}
this.engine = TemplateEngine.getEngine(this.engineName, this.includesDir);
this.engine.initRequireCache(this.path);
}
cleanupEngineName(tmplPath) {
return TemplateRender._cleanupEngineName(
tmplPath,
this.extensionMap || EleventyExtensionMap
);
}
static cleanupEngineName(tmplPath) {
return TemplateRender._cleanupEngineName(tmplPath, EleventyExtensionMap);
}
static _cleanupEngineName(tmplPath, extensionMapRef) {
return extensionMapRef.getKey(tmplPath);
}
static hasEngine(tmplPath) {
let name = TemplateRender.cleanupEngineName(tmplPath);
return TemplateEngine.hasEngine(name);
}
static parseEngineOverrides(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) {
// todo use unshift or something (no wifi here to look up docs :D)
engines = ["md"].concat(engines);
}
return engines;
}
// used for error logging.
getEnginesStr() {
if (this.engineName === "md" && this.useMarkdown) {
return this.parseMarkdownWith + " (and markdown)";
}
return this.engineName;
}
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) {
this.init("html");
return;
}
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;
}
getIncludesDir() {
return this.includesDir;
}
_normalizeIncludesDir(dir) {
return TemplatePath.join(
dir ? dir : this.config.dir.input,
this.config.dir.includes
);
}
isEngine(engine) {
return this.engineName === engine;
}
setUseMarkdown(useMarkdown) {
this.useMarkdown = !!useMarkdown;
}
setMarkdownEngine(markdownEngine) {
this.parseMarkdownWith = markdownEngine;
}
setHtmlEngine(htmlEngineName) {
this.parseHtmlWith = htmlEngineName;
}
async render(str, data) {
return this.engine.render(str, data);
}
async getCompiledTemplate(str) {
// TODO refactor better, move into TemplateEngine logic
if (this.engineName === "md") {
return this.engine.compile(
str,
this.path,
this.parseMarkdownWith,
!this.useMarkdown
);
} else if (this.engineName === "html") {
return this.engine.compile(str, this.path, this.parseHtmlWith);
} else {
return this.engine.compile(str, this.path);
}
}
}
module.exports = TemplateRender;