import { DeepCopy } from "@11ty/eleventy-utils"; import urlFilter from "../Filters/Url.js"; import PathPrefixer from "../Util/PathPrefixer.js"; import { HtmlTransformer } from "../Util/HtmlTransformer.js"; import isValidUrl from "../Util/ValidUrl.js"; function addPathPrefixToUrl(url, pathPrefix, base) { let u; if (base) { u = new URL(url, base); } else { u = new URL(url); } // Add pathPrefix **after** url is transformed using base if (pathPrefix) { u.pathname = PathPrefixer.joinUrlParts(pathPrefix, u.pathname); } return u.toString(); } // pathprefix is only used when overrideBase is a full URL function transformUrl(url, base, opts = {}) { let { pathPrefix, pageUrl, htmlContext } = opts; // Warning, this will not work with HtmlTransformer, as we’ll receive "false" (string) here instead of `false` (boolean) if (url === false) { throw new Error( `Invalid url transformed in the HTML \`\` plugin.${url === false ? ` Did you attempt to link to a \`permalink: false\` page?` : ""} Received: ${url}`, ); } // full URL, return as-is if (isValidUrl(url)) { return url; } // Not a full URL, but with a full base URL // e.g. relative urls like "subdir/", "../subdir", "./subdir" if (isValidUrl(base)) { // convert relative paths to absolute path first using pageUrl if (pageUrl && !url.startsWith("/")) { let urlObj = new URL(url, `http://example.com${pageUrl}`); url = urlObj.pathname + (urlObj.hash || ""); } return addPathPrefixToUrl(url, pathPrefix, base); } // Not a full URL, nor a full base URL (call the built-in `url` filter) return urlFilter(url, base); } function eleventyHtmlBasePlugin(eleventyConfig, defaultOptions = {}) { let opts = DeepCopy( { // eleventyConfig.pathPrefix is new in Eleventy 2.0.0-canary.15 // `base` can be a directory (for path prefix transformations) // OR a full URL with origin and pathname baseHref: eleventyConfig.pathPrefix, extensions: "html", }, defaultOptions, ); // `filters` option to rename filters was removed in 3.0.0-alpha.13 // Renaming these would cause issues in other plugins (e.g. RSS) if (opts.filters !== undefined) { throw new Error( "The `filters` option in the HTML Base plugin was removed to prevent future cross-plugin compatibility issues.", ); } if (opts.baseHref === undefined) { throw new Error("The `baseHref` option is required in the HTML Base plugin."); } eleventyConfig.addFilter("addPathPrefixToFullUrl", function (url) { return addPathPrefixToUrl(url, eleventyConfig.pathPrefix); }); // Apply to one URL eleventyConfig.addFilter( "htmlBaseUrl", /** @this {object} */ function (url, baseOverride, pageUrlOverride) { let base = baseOverride || opts.baseHref; // Do nothing with a default base if (base === "/") { return url; } return transformUrl(url, base, { pathPrefix: eleventyConfig.pathPrefix, pageUrl: pageUrlOverride || this.page?.url, }); }, ); // Apply to a block of HTML eleventyConfig.addAsyncFilter( "transformWithHtmlBase", /** @this {object} */ function (content, baseOverride, pageUrlOverride) { let base = baseOverride || opts.baseHref; // Do nothing with a default base if (base === "/") { return content; } return HtmlTransformer.transformStandalone(content, (url, htmlContext) => { return transformUrl(url.trim(), base, { pathPrefix: eleventyConfig.pathPrefix, pageUrl: pageUrlOverride || this.page?.url, htmlContext, }); }); }, ); // Apply to all HTML output in your project eleventyConfig.htmlTransformer.addUrlTransform( opts.extensions, /** @this {object} */ function (urlInMarkup, htmlContext) { // baseHref override is via renderTransforms filter for adding the absolute URL (e.g. https://example.com/pathPrefix/) for RSS/Atom/JSON feeds return transformUrl(urlInMarkup.trim(), this.baseHref || opts.baseHref, { pathPrefix: eleventyConfig.pathPrefix, pageUrl: this.url, htmlContext, }); }, { priority: -2, // priority is descending, so this runs last (especially after AutoCopy and InputPathToUrl transform) enabled: function (context) { // Enabled when pathPrefix is non-default or via renderTransforms return Boolean(context.baseHref) || opts.baseHref !== "/"; }, }, ); } Object.defineProperty(eleventyHtmlBasePlugin, "eleventyPackage", { value: "@11ty/eleventy/html-base-plugin", }); Object.defineProperty(eleventyHtmlBasePlugin, "eleventyPluginOptions", { value: { unique: true, }, }); export default eleventyHtmlBasePlugin; export { transformUrl as applyBaseToUrl };