From 28d32d541bdd7832c40526d2af32e5633572c5ab Mon Sep 17 00:00:00 2001 From: Ger Hobbelt Date: Wed, 8 Apr 2020 23:58:40 +0200 Subject: [PATCH] provide JavaScript Layout Templates' `data` class member with some useful info: the inputPath (template), the invoking template instance and the current engine instance. Relevant documentation which drove this change: https://www.11ty.dev/docs/languages/javascript/#permalink-function -- without the change, the `permalink` member in a `_data/layouts/layout.js` layout template will fail when it's made a function like the documentation says it can. Extra are the extra parameters passed into such a class' `data` member function: template and engine. For that, a few other classes needed to have their APIs changed as well. The meat for making the `permalink` member work as a member function type is in src/Template.js, while the meat for the `data` member function is in src/Engines/JavaScript.js (the `inst.data(...)` call in there). --- src/Engines/JavaScript.js | 4 ++-- src/Engines/TemplateEngine.js | 2 +- src/Template.js | 29 ++++++++++++++++++++++++++++- src/TemplateContent.js | 4 ++-- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/Engines/JavaScript.js b/src/Engines/JavaScript.js index b4fa4a8f2..83828a691 100644 --- a/src/Engines/JavaScript.js +++ b/src/Engines/JavaScript.js @@ -89,13 +89,13 @@ class JavaScript extends TemplateEngine { } } - async getExtraDataFromFile(inputPath) { + async getExtraDataFromFile(inputPath, template) { let inst = this.getInstanceFromInputPath(inputPath); if (inst && "data" in inst) { // get extra data from `data` method, // either as a function or getter or object literal let result = await (typeof inst.data === "function" - ? inst.data() + ? inst.data(inputPath, this, template) : inst.data); if (typeof result !== "object") { throw new JavaScriptTemplateInvalidDataFormatError( diff --git a/src/Engines/TemplateEngine.js b/src/Engines/TemplateEngine.js index 222d33f20..b46d5c52c 100644 --- a/src/Engines/TemplateEngine.js +++ b/src/Engines/TemplateEngine.js @@ -119,7 +119,7 @@ class TemplateEngine { return true; } - getExtraDataFromFile(inputPath) { + getExtraDataFromFile(inputPath, template) { return {}; } diff --git a/src/Template.js b/src/Template.js index d722e516a..21625962d 100644 --- a/src/Template.js +++ b/src/Template.js @@ -116,13 +116,15 @@ class Template extends TemplateContent { let permalink = data[this.config.keys.permalink]; if (permalink) { + permalink = await this.mkString(permalink, data, this); + debugDev(`_getLink()::mkString() -> permalink = ${permalink}, extraOutputSubdirectory = ${this.extraOutputSubdirectory}`); // render variables inside permalink front matter, bypass markdown let permalinkValue; if (!this.config.dynamicPermalinks || data.dynamicPermalink === false) { debugDev("Not using dynamicPermalinks, using %o", permalink); permalinkValue = permalink; } else { - permalinkValue = await super.render(permalink, data, true); + permalinkValue = await super.render(permalink, data, /* bypassMarkdown */ true); debug( "Rendering permalink for %o: %s becomes %o", this.inputPath, @@ -154,6 +156,7 @@ class Template extends TemplateContent { // TODO instead of htmlIOException, do a global search to check if output path = input path and then add extra suffix async getOutputLink(data) { let link = await this._getLink(data); + debugDev(`getOutputLink() -> ${link.toString()}`) return link.toString(); } @@ -733,6 +736,30 @@ class Template extends TemplateContent { } return contents; } + + // ripped from Engines/JavaScript/_getInstance() and tweaked: + // + // String, Buffer, Promise + // Function, Class + // Object + async mkString(mod, data) { + if (typeof mod === "string" || mod instanceof Buffer) { + return mod.toString(); + } + else if (mod.then) { + debugDev(`mkString with promise: ${mod}`); + return mod.toString(); + } else if (typeof mod === "function") { + //debugDev(`mkString with function: ${mod.toString()}`); + if (mod.prototype && "render" in mod.prototype) { + return await (new mod()).render(data, this.config, this); + } else { + return await mod(data, this.config, this); + } + } else if ("render" in mod) { + return await mod.render(data, this.config, this); + } + } } module.exports = Template; diff --git a/src/TemplateContent.js b/src/TemplateContent.js index 0c82a159a..ad2bc709e 100644 --- a/src/TemplateContent.js +++ b/src/TemplateContent.js @@ -135,7 +135,7 @@ class TemplateContent { await this.read(); } - let extraData = await this.engine.getExtraDataFromFile(this.inputPath); + let extraData = await this.engine.getExtraDataFromFile(this.inputPath, this); let data = TemplateData.mergeDeep({}, this.frontMatter.data, extraData); return TemplateData.cleanupData(data); } @@ -174,7 +174,7 @@ class TemplateContent { debugDev("%o getCompiledTemplate function created", this.inputPath); return fn; } catch (e) { - debug(`Having trouble compiling template ${this.inputPath}: %O`, str); + debug(`Having trouble compiling template ${this.inputPath}: %O --> %O`, str, e); throw new TemplateContentCompileError( `Having trouble compiling template ${this.inputPath}`, e