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