diff --git a/src/Plugins/HtmlBasePlugin.js b/src/Plugins/HtmlBasePlugin.js index 820a85280..3760d8a48 100644 --- a/src/Plugins/HtmlBasePlugin.js +++ b/src/Plugins/HtmlBasePlugin.js @@ -138,5 +138,11 @@ Object.defineProperty(eleventyHtmlBasePlugin, "eleventyPackage", { value: "@11ty/eleventy/html-base-plugin", }); +Object.defineProperty(eleventyHtmlBasePlugin, "eleventyPluginOptions", { + value: { + unique: true, + }, +}); + export default eleventyHtmlBasePlugin; export { transformUrl as applyBaseToUrl }; diff --git a/src/Plugins/I18nPlugin.js b/src/Plugins/I18nPlugin.js index 277c21c7a..6f53825fb 100644 --- a/src/Plugins/I18nPlugin.js +++ b/src/Plugins/I18nPlugin.js @@ -154,7 +154,7 @@ function getLocaleUrlsMap(urlToInputPath, extensionMap, options = {}) { return urlMap; } -function EleventyPlugin(eleventyConfig, opts = {}) { +function eleventyI18nPlugin(eleventyConfig, opts = {}) { let options = DeepCopy( { defaultLanguage: "", @@ -304,4 +304,14 @@ function EleventyPlugin(eleventyConfig, opts = {}) { export { Comparator, LangUtils }; -export default EleventyPlugin; +Object.defineProperty(eleventyI18nPlugin, "eleventyPackage", { + value: "@11ty/eleventy/i18n-plugin", +}); + +Object.defineProperty(eleventyI18nPlugin, "eleventyPluginOptions", { + value: { + unique: true, + }, +}); + +export default eleventyI18nPlugin; diff --git a/src/Plugins/InputPathToUrl.js b/src/Plugins/InputPathToUrl.js index a6ba0c173..4dcbba1a5 100644 --- a/src/Plugins/InputPathToUrl.js +++ b/src/Plugins/InputPathToUrl.js @@ -75,4 +75,16 @@ function TransformPlugin(eleventyConfig, defaultOptions = {}) { }); } +Object.defineProperty(TransformPlugin, "eleventyPackage", { + value: "@11ty/eleventy/inputpath-to-url-plugin", +}); + +Object.defineProperty(TransformPlugin, "eleventyPluginOptions", { + value: { + unique: true, + }, +}); + +export default TransformPlugin; + export { FilterPlugin, TransformPlugin }; diff --git a/src/Plugins/RenderPlugin.js b/src/Plugins/RenderPlugin.js index 4c82c033c..4dd90f06a 100644 --- a/src/Plugins/RenderPlugin.js +++ b/src/Plugins/RenderPlugin.js @@ -132,7 +132,7 @@ async function renderShortcodeFn(fn, data) { * @param {module:11ty/eleventy/UserConfig} eleventyConfig - User-land configuration instance. * @param {Object} options - Plugin options */ -function EleventyPlugin(eleventyConfig, options = {}) { +function eleventyRenderPlugin(eleventyConfig, options = {}) { /** * @typedef {Object} options * @property {string} [tagName] - The shortcode name to render a template string. @@ -380,7 +380,7 @@ class RenderManager { this.templateConfig.setDirectories(new ProjectDirectories()); // This is the only plugin running on the Edge - this.templateConfig.userConfig.addPlugin(EleventyPlugin, { + this.templateConfig.userConfig.addPlugin(eleventyRenderPlugin, { templateConfig: this.templateConfig, accessGlobalData: true, }); @@ -447,6 +447,16 @@ class RenderManager { } } -export default EleventyPlugin; +Object.defineProperty(eleventyRenderPlugin, "eleventyPackage", { + value: "@11ty/eleventy/render-plugin", +}); + +Object.defineProperty(eleventyRenderPlugin, "eleventyPluginOptions", { + value: { + unique: true, + }, +}); + +export default eleventyRenderPlugin; export { compileFile as File, compile as String, RenderManager }; diff --git a/src/UserConfig.js b/src/UserConfig.js index b01d685d6..0185fc677 100644 --- a/src/UserConfig.js +++ b/src/UserConfig.js @@ -397,7 +397,11 @@ class UserConfig { } /* Async friendly in 3.0 */ - addPlugin(plugin, options) { + addPlugin(plugin, options = {}) { + if (plugin?.eleventyPluginOptions?.unique && this.hasPlugin(plugin)) { + return; + } + if (this.isPluginExecution() || options?.immediate) { // might return a promise return this._executePlugin(plugin, options); @@ -410,8 +414,26 @@ class UserConfig { } } - hasPlugin(name) { - return this.plugins.some((entry) => this._getPluginName(entry.plugin) === name); + async resolvePlugin(name) { + let filenameLookup = { + "@11ty/eleventy/html-base-plugin": "./Plugins/HtmlBasePlugin.js", + "@11ty/eleventy/i18n-plugin": "./Plugins/I18nPlugin.js", + "@11ty/eleventy/render-plugin": "./Plugins/RenderPlugin.js", + "@11ty/eleventy/inputpath-to-url-plugin": "./Plugins/InputPathToUrl.js", + }; + if (!filenameLookup[name]) { + throw new Error(`Invalid name "${name}" passed to resolvePlugin.`); + } + // TODO add support for any npm package name. + let plugin = await import(filenameLookup[name]); + return plugin.default; + } + + hasPlugin(plugin) { + if (typeof plugin !== "string") { + plugin = this._getPluginName(plugin); + } + return this.plugins.some((entry) => this._getPluginName(entry.plugin) === plugin); } // Using Function.name https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/name#examples diff --git a/test/HtmlBasePluginTest.js b/test/HtmlBasePluginTest.js index cc4acad94..8119fc284 100644 --- a/test/HtmlBasePluginTest.js +++ b/test/HtmlBasePluginTest.js @@ -565,3 +565,44 @@ test("Using the HTML base plugin with pathPrefix: /test/ and transformed attribu ` ); }); + +test("HTML base plugin only adds once (unique)", async (t) => { + t.plan(2); + let elev = new Eleventy("./test/stubs-base/", "./test/stubs-base/_site", { + configPath: false, + config: function (eleventyConfig) { + // Runs before defaultConfig.js + t.is(eleventyConfig.plugins.length, 0); + eleventyConfig.addPlugin(HtmlBasePlugin); + eleventyConfig.addPlugin(HtmlBasePlugin); + eleventyConfig.addPlugin(HtmlBasePlugin); + eleventyConfig.addPlugin(HtmlBasePlugin); + t.is(eleventyConfig.plugins.length, 1); + }, + }); + await elev.init(); +}); + +test("HTML base plugin can resolve by name", async (t) => { + t.plan(2); + let elev = new Eleventy("./test/stubs-base/", "./test/stubs-base/_site", { + configPath: false, + config: async function (eleventyConfig) { + // Runs before defaultConfig.js + t.is(eleventyConfig.plugins.length, 0); + + let plugin = await eleventyConfig.resolvePlugin("@11ty/eleventy/html-base-plugin"); + eleventyConfig.addPlugin(plugin); + + // does not add duplicate + eleventyConfig.addPlugin(plugin); + + // does not add duplicate even with a different reference + eleventyConfig.addPlugin(HtmlBasePlugin); + eleventyConfig.addPlugin(HtmlBasePlugin); + + t.is(eleventyConfig.plugins.length, 1); + }, + }); + await elev.init(); +});