Skip to content

Commit

Permalink
Fixes #2248
Browse files Browse the repository at this point in the history
  • Loading branch information
zachleat committed Dec 8, 2022
1 parent f4cc4bc commit 5352697
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 32 deletions.
2 changes: 0 additions & 2 deletions src/Engines/Custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ class CustomEngine extends TemplateEngine {
// Here, we are short circuiting fallback to defaultRenderer, does not account for compile
// functions that call defaultRenderer explicitly
if (
!this.entry.compile &&
this._defaultEngine &&
"needsToReadFileContents" in this._defaultEngine
) {
Expand Down Expand Up @@ -183,7 +182,6 @@ class CustomEngine extends TemplateEngine {

// Fall back to default compiler if the user does not provide their own
if (!this.entry.compile && defaultRenderer) {
// If the defaultRenderer is used here, needsToReadFileContents is also aliased to the upstream engine too in needsToReadFileContents(), #2279
return defaultRenderer;
}

Expand Down
6 changes: 5 additions & 1 deletion src/TemplateConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,11 @@ class TemplateConfig {
debug("rootConfig %o", this.rootConfig);
}

/* Add additional overrides to the root config object, used for testing */
/*
* Add additional overrides to the root config object, used for testing
*
* @param {Object} - a subset of the return Object from the user’s config file.
*/
appendToRootConfig(obj) {
Object.assign(this.rootConfig, obj);
}
Expand Down
30 changes: 28 additions & 2 deletions src/TemplateEngineManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@ class TemplateEngineManager {
this.engineCache = {};
}

static isCustomEngineSimpleAlias(entry) {
let keys = Object.keys(entry);
if (keys.length > 2) {
return false;
}
return !keys.some((key) => {
return key !== "key" && key !== "extension";
});
}

get keyToClassNameMap() {
if (!this._keyToClassNameMap) {
this._keyToClassNameMap = {
Expand All @@ -26,9 +36,23 @@ class TemplateEngineManager {
"11ty.js": "JavaScript",
};

// Custom entries *can* overwrite default entries above
if ("extensionMap" in this.config) {
for (let entry of this.config.extensionMap) {
this._keyToClassNameMap[entry.key] = "Custom";
// either the key does not already exist or it is not a simple alias and is an override: https://www.11ty.dev/docs/languages/custom/#overriding-an-existing-template-language
if (
!this._keyToClassNameMap[entry.key] ||
!TemplateEngineManager.isCustomEngineSimpleAlias(entry)
) {
// throw an error if you try to override a Custom engine, this is a short term error until we swap this to use the extension instead of the key to get the class
if (this._keyToClassNameMap[entry.key] === "Custom") {
throw new Error(
`An attempt was made to override the *already* overridden "${entry.key}" template syntax via the \`addExtension\` configuration API. A maximum of one override is currently supported. If you’re trying to add an alias to an existing syntax, make sure only the \`key\` property is present in the addExtension options object.`
);
}

this._keyToClassNameMap[entry.key] = "Custom";
}
}
}
}
Expand Down Expand Up @@ -83,6 +107,8 @@ class TemplateEngineManager {
);
}

// 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]) {
return this.engineCache[name];
}
Expand All @@ -93,7 +119,7 @@ class TemplateEngineManager {
instance.extensionMap = extensionMap;
instance.engineManager = this;

// If the user provides a "Custom" engine using addExtension,
// If provided a "Custom" engine using addExtension,
// But that engine's instance is *not* custom,
// The user must be overriding an existing engine
// i.e. addExtension('md', { ...overrideBehavior })
Expand Down
135 changes: 108 additions & 27 deletions test/TemplateRenderCustomTest.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const test = require("ava");
const TemplateData = require("../src/TemplateData");
const TemplateRender = require("../src/TemplateRender");
const EleventyExtensionMap = require("../src/EleventyExtensionMap");
const TemplateConfig = require("../src/TemplateConfig");
Expand All @@ -8,21 +7,22 @@ const getNewTemplate = require("./_getNewTemplateForTests");
const { createSSRApp } = require("vue");
const { renderToString } = require("@vue/server-renderer");

function getNewTemplateRender(name, inputDir, eleventyConfig) {
function getNewTemplateRender(name, inputDir, eleventyConfig, extensionMap) {
if (!eleventyConfig) {
eleventyConfig = new TemplateConfig();
}
if (!extensionMap) {
extensionMap = new EleventyExtensionMap([], eleventyConfig);
}
let tr = new TemplateRender(name, inputDir, eleventyConfig);
tr.extensionMap = new EleventyExtensionMap([], eleventyConfig);
tr.extensionMap = extensionMap;
return tr;
}

test("Custom plaintext Render", async (t) => {
let eleventyConfig = new TemplateConfig();
// addExtension() API
eleventyConfig.userConfig.extensionMap.add({
extension: "txt",
key: "txt",
eleventyConfig.userConfig.addExtension("txt", {
compile: function (str, inputPath) {
// plaintext
return function (data) {
Expand All @@ -42,9 +42,7 @@ test("Custom Markdown Render with `compile` override", async (t) => {
let eleventyConfig = new TemplateConfig();

// addExtension() API
eleventyConfig.userConfig.extensionMap.add({
extension: "md",
key: "md",
eleventyConfig.userConfig.addExtension("md", {
compile: function (str, inputPath) {
return function (data) {
return `<not-markdown>${str.trim()}</not-markdown>`;
Expand All @@ -64,9 +62,7 @@ test("Custom Markdown Render without `compile` override", async (t) => {
let initCalled = false;

// addExtension() API
eleventyConfig.userConfig.extensionMap.add({
extension: "md",
key: "md",
eleventyConfig.userConfig.addExtension("md", {
init: function () {
initCalled = true;
},
Expand All @@ -84,9 +80,7 @@ test("Custom Markdown Render with `compile` override + call to default compiler"
let eleventyConfig = new TemplateConfig();

// addExtension() API
eleventyConfig.userConfig.extensionMap.add({
extension: "md",
key: "md",
eleventyConfig.userConfig.addExtension("md", {
compile: function (str, inputPath) {
return async function (data) {
const result = await this.defaultRenderer(data);
Expand All @@ -109,9 +103,7 @@ test("Custom Vue Render", async (t) => {
let tr = getNewTemplateRender("vue");

// addExtension() API
tr.eleventyConfig.userConfig.extensionMap.add({
extension: "vue",
key: "vue",
tr.eleventyConfig.userConfig.addExtension("vue", {
compile: function (str) {
return async function (data) {
const app = createSSRApp({
Expand All @@ -136,9 +128,7 @@ test("Custom Sass Render", async (t) => {
let tr = getNewTemplateRender("sass");

// addExtension() API
tr.eleventyConfig.userConfig.extensionMap.add({
extension: "sass",
key: "sass",
tr.eleventyConfig.userConfig.addExtension("sass", {
compile: function (str, inputPath) {
// TODO declare data variables as SASS variables?
return async function (data) {
Expand Down Expand Up @@ -194,9 +184,7 @@ test("JavaScript functions should not be mutable but not *that* mutable", async
};

// addExtension() API
eleventyConfig.userConfig.extensionMap.add({
extension: "js1",
key: "js1",
eleventyConfig.userConfig.addExtension("js1", {
getData: ["dataForCascade"],
getInstanceFromInputPath: function (inputPath) {
t.truthy(true);
Expand Down Expand Up @@ -228,9 +216,7 @@ test("Return undefined in compile to ignore #2267", async (t) => {
let eleventyConfig = new TemplateConfig();

// addExtension() API
eleventyConfig.userConfig.extensionMap.add({
extension: "txt",
key: "txt",
eleventyConfig.userConfig.addExtension("txt", {
compile: function (str, inputPath) {
return;
},
Expand All @@ -241,3 +227,98 @@ test("Return undefined in compile to ignore #2267", async (t) => {
let fn = await tr.getCompiledTemplate("<p>Paragraph</p>");
t.is(fn, undefined);
});

test("Simple alias to Markdown Render", async (t) => {
let eleventyConfig = new TemplateConfig();
eleventyConfig.userConfig.addExtension("mdx", {
key: "md",
});

let tr = getNewTemplateRender("mdx", null, eleventyConfig);

let fn = await tr.getCompiledTemplate("# Header");
t.is((await fn()).trim(), "<h1>Header</h1>");
t.is((await fn({})).trim(), "<h1>Header</h1>");
});

test("Simple alias to JavaScript Render", async (t) => {
let eleventyConfig = new TemplateConfig();
eleventyConfig.userConfig.addExtension("11ty.custom", {
key: "11ty.js",
});

let fn = await getNewTemplateRender(
"./test/stubs/string.11ty.custom",
null,
eleventyConfig
).getCompiledTemplate();
t.is(await fn({ name: "Bill" }), "<p>Zach</p>");
});

test("Override to JavaScript Render", async (t) => {
let eleventyConfig = new TemplateConfig();
eleventyConfig.userConfig.addExtension("11ty.custom", {
key: "11ty.js",
init: function () {},
});

let fn = await getNewTemplateRender(
"./test/stubs/string.11ty.custom",
null,
eleventyConfig
).getCompiledTemplate();
t.is(await fn({ name: "Bill" }), "<p>Zach</p>");
});

test("Two simple aliases to JavaScript Render", async (t) => {
t.plan(2);
let eleventyConfig = new TemplateConfig();
let map = new EleventyExtensionMap([], eleventyConfig); // reuse this

eleventyConfig.userConfig.addExtension(["11ty.custom", "11ty.possum"], {
key: "11ty.js",
});

let fn = await getNewTemplateRender(
"./test/stubs/string.11ty.custom",
null,
eleventyConfig,
map
).getCompiledTemplate();
t.is(await fn({}), "<p>Zach</p>");

let fn2 = await getNewTemplateRender(
"./test/stubs/string.11ty.possum",
null,
eleventyConfig,
map
).getCompiledTemplate();
t.is(await fn2({}), "<p>Possum</p>");
});

test("Double override (not aliases) throws an error", async (t) => {
let eleventyConfig = new TemplateConfig();
let map = new EleventyExtensionMap([], eleventyConfig); // reuse this

eleventyConfig.userConfig.addExtension(["11ty.custom", "11ty.possum"], {
key: "11ty.js",
init: function () {
t.true(true);
},
});

await t.throwsAsync(
async () => {
await getNewTemplateRender(
"./test/stubs/string.11ty.custom",
null,
eleventyConfig,
map
).getCompiledTemplate();
},
{
message:
'An attempt was made to override the *already* overridden "11ty.js" template syntax via the `addExtension` configuration API. A maximum of one override is currently supported. If you’re trying to add an alias to an existing syntax, make sure only the `key` property is present in the addExtension options object.',
}
);
});
1 change: 1 addition & 0 deletions test/stubs/string.11ty.custom
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = "<p>Zach</p>";
1 change: 1 addition & 0 deletions test/stubs/string.11ty.possum
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = "<p>Possum</p>";

0 comments on commit 5352697

Please sign in to comment.