From 62906b0c97788897e2232a6623ce9689a569e7d2 Mon Sep 17 00:00:00 2001 From: Zach Leatherman Date: Sun, 14 Apr 2024 09:57:37 -0500 Subject: [PATCH 1/3] =?UTF-8?q?Fix=20pkg=20global=20data=20to=20be=20proje?= =?UTF-8?q?ct=20package.json=20and=20not=20eleventy=E2=80=99s.=20Add=20sup?= =?UTF-8?q?port=20for=20`false`=20to=20keys.package=20to=20disable=20packa?= =?UTF-8?q?ge.json=20global=20data.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Data/TemplateData.js | 33 ++++++++++++++++------- src/Data/TemplateDataInitialGlobalData.js | 10 ------- src/defaultConfig.js | 4 +-- test/EleventyTest.js | 31 +++++++++++++++++++++ 4 files changed, 57 insertions(+), 21 deletions(-) diff --git a/src/Data/TemplateData.js b/src/Data/TemplateData.js index d35f3cba2..6ef59c76e 100644 --- a/src/Data/TemplateData.js +++ b/src/Data/TemplateData.js @@ -1,4 +1,5 @@ import path from "node:path"; +import semver from "semver"; import lodash from "@11ty/lodash-custom"; import { TemplatePath, isPlainObject } from "@11ty/eleventy-utils"; @@ -10,15 +11,12 @@ import TemplateGlob from "../TemplateGlob.js"; import EleventyExtensionMap from "../EleventyExtensionMap.js"; import EleventyBaseError from "../Errors/EleventyBaseError.js"; import TemplateDataInitialGlobalData from "./TemplateDataInitialGlobalData.js"; -import { getEleventyPackageJson } from "../Util/ImportJsonSync.js"; -import { - EleventyImport, - EleventyLoadContent, - normalizeFilePathInEleventyPackage, -} from "../Util/Require.js"; +import { getEleventyPackageJson, getWorkingProjectPackageJson } from "../Util/ImportJsonSync.js"; +import { EleventyImport, EleventyLoadContent } from "../Util/Require.js"; import { DeepFreeze } from "../Util/DeepFreeze.js"; const { set: lodashSet, get: lodashGet } = lodash; + const debugWarn = debugUtil("Eleventy:Warnings"); const debug = debugUtil("Eleventy:TemplateData"); const debugDev = debugUtil("Dev:Eleventy:TemplateData"); @@ -108,16 +106,24 @@ class TemplateData { } getRawImports() { + if (!this.config.keys.package) { + debug( + "Opted-out of package.json assignment for global data with falsy value for `keys.package` configuration.", + ); + return this.rawImports; + } else if (Object.keys(this.rawImports).length > 0) { + return this.rawImports; + } + try { - let pkgJson = getEleventyPackageJson(); + let pkgJson = getWorkingProjectPackageJson(); this.rawImports[this.config.keys.package] = pkgJson; if (this.config.freezeReservedData) { DeepFreeze(this.rawImports); } } catch (e) { - let pkgPath = normalizeFilePathInEleventyPackage("package.json"); - debug("Could not find and/or require package.json for data preprocessing at %o", pkgPath); + debug("Could not find or require package.json import for global data."); } return this.rawImports; @@ -311,6 +317,15 @@ class TemplateData { this.configApiGlobalData = new Promise(async (resolve) => { let globalData = await this.initialGlobalData.getData(); + if (!("eleventy" in globalData)) { + globalData.eleventy = {}; + } + + // #2293 for meta[name=generator] + const pkg = getEleventyPackageJson(); + globalData.eleventy.version = semver.coerce(pkg.version).toString(); + globalData.eleventy.generator = `Eleventy v${globalData.eleventy.version}`; + if (this.environmentVariables) { if (!("env" in globalData.eleventy)) { globalData.eleventy.env = {}; diff --git a/src/Data/TemplateDataInitialGlobalData.js b/src/Data/TemplateDataInitialGlobalData.js index fc83a5c21..ffd5029fb 100644 --- a/src/Data/TemplateDataInitialGlobalData.js +++ b/src/Data/TemplateDataInitialGlobalData.js @@ -1,11 +1,8 @@ -import semver from "semver"; import lodash from "@11ty/lodash-custom"; import EleventyBaseError from "../Errors/EleventyBaseError.js"; -import { getEleventyPackageJson } from "../Util/ImportJsonSync.js"; const { set: lodashSet } = lodash; -const pkg = getEleventyPackageJson(); class TemplateDataConfigError extends EleventyBaseError {} @@ -35,13 +32,6 @@ class TemplateDataInitialGlobalData { } } - if (!("eleventy" in globalData)) { - globalData.eleventy = {}; - } - // #2293 for meta[name=generator] - globalData.eleventy.version = semver.coerce(pkg.version).toString(); - globalData.eleventy.generator = `Eleventy v${globalData.eleventy.version}`; - return globalData; } } diff --git a/src/defaultConfig.js b/src/defaultConfig.js index 002134596..05a74050a 100644 --- a/src/defaultConfig.js +++ b/src/defaultConfig.js @@ -31,7 +31,7 @@ import { HtmlTransformer } from "./Util/HtmlTransformer.js"; * @property {string} [htmlOutputSuffix='-o'] * @property {string} [jsDataFileSuffix='.11tydata'] - File suffix for jsData files. * @property {Object} keys - * @property {string} [keys.package='pkg'] + * @property {string} [keys.package='pkg'] - Global data property for package.json data * @property {string} [keys.layout='layout'] * @property {string} [keys.permalink='permalink'] * @property {string} [keys.permalinkRoot='permalinkBypassOutputDir'] @@ -118,7 +118,7 @@ export default function (config) { dataFileDirBaseNameOverride: false, keys: { - package: "pkg", + package: "pkg", // supports `false` layout: "layout", permalink: "permalink", permalinkRoot: "permalinkBypassOutputDir", diff --git a/test/EleventyTest.js b/test/EleventyTest.js index 77fdc40af..d14ca60fa 100644 --- a/test/EleventyTest.js +++ b/test/EleventyTest.js @@ -1066,6 +1066,37 @@ pkg: }); }); +test("Eleventy setting pkg data is okay when keys.package is false", async (t) => { + let elev = new Eleventy("./test/stubs-virtual/", undefined, { + config: eleventyConfig => { + eleventyConfig.addTemplate("index.html", `--- +pkg: + myOwn: OVERRIDE +--- +{{ pkg.myOwn }}`); + } + }); + elev.disableLogger(); + + await elev.initializeConfig({ + keys: { + package: false + } + }); + + // Remap successful + t.is(elev.eleventyConfig.config.keys.package, false); + + let [result] = await elev.toJSON(); + t.deepEqual(result, { + content: "OVERRIDE", + inputPath: "./test/stubs-virtual/index.html", + outputPath: "./_site/index.html", + rawInput: "{{ pkg.myOwn }}", + url: "/" + }); +}); + test("Eleventy setting reserved data throws error (pkg remapped to parkour)", async (t) => { let elev = new Eleventy("./test/stubs-virtual/", undefined, { config: eleventyConfig => { From fed1b389b240e76c90fb59387935b4afd3e3c59b Mon Sep 17 00:00:00 2001 From: Zach Leatherman Date: Tue, 16 Apr 2024 14:06:51 -0500 Subject: [PATCH 2/3] Normalize templateFormats across CLI and config files. --- cmd.cjs | 4 +- src/Data/TemplateData.js | 3 +- src/Eleventy.js | 30 ++-- src/EleventyExtensionMap.js | 13 +- src/EleventyFiles.js | 3 +- src/Engines/TemplateEngine.js | 3 +- src/TemplateConfig.js | 69 ++++---- src/TemplateContent.js | 3 +- src/TemplatePassthroughManager.js | 3 +- src/TemplateRender.js | 3 +- src/TemplateWriter.js | 3 +- src/UserConfig.js | 32 +--- src/Util/ProjectTemplateFormats.js | 130 +++++++++++++++ src/defaultConfig.js | 1 + test/EleventyExtensionMapTest.js | 3 +- test/ProjectTemplateFormatsTest.js | 196 +++++++++++++++++++++++ test/TemplateConfigTest.js | 4 +- test/TemplateFileSlugTest.js | 3 +- test/TemplateLayoutPathResolverTest.js | 3 +- test/TemplateLayoutTest.js | 3 +- test/TemplateRenderCustomTest.js | 9 +- test/TemplateRenderHTMLTest.js | 3 +- test/TemplateRenderJavaScriptTest.js | 3 +- test/TemplateRenderLiquidTest.js | 3 +- test/TemplateRenderMarkdownPluginTest.js | 3 +- test/TemplateRenderMarkdownTest.js | 3 +- test/TemplateRenderNunjucksTest.js | 3 +- test/TemplateRenderTest.js | 3 +- test/TemplateTest.js | 6 +- test/TemplateWriterTest.js | 6 +- test/UserConfigTest.js | 18 +-- test/_getNewTemplateForTests.js | 3 +- 32 files changed, 461 insertions(+), 114 deletions(-) create mode 100644 src/Util/ProjectTemplateFormats.js create mode 100644 test/ProjectTemplateFormatsTest.js diff --git a/cmd.cjs b/cmd.cjs index bcc9ba437..f96ebb8cc 100755 --- a/cmd.cjs +++ b/cmd.cjs @@ -80,6 +80,9 @@ const debug = require("debug")("Eleventy:cmd"); // reuse ErrorHandler instance in Eleventy errorHandler = elev.errorHandler; + // Before init + elev.setFormats(argv.formats); + // careful, we can’t use async/await here to error properly // with old node versions in `please-upgrade-node` above. elev @@ -92,7 +95,6 @@ const debug = require("debug")("Eleventy:cmd"); elev.setIgnoreInitial(argv["ignore-initial"]); elev.setIncrementalBuild(argv.incremental); - elev.setFormats(argv.formats); try { if (argv.serve) { diff --git a/src/Data/TemplateData.js b/src/Data/TemplateData.js index 6ef59c76e..9a1d57900 100644 --- a/src/Data/TemplateData.js +++ b/src/Data/TemplateData.js @@ -83,7 +83,8 @@ class TemplateData { get extensionMap() { if (!this._extensionMap) { - this._extensionMap = new EleventyExtensionMap([], this.eleventyConfig); + this._extensionMap = new EleventyExtensionMap(this.eleventyConfig); + this._extensionMap.setFormats([]); } return this._extensionMap; } diff --git a/src/Eleventy.js b/src/Eleventy.js index 671bf0652..88de12afa 100644 --- a/src/Eleventy.js +++ b/src/Eleventy.js @@ -26,6 +26,7 @@ import RenderPlugin, * as RenderPluginExtras from "./Plugins/RenderPlugin.js"; import I18nPlugin, * as I18nPluginExtras from "./Plugins/I18nPlugin.js"; import HtmlBasePlugin, * as HtmlBasePluginExtras from "./Plugins/HtmlBasePlugin.js"; import { TransformPlugin as InputPathToUrlTransformPlugin } from "./Plugins/InputPathToUrl.js"; +import ProjectTemplateFormats from "./Util/ProjectTemplateFormats.js"; const pkg = getEleventyPackageJson(); const debug = debugUtil("Eleventy"); @@ -42,8 +43,9 @@ const debug = debugUtil("Eleventy"); * @returns {module:11ty/eleventy/Eleventy~Eleventy} */ class Eleventy { - #directories; /* ProjectDirectories instance */ #projectPackageJson; /* userspace package.json file contents */ + #directories; /* ProjectDirectories instance */ + #templateFormats; /* ProjectTemplateFormats instance */ constructor(input, output, options = {}, eleventyConfig = null) { /** @member {String} - Holds the path to the input (might be a file or folder) */ @@ -97,12 +99,6 @@ class Eleventy { */ this.needsInit = true; - /** - * @member {Array} - Subset of template types. - * @default null - */ - this.formatsOverride = null; - /** * @member {Boolean} - Is this an incremental build? (only operates on a subset of input files) * @default false @@ -139,6 +135,7 @@ class Eleventy { this.eleventyConfig.setProjectUsingEsm(this.isEsm); this.eleventyConfig.setLogger(this.logger); this.eleventyConfig.setDirectories(this.directories); + this.eleventyConfig.setTemplateFormats(this.templateFormats); if (this.pathPrefix || this.pathPrefix === "") { this.eleventyConfig.setPathPrefix(this.pathPrefix); @@ -423,8 +420,10 @@ class Eleventy { await this.config.events.emit("eleventy.env", this.env); } - let formats = this.formatsOverride || this.config.templateFormats; - this.extensionMap = new EleventyExtensionMap(formats, this.eleventyConfig); + let formats = this.templateFormats.getTemplateFormats(); + + this.extensionMap = new EleventyExtensionMap(this.eleventyConfig); + this.extensionMap.setFormats(formats); await this.config.events.emit("eleventy.extensionmap", this.extensionMap); // eleventyServe is always available, even when not in --serve mode @@ -617,6 +616,15 @@ Verbose Output: ${this.verboseMode}`; this.eleventyConfig.verbose = isVerbose; } + get templateFormats() { + if (!this.#templateFormats) { + let tf = new ProjectTemplateFormats(); + this.#templateFormats = tf; + } + + return this.#templateFormats; + } + /** * Updates the template formats of Eleventy. * @@ -624,9 +632,7 @@ Verbose Output: ${this.verboseMode}`; * @param {String} formats - The new template formats. */ setFormats(formats) { - if (formats && formats !== "*") { - this.formatsOverride = formats.split(","); - } + this.templateFormats.setViaCommandLine(formats); } /** diff --git a/src/EleventyExtensionMap.js b/src/EleventyExtensionMap.js index 1d744b0df..3f1bbeddf 100644 --- a/src/EleventyExtensionMap.js +++ b/src/EleventyExtensionMap.js @@ -6,17 +6,16 @@ import EleventyBaseError from "./Errors/EleventyBaseError.js"; class EleventyExtensionMapConfigError extends EleventyBaseError {} class EleventyExtensionMap { - constructor(formatKeys, config) { + constructor(config) { this.config = config; - this.formatKeys = formatKeys; - - this.setFormats(formatKeys); - this._spiderJsDepsCache = {}; } setFormats(formatKeys = []) { + // raw + this.formatKeys = formatKeys; + this.unfilteredFormatKeys = formatKeys.map(function (key) { return key.trim().toLowerCase(); }); @@ -251,6 +250,10 @@ class EleventyExtensionMap { return this._extensionToKeyMap; } + getAllTemplateKeys() { + return Object.keys(this.extensionToKeyMap); + } + getReadableFileExtensions() { return Object.keys(this.extensionToKeyMap).join(" "); } diff --git a/src/EleventyFiles.js b/src/EleventyFiles.js index e9bd853cd..fea45bb40 100644 --- a/src/EleventyFiles.js +++ b/src/EleventyFiles.js @@ -130,7 +130,8 @@ class EleventyFiles { get extensionMap() { // for tests if (!this._extensionMap) { - this._extensionMap = new EleventyExtensionMap(this.formats, this.eleventyConfig); + this._extensionMap = new EleventyExtensionMap(this.eleventyConfig); + this._extensionMap.setFormats(this.formats); this._extensionMap.config = this.eleventyConfig; } return this._extensionMap; diff --git a/src/Engines/TemplateEngine.js b/src/Engines/TemplateEngine.js index 15f6999ac..b3c7e33cf 100644 --- a/src/Engines/TemplateEngine.js +++ b/src/Engines/TemplateEngine.js @@ -55,7 +55,8 @@ class TemplateEngine { get extensionMap() { if (!this._extensionMap) { - this._extensionMap = new EleventyExtensionMap([], this.eleventyConfig); + this._extensionMap = new EleventyExtensionMap(this.eleventyConfig); + this._extensionMap.setFormats([]); } return this._extensionMap; } diff --git a/src/TemplateConfig.js b/src/TemplateConfig.js index c16050ff7..03fd77207 100644 --- a/src/TemplateConfig.js +++ b/src/TemplateConfig.js @@ -11,6 +11,7 @@ import ExistsCache from "./Util/ExistsCache.js"; import merge from "./Util/Merge.js"; import unique from "./Util/Unique.js"; import eventBus from "./EventBus.js"; +import ProjectTemplateFormats from "./Util/ProjectTemplateFormats.js"; const debug = debugUtil("Eleventy:TemplateConfig"); const debugDev = debugUtil("Dev:Eleventy:TemplateConfig"); @@ -50,6 +51,8 @@ class EleventyPluginError extends EleventyBaseError {} * @param {String} projectConfigPath - Path to local project config. */ class TemplateConfig { + #templateFormats; + constructor(customRootConfig, projectConfigPath) { this.userConfig = new UserConfig(); @@ -101,6 +104,18 @@ class TemplateConfig { this.userConfig.directories = directories.getUserspaceInstance(); } + /* Setter for TemplateFormats instance */ + setTemplateFormats(templateFormats) { + this.#templateFormats = templateFormats; + } + + get templateFormats() { + if (!this.#templateFormats) { + this.#templateFormats = new ProjectTemplateFormats(); + } + return this.#templateFormats; + } + /* Backwards compat */ get inputDir() { return this.directories.input; @@ -335,8 +350,6 @@ class TemplateConfig { this.directories.setViaConfigObject(exportedConfigObject.dir); } - // TODO (config) sync `exportedConfigObject` to the rest of `userConfig` (e.g. templateFormats) - if (typeof configDefaultReturn === "function") { localConfig = await configDefaultReturn(this.userConfig); } else { @@ -399,20 +412,28 @@ class TemplateConfig { } } - // Template Formats: - // 1. Root Config (usually defaultConfig.js) - // 2. Local Config return object (project .eleventy.js) - // 3. - let templateFormats = this.rootConfig.templateFormats || []; - if (localConfig && localConfig.templateFormats) { - templateFormats = localConfig.templateFormats; - delete localConfig.templateFormats; + // `templateFormats` is an override via `setTemplateFormats` + if (this.userConfig && this.userConfig.templateFormats) { + this.templateFormats.setViaConfig(this.userConfig.templateFormats); + } else if (localConfig?.templateFormats || this.rootConfig?.templateFormats) { + // Local project config or defaultConfig.js + this.templateFormats.setViaConfig( + localConfig.templateFormats || this.rootConfig?.templateFormats, + ); + } + + // `templateFormatsAdded` is additive via `addTemplateFormats` + if (this.userConfig && this.userConfig.templateFormatsAdded) { + this.templateFormats.addViaConfig(this.userConfig.templateFormatsAdded); } let mergedConfig = merge({}, this.rootConfig, localConfig); // Setup a few properties for plugins: + // Set frozen templateFormats + mergedConfig.templateFormats = Object.freeze(this.templateFormats.getTemplateFormats()); + // Setup pathPrefix set via command line for plugin consumption if (this.overrides.pathPrefix) { mergedConfig.pathPrefix = this.overrides.pathPrefix; @@ -433,9 +454,6 @@ class TemplateConfig { // But BEFORE the rest of the config options are merged // this way we can pass directories and other template information to plugins - // Temporarily restore templateFormats - mergedConfig.templateFormats = templateFormats; - await this.userConfig.events.emit("eleventy.beforeConfig", this.userConfig); let benchmarkManager = this.userConfig.benchmarkManager.get("Aggregate"); @@ -444,30 +462,17 @@ class TemplateConfig { await this.processPlugins(mergedConfig); pluginsBench.after(); - delete mergedConfig.templateFormats; - let eleventyConfigApiMergingObject = this.userConfig.getMergingConfigObject(); - // `templateFormats` is an override via `setTemplateFormats` - // `templateFormatsAdded` is additive via `addTemplateFormats` - if (eleventyConfigApiMergingObject && eleventyConfigApiMergingObject.templateFormats) { - templateFormats = eleventyConfigApiMergingObject.templateFormats; - delete eleventyConfigApiMergingObject.templateFormats; + if ("templateFormats" in eleventyConfigApiMergingObject) { + throw new Error( + "Internal error: templateFormats should not return from `getMergingConfigObject`", + ); } - let templateFormatsAdded = eleventyConfigApiMergingObject.templateFormatsAdded || []; - delete eleventyConfigApiMergingObject.templateFormatsAdded; - - templateFormats = unique([...templateFormats, ...templateFormatsAdded]); - - merge(mergedConfig, eleventyConfigApiMergingObject); - - // Apply overrides, currently only pathPrefix uses this I think! + // Overrides are only used by pathPrefix debug("Configuration overrides: %o", this.overrides); - merge(mergedConfig, this.overrides); - - // Restore templateFormats - mergedConfig.templateFormats = templateFormats; + merge(mergedConfig, eleventyConfigApiMergingObject, this.overrides); debug("Current configuration: %o", mergedConfig); diff --git a/src/TemplateContent.js b/src/TemplateContent.js index c935d2777..f8eabd8e4 100644 --- a/src/TemplateContent.js +++ b/src/TemplateContent.js @@ -81,7 +81,8 @@ class TemplateContent { /* Used by tests */ get extensionMap() { if (!this._extensionMap) { - this._extensionMap = new EleventyExtensionMap([], this.eleventyConfig); + this._extensionMap = new EleventyExtensionMap(this.eleventyConfig); + this._extensionMap.setFormats([]); } return this._extensionMap; } diff --git a/src/TemplatePassthroughManager.js b/src/TemplatePassthroughManager.js index dbf29ed8b..664da514b 100644 --- a/src/TemplatePassthroughManager.js +++ b/src/TemplatePassthroughManager.js @@ -37,7 +37,8 @@ class TemplatePassthroughManager { get extensionMap() { if (!this._extensionMap) { - this._extensionMap = new EleventyExtensionMap([], this.eleventyConfig); + this._extensionMap = new EleventyExtensionMap(this.eleventyConfig); + this._extensionMap.setFormats([]); } return this._extensionMap; } diff --git a/src/TemplateRender.js b/src/TemplateRender.js index 9e4c5a3f0..c6567991c 100644 --- a/src/TemplateRender.js +++ b/src/TemplateRender.js @@ -61,7 +61,8 @@ class TemplateRender { get extensionMap() { if (!this._extensionMap) { - this._extensionMap = new EleventyExtensionMap([], this.eleventyConfig); + this._extensionMap = new EleventyExtensionMap(this.eleventyConfig); + this._extensionMap.setFormats([]); } return this._extensionMap; } diff --git a/src/TemplateWriter.js b/src/TemplateWriter.js index 06ea9fdc2..81d5ccfbf 100755 --- a/src/TemplateWriter.js +++ b/src/TemplateWriter.js @@ -106,7 +106,8 @@ class TemplateWriter { get extensionMap() { if (!this._extensionMap) { - this._extensionMap = new EleventyExtensionMap(this.templateFormats, this.eleventyConfig); + this._extensionMap = new EleventyExtensionMap(this.eleventyConfig); + this._extensionMap.setFormats(this.templateFormats); } return this._extensionMap; } diff --git a/src/UserConfig.js b/src/UserConfig.js index fa68e6e9f..dedc9a231 100644 --- a/src/UserConfig.js +++ b/src/UserConfig.js @@ -48,6 +48,7 @@ class UserConfig { this.collections = {}; this.precompiledCollections = {}; this.templateFormats = undefined; + this.templateFormatsAdded = []; this.liquidOptions = {}; this.liquidTags = {}; @@ -489,38 +490,17 @@ class UserConfig { return this; } - _normalizeTemplateFormats(templateFormats, existingValues) { - // setTemplateFormats(null) should return null - if (templateFormats === null || templateFormats === undefined) { - return null; - } - - let set = new Set(); - if (Array.isArray(templateFormats)) { - set = new Set(templateFormats.map((format) => format.trim())); - } else if (typeof templateFormats === "string") { - for (let format of templateFormats.split(",")) { - set.add(format.trim()); - } - } - - for (let format of existingValues || []) { - set.add(format); - } - - return Array.from(set); + _normalizeTemplateFormats() { + throw new Error("The internal _normalizeTemplateFormats() method was removed in Eleventy 3.0"); } setTemplateFormats(templateFormats) { - this.templateFormats = this._normalizeTemplateFormats(templateFormats); + this.templateFormats = templateFormats; } // additive, usually for plugins addTemplateFormats(templateFormats) { - this.templateFormatsAdded = this._normalizeTemplateFormats( - templateFormats, - this.templateFormatsAdded, - ); + this.templateFormatsAdded.push(templateFormats); } setLibrary(engineName, libraryInstance) { @@ -927,8 +907,6 @@ class UserConfig { getMergingConfigObject() { let obj = { - templateFormats: this.templateFormats, - templateFormatsAdded: this.templateFormatsAdded, // filters removed in 1.0 (use addTransform instead) transforms: this.transforms, linters: this.linters, diff --git a/src/Util/ProjectTemplateFormats.js b/src/Util/ProjectTemplateFormats.js new file mode 100644 index 000000000..69243de6f --- /dev/null +++ b/src/Util/ProjectTemplateFormats.js @@ -0,0 +1,130 @@ +import debugUtil from "debug"; +const debug = debugUtil("Eleventy:Util:ProjectTemplateFormats"); + +class ProjectTemplateFormats { + #useAll = {}; + #raw = {}; + + #allFormats = []; + #values = {}; // Set objects + + static union(...sets) { + let s = new Set(); + + for (let set of sets) { + if (!set || typeof set[Symbol.iterator] !== "function") { + continue; + } + for (let v of set) { + s.add(v); + } + } + + return s; + } + + #normalize(formats) { + if (Array.isArray(formats)) { + return formats.map((entry) => entry.trim()); + } + + if (typeof formats !== "string") { + throw new Error( + `Invalid formats (expect String, Array) passed to ProjectTemplateFormats->normalize: ${formats}`, + ); + } + + let final = new Set(); + for (let format of formats.split(",")) { + format = format.trim(); + if (format && format !== "*") { + final.add(format); + } + } + + return final; + } + + #isUseAll(rawFormats) { + if (rawFormats === "") { + return false; + } + + if (typeof rawFormats === "string") { + rawFormats = rawFormats.split(","); + } + + if (Array.isArray(rawFormats)) { + return rawFormats.find((entry) => entry === "*") !== undefined; + } + + return false; + } + + // 3.x Breaking: "" now means no formats. In 2.x and prior it meant "*" + setViaCommandLine(formats) { + if (formats === undefined) { + return; + } + + this.#useAll.cli = this.#isUseAll(formats); + this.#raw.cli = formats; + this.#values.cli = this.#normalize(formats); + } + + // 3.x Breaking: "" now means no formats—in 2.x and prior it meant "*" + // 3.x Adds support for comma separated string—in 2.x this required an Array + setViaConfig(formats) { + if (formats === undefined) { + return; + } + + // "*" is supported + this.#useAll.config = this.#isUseAll(formats); + this.#raw.config = formats; + this.#values.config = this.#normalize(formats); + } + + addViaConfig(formats) { + if (!formats) { + return; + } + + if (this.#isUseAll(formats)) { + throw new Error( + `\`addTemplateFormats("*")\` is not supported for project template syntaxes.`, + ); + } + + // "*" not supported here + this.#raw.configAdd = formats; + this.#values.configAdd = this.#normalize(formats); + } + + getAllTemplateFormats() { + return Array.from(ProjectTemplateFormats.union(this.#values.config, this.#values.configAdd)); + } + + getTemplateFormats() { + if (this.#useAll.cli) { + let v = this.getAllTemplateFormats(); + debug("Using CLI --formats='*': %o", v); + return v; + } + + if (this.#raw.cli !== undefined) { + let v = Array.from(this.#values.cli); + debug("Using CLI --formats: %o", v); + return v; + } + + let v = this.getAllTemplateFormats(); + debug( + "Using configuration `templateFormats`, `setTemplateFormats()`, `addTemplateFormats()`: %o", + v, + ); + return v; + } +} + +export default ProjectTemplateFormats; diff --git a/src/defaultConfig.js b/src/defaultConfig.js index 05a74050a..9234d3a63 100644 --- a/src/defaultConfig.js +++ b/src/defaultConfig.js @@ -118,6 +118,7 @@ export default function (config) { dataFileDirBaseNameOverride: false, keys: { + // TODO breaking: use `false` by default package: "pkg", // supports `false` layout: "layout", permalink: "permalink", diff --git a/test/EleventyExtensionMapTest.js b/test/EleventyExtensionMapTest.js index a040ec66b..6abb8a5a2 100644 --- a/test/EleventyExtensionMapTest.js +++ b/test/EleventyExtensionMapTest.js @@ -6,7 +6,8 @@ async function getExtensionMap(formats, config = new TemplateConfig()) { if (config) { await config.init(); } - let map = new EleventyExtensionMap(formats, config); + let map = new EleventyExtensionMap(config); + map.setFormats(formats); return map; } diff --git a/test/ProjectTemplateFormatsTest.js b/test/ProjectTemplateFormatsTest.js new file mode 100644 index 000000000..c42973c5f --- /dev/null +++ b/test/ProjectTemplateFormatsTest.js @@ -0,0 +1,196 @@ +import test from "ava"; +import ProjectTemplateFormats from "../src/Util/ProjectTemplateFormats.js"; + +function getTestInstance() { + let tf = new ProjectTemplateFormats(); + return tf; +} + +test("Empty formats", t => { + let tf = new ProjectTemplateFormats(); + t.deepEqual(tf.getTemplateFormats(), []); +}); + +test("Return all eligible on no config or CLI", t => { + let tf = getTestInstance(); + + t.deepEqual(tf.getTemplateFormats(), []); +}); + + +// CLI +test("CLI", t => { + let tf = getTestInstance(); + tf.setViaCommandLine("md,html"); + + t.deepEqual(tf.getTemplateFormats(), ["md", "html"]); +}); + +test("CLI empty", t => { + let tf = getTestInstance(); + tf.setViaCommandLine(""); + + t.deepEqual(tf.getTemplateFormats(), []); +}); + +test("CLI *", t => { + let tf = getTestInstance(); + tf.setViaCommandLine("*"); + + t.deepEqual(tf.getTemplateFormats(), []); +}); + + +// Config Set +test("Config", t => { + let tf = getTestInstance(); + tf.setViaConfig("md,html"); + + t.deepEqual(tf.getTemplateFormats(), ["md", "html"]); +}); + +test("Config empty", t => { + let tf = getTestInstance(); + tf.setViaConfig(""); + + t.deepEqual(tf.getTemplateFormats(), []); +}); + +test("Config *", t => { + let tf = getTestInstance(); + tf.setViaConfig("*"); + + t.deepEqual(tf.getTemplateFormats(), []); +}); + + +// Config Add +test("Config Add", t => { + let tf = getTestInstance(); + // add without set unions all with new + tf.addViaConfig("md,html"); + + t.deepEqual(tf.getTemplateFormats(), ["md", "html"]); +}); + +test("Config Add (not yet known)", t => { + let tf = getTestInstance(); + // add without set unions all with new + tf.addViaConfig("vue"); + + t.deepEqual(tf.getTemplateFormats(), ["vue"]); +}); + +test("Config Add empty", t => { + let tf = getTestInstance(); + tf.addViaConfig(""); + + t.deepEqual(tf.getTemplateFormats(), []); +}); + +test("Config Add *", t => { + let tf = getTestInstance(); + t.throws(() => { + tf.addViaConfig("*"); + }, { + message: '`addTemplateFormats("*")` is not supported for project template syntaxes.' + }); +}); + +test("Config Add Multiple", t => { + let tf = getTestInstance(); +// While this does support multiple addTemplateFormat calls from config, they are collected and addViaConfig is only called once. + // add without set unions all with new + tf.addViaConfig("vue"); + tf.addViaConfig("pug"); + tf.addViaConfig("zbbbbb"); + + t.deepEqual(tf.getTemplateFormats(), ["zbbbbb"]); +}); + + +// CLI and Config (CLI wins every time) +test("CLI + Config", t => { + let tf = getTestInstance(); + tf.setViaCommandLine("md,html"); + tf.setViaConfig("liquid"); + tf.addViaConfig("vue"); + + t.deepEqual(tf.getTemplateFormats(), ["md", "html"]); +}); + +test("CLI + Config empty", t => { + let tf = getTestInstance(); + tf.setViaCommandLine(""); + tf.setViaConfig("liquid"); + tf.addViaConfig("vue"); + + t.deepEqual(tf.getTemplateFormats(), []); +}); + +test("CLI + Config *", t => { + let tf = getTestInstance(); + tf.setViaCommandLine("*"); + tf.setViaConfig("liquid"); + tf.addViaConfig("vue"); + + t.deepEqual(tf.getTemplateFormats(), ["liquid", "vue"]); +}); + + +// Config set and add +test("Config set/add", t => { + let tf = getTestInstance(); + tf.setViaConfig("liquid"); + tf.addViaConfig("vue"); + + t.deepEqual(tf.getTemplateFormats(), ["liquid", "vue"]); +}); + +test("Config set/add set undefined", t => { + let tf = getTestInstance(); + tf.setViaConfig(undefined); + tf.addViaConfig("vue"); + + t.deepEqual(tf.getTemplateFormats(), ["vue"]); +}); + +test("Config set/add add undefined", t => { + let tf = getTestInstance(); + tf.setViaConfig("vue"); + tf.addViaConfig(undefined); + + t.deepEqual(tf.getTemplateFormats(), ["vue"]); +}); + +test("Config set/add set empty", t => { + let tf = getTestInstance(); + tf.setViaConfig(""); + tf.addViaConfig("vue"); + + t.deepEqual(tf.getTemplateFormats(), ["vue"]); +}); + +test("Config set/add add empty", t => { + let tf = getTestInstance(); + tf.setViaConfig("vue"); + tf.addViaConfig(""); + + t.deepEqual(tf.getTemplateFormats(), ["vue"]); +}); + +test("Config set/add both empty", t => { + let tf = getTestInstance(); + tf.setViaConfig(""); + tf.addViaConfig(""); + + t.deepEqual(tf.getTemplateFormats(), []); +}); + +test("Config set *, add", t => { + let tf = getTestInstance(); + tf.setViaConfig("*"); + tf.addViaConfig("vue"); + + t.deepEqual(tf.getTemplateFormats(), ["vue"]); +}); diff --git a/test/TemplateConfigTest.js b/test/TemplateConfigTest.js index aa9ea0766..96ca27047 100644 --- a/test/TemplateConfigTest.js +++ b/test/TemplateConfigTest.js @@ -282,7 +282,7 @@ test("setTemplateFormats(null)", async (t) => { await templateCfg.init(); let cfg = templateCfg.getConfig(); - t.deepEqual(cfg.templateFormats.sort(), ["md", "njk"]); + t.deepEqual([...cfg.templateFormats].sort(), ["md", "njk"]); }); test("setTemplateFormats(undefined)", async (t) => { @@ -292,7 +292,7 @@ test("setTemplateFormats(undefined)", async (t) => { await templateCfg.init(); let cfg = templateCfg.getConfig(); - t.deepEqual(cfg.templateFormats.sort(), ["md", "njk"]); + t.deepEqual([...cfg.templateFormats].sort(), ["md", "njk"]); }); test("multiple setTemplateFormats calls", async (t) => { diff --git a/test/TemplateFileSlugTest.js b/test/TemplateFileSlugTest.js index 154c08ea2..1ce9e8650 100644 --- a/test/TemplateFileSlugTest.js +++ b/test/TemplateFileSlugTest.js @@ -12,7 +12,8 @@ async function getNewSlugInstance(path, inputDir) { } }); - let extensionMap = new EleventyExtensionMap([], eleventyConfig); + let extensionMap = new EleventyExtensionMap(eleventyConfig); + extensionMap.setFormats([]); let fs = new TemplateFileSlug(path, extensionMap, eleventyConfig); return fs; } diff --git a/test/TemplateLayoutPathResolverTest.js b/test/TemplateLayoutPathResolverTest.js index 2df432877..8b5c18f49 100644 --- a/test/TemplateLayoutPathResolverTest.js +++ b/test/TemplateLayoutPathResolverTest.js @@ -15,7 +15,8 @@ async function getResolverInstance(path, inputDir, { eleventyConfig, map } = {}) } if (!map) { - map = new EleventyExtensionMap(["liquid", "md", "njk", "html", "11ty.js"], eleventyConfig); + map = new EleventyExtensionMap(eleventyConfig); + map.setFormats(["liquid", "md", "njk", "html", "11ty.js"]); } return new TemplateLayoutPathResolver(path, map, eleventyConfig); diff --git a/test/TemplateLayoutTest.js b/test/TemplateLayoutTest.js index d798a276c..7af3b3879 100644 --- a/test/TemplateLayoutTest.js +++ b/test/TemplateLayoutTest.js @@ -14,7 +14,8 @@ async function getTemplateLayoutInstance(key, inputDir, map) { }); if (!map) { - map = new EleventyExtensionMap(["liquid", "md", "njk", "html", "11ty.js"], eleventyConfig); + map = new EleventyExtensionMap(eleventyConfig); + map.setFormats(["liquid", "md", "njk", "html", "11ty.js"]); } let layout = new TemplateLayout(key, map, eleventyConfig); return layout; diff --git a/test/TemplateRenderCustomTest.js b/test/TemplateRenderCustomTest.js index 92e7b0021..d418c2505 100644 --- a/test/TemplateRenderCustomTest.js +++ b/test/TemplateRenderCustomTest.js @@ -17,7 +17,8 @@ async function getNewTemplateRender(name, inputDir, eleventyConfig, extensionMap } if (!extensionMap) { - extensionMap = new EleventyExtensionMap([], eleventyConfig); + extensionMap = new EleventyExtensionMap(eleventyConfig); + extensionMap.setFormats([]); } let tr = new TemplateRender(name, eleventyConfig); @@ -318,7 +319,8 @@ test.skip("Breaking Change (3.0): Two simple aliases to JavaScript Render", asyn } ); - let map = new EleventyExtensionMap([], eleventyConfig); // reuse this + let map = new EleventyExtensionMap(eleventyConfig); // reuse this + map.setFormats([]); let tr = await getNewTemplateRender("./test/stubs/string.11ty.custom", null, eleventyConfig, map); let fn = await tr.getCompiledTemplate(); @@ -347,7 +349,8 @@ test("Double override (not aliases) throws an error", async (t) => { } ); - let map = new EleventyExtensionMap([], eleventyConfig); // reuse this + let map = new EleventyExtensionMap(eleventyConfig); // reuse this + map.setFormats([]); await t.throwsAsync( async () => { diff --git a/test/TemplateRenderHTMLTest.js b/test/TemplateRenderHTMLTest.js index b3ddd3a80..8079344a5 100644 --- a/test/TemplateRenderHTMLTest.js +++ b/test/TemplateRenderHTMLTest.js @@ -12,7 +12,8 @@ async function getNewTemplateRender(name, inputDir) { }); let tr = new TemplateRender(name, eleventyConfig); - tr.extensionMap = new EleventyExtensionMap([], eleventyConfig); + tr.extensionMap = new EleventyExtensionMap(eleventyConfig); + tr.extensionMap.setFormats([]); await tr.init(); return tr; } diff --git a/test/TemplateRenderJavaScriptTest.js b/test/TemplateRenderJavaScriptTest.js index 7737f8031..8dcfdef18 100644 --- a/test/TemplateRenderJavaScriptTest.js +++ b/test/TemplateRenderJavaScriptTest.js @@ -13,7 +13,8 @@ async function getNewTemplateRender(name, inputDir, extendedConfig) { }, null, extendedConfig); let tr = new TemplateRender(name, eleventyConfig); - tr.extensionMap = new EleventyExtensionMap([], eleventyConfig); + tr.extensionMap = new EleventyExtensionMap(eleventyConfig); + tr.extensionMap.setFormats([]); await tr.init(); return tr; diff --git a/test/TemplateRenderLiquidTest.js b/test/TemplateRenderLiquidTest.js index 94a802f39..7e847e1ef 100644 --- a/test/TemplateRenderLiquidTest.js +++ b/test/TemplateRenderLiquidTest.js @@ -14,7 +14,8 @@ async function getNewTemplateRender(name, inputDir, userConfig = {}) { }, null, userConfig); let tr = new TemplateRender(name, eleventyConfig); - tr.extensionMap = new EleventyExtensionMap([], eleventyConfig); + tr.extensionMap = new EleventyExtensionMap(eleventyConfig); + tr.extensionMap.setFormats([]); await tr.init(); return tr; } diff --git a/test/TemplateRenderMarkdownPluginTest.js b/test/TemplateRenderMarkdownPluginTest.js index 356b86664..57bd88a03 100644 --- a/test/TemplateRenderMarkdownPluginTest.js +++ b/test/TemplateRenderMarkdownPluginTest.js @@ -10,7 +10,8 @@ async function getNewTemplateRender(name, inputDir) { let eleventyConfig = await getTemplateConfigInstance(); let tr = new TemplateRender(name, eleventyConfig); - tr.extensionMap = new EleventyExtensionMap([], eleventyConfig); + tr.extensionMap = new EleventyExtensionMap(eleventyConfig); + tr.extensionMap.setFormats([]); await tr.init(); return tr; } diff --git a/test/TemplateRenderMarkdownTest.js b/test/TemplateRenderMarkdownTest.js index c1bb7ce92..ff25e933e 100644 --- a/test/TemplateRenderMarkdownTest.js +++ b/test/TemplateRenderMarkdownTest.js @@ -21,7 +21,8 @@ async function getNewTemplateRender(name, inputDir, eleventyConfig) { } let tr = new TemplateRender(name, eleventyConfig); - tr.extensionMap = new EleventyExtensionMap([], eleventyConfig); + tr.extensionMap = new EleventyExtensionMap(eleventyConfig); + tr.extensionMap.setFormats([]); await tr.init(); return tr; } diff --git a/test/TemplateRenderNunjucksTest.js b/test/TemplateRenderNunjucksTest.js index 8e6623378..7fcfc4308 100644 --- a/test/TemplateRenderNunjucksTest.js +++ b/test/TemplateRenderNunjucksTest.js @@ -17,7 +17,8 @@ async function getNewTemplateRender(name, inputDir, eleventyConfig) { } let tr = new TemplateRender(name, eleventyConfig); - tr.extensionMap = new EleventyExtensionMap([], eleventyConfig); + tr.extensionMap = new EleventyExtensionMap(eleventyConfig); + tr.extensionMap.setFormats([]); await tr.init(); return tr; } diff --git a/test/TemplateRenderTest.js b/test/TemplateRenderTest.js index d2de6564b..203dbaf5f 100644 --- a/test/TemplateRenderTest.js +++ b/test/TemplateRenderTest.js @@ -13,7 +13,8 @@ async function getNewTemplateRender(name, inputDir) { }); let tr = new TemplateRender(name, eleventyConfig); - tr.extensionMap = new EleventyExtensionMap([], eleventyConfig); + tr.extensionMap = new EleventyExtensionMap(eleventyConfig); + tr.extensionMap.setFormats([]); await tr.init(); return tr; } diff --git a/test/TemplateTest.js b/test/TemplateTest.js index 3d077d261..f632bf650 100644 --- a/test/TemplateTest.js +++ b/test/TemplateTest.js @@ -1655,7 +1655,8 @@ test("Engine Singletons", async (t) => { } }); - let map = new EleventyExtensionMap(["njk"], eleventyConfig); + let map = new EleventyExtensionMap(eleventyConfig); + map.setFormats(["njk"]); let tmpl1 = await getNewTemplate( "./test/stubs/engine-singletons/first.njk", "./test/stubs/engine-singletons/", @@ -1738,7 +1739,8 @@ test("Add Extension via Configuration (txt file)", async (t) => { }); }); - let map = new EleventyExtensionMap([], eleventyConfig); + let map = new EleventyExtensionMap(eleventyConfig); + map.setFormats([]); let tmpl = await getNewTemplate( "./test/stubs/default.txt", "./test/stubs/", diff --git a/test/TemplateWriterTest.js b/test/TemplateWriterTest.js index c80ab6e40..fd9ea63aa 100644 --- a/test/TemplateWriterTest.js +++ b/test/TemplateWriterTest.js @@ -629,7 +629,8 @@ test.skip("Markdown with alias", async (t) => { } }); - let map = new EleventyExtensionMap(["md"], eleventyConfig); + let map = new EleventyExtensionMap(eleventyConfig); + map.setFormats(["md"]); map.config = { templateExtensionAliases: { markdown: "md", @@ -675,7 +676,8 @@ test.skip("JavaScript with alias", async (t) => { } }); - let map = new EleventyExtensionMap(["11ty.js"], eleventyConfig); + let map = new EleventyExtensionMap(eleventyConfig); + map.setFormats(["11ty.js"]); map.config = { templateExtensionAliases: { js: "11ty.js", diff --git a/test/UserConfigTest.js b/test/UserConfigTest.js index 2a870b02d..971f3df57 100644 --- a/test/UserConfigTest.js +++ b/test/UserConfigTest.js @@ -4,29 +4,29 @@ import UserConfig from "../src/UserConfig.js"; test("Template Formats", (t) => { let userCfg = new UserConfig(); - t.falsy(userCfg.getMergingConfigObject().templateFormats); + t.falsy(userCfg.templateFormats); userCfg.setTemplateFormats("njk,liquid"); - t.deepEqual(userCfg.getMergingConfigObject().templateFormats, ["njk", "liquid"]); + t.deepEqual(userCfg.templateFormats, "njk,liquid"); // setting multiple times takes the last one userCfg.setTemplateFormats("njk,liquid,pug"); userCfg.setTemplateFormats("njk,liquid"); - t.deepEqual(userCfg.getMergingConfigObject().templateFormats, ["njk", "liquid"]); + t.deepEqual(userCfg.templateFormats, "njk,liquid"); }); test("Template Formats (Arrays)", (t) => { let userCfg = new UserConfig(); - t.falsy(userCfg.getMergingConfigObject().templateFormats); + t.falsy(userCfg.templateFormats); userCfg.setTemplateFormats(["njk", "liquid"]); - t.deepEqual(userCfg.getMergingConfigObject().templateFormats, ["njk", "liquid"]); + t.deepEqual(userCfg.templateFormats, ["njk", "liquid"]); // setting multiple times takes the last one userCfg.setTemplateFormats(["njk", "liquid", "pug"]); userCfg.setTemplateFormats(["njk", "liquid"]); - t.deepEqual(userCfg.getMergingConfigObject().templateFormats, ["njk", "liquid"]); + t.deepEqual(userCfg.templateFormats, ["njk", "liquid"]); }); // more in TemplateConfigTest.js @@ -144,7 +144,7 @@ test("Set manual Pass-through File Copy (glob patterns)", (t) => { test("Set Template Formats (string)", (t) => { let userCfg = new UserConfig(); userCfg.setTemplateFormats("njk, liquid"); - t.deepEqual(userCfg.templateFormats, ["njk", "liquid"]); + t.deepEqual(userCfg.templateFormats, "njk, liquid"); }); test("Set Template Formats (array)", (t) => { @@ -156,13 +156,13 @@ test("Set Template Formats (array)", (t) => { test("Set Template Formats (js passthrough copy)", (t) => { let userCfg = new UserConfig(); userCfg.setTemplateFormats("njk, liquid, js"); - t.deepEqual(userCfg.templateFormats, ["njk", "liquid", "js"]); + t.deepEqual(userCfg.templateFormats, "njk, liquid, js"); }); test("Set Template Formats (11ty.js)", (t) => { let userCfg = new UserConfig(); userCfg.setTemplateFormats("njk, liquid, 11ty.js"); - t.deepEqual(userCfg.templateFormats, ["njk", "liquid", "11ty.js"]); + t.deepEqual(userCfg.templateFormats, "njk, liquid, 11ty.js"); }); test("Add Template Formats", (t) => { diff --git a/test/_getNewTemplateForTests.js b/test/_getNewTemplateForTests.js index c7abaecca..e9820354e 100644 --- a/test/_getNewTemplateForTests.js +++ b/test/_getNewTemplateForTests.js @@ -22,7 +22,8 @@ export default async function getNewTemplate( } if (!map) { - map = new EleventyExtensionMap(["liquid", "md", "njk", "html", "11ty.js"], eleventyConfig); + map = new EleventyExtensionMap(eleventyConfig); + map.setFormats(["liquid", "md", "njk", "html", "11ty.js"]) } if (templateData) { templateData.setFileSystemSearch(new FileSystemSearch()); From db71034429d4b7235c63ff2563b8acc96e60eda9 Mon Sep 17 00:00:00 2001 From: Zach Leatherman Date: Tue, 16 Apr 2024 14:15:32 -0500 Subject: [PATCH 3/3] Remove extra import --- src/TemplateConfig.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/TemplateConfig.js b/src/TemplateConfig.js index 03fd77207..98e130dfc 100644 --- a/src/TemplateConfig.js +++ b/src/TemplateConfig.js @@ -9,7 +9,6 @@ import UserConfig from "./UserConfig.js"; import GlobalDependencyMap from "./GlobalDependencyMap.js"; import ExistsCache from "./Util/ExistsCache.js"; import merge from "./Util/Merge.js"; -import unique from "./Util/Unique.js"; import eventBus from "./EventBus.js"; import ProjectTemplateFormats from "./Util/ProjectTemplateFormats.js";