import path from "node:path"; import { TemplatePath } from "@11ty/eleventy-utils"; import isValidUrl from "../Util/ValidUrl.js"; function getValidPath(contentMap, testPath) { let normalized = TemplatePath.addLeadingDotSlash(testPath); // it must exist in the content map to be valid if (contentMap[normalized]) { return normalized; } } function normalizeInputPath(targetInputPath, inputDir, sourceInputPath, contentMap) { // inputDir is optional at the beginning of the developer supplied-path // Input directory already on the input path if (TemplatePath.join(targetInputPath).startsWith(TemplatePath.join(inputDir))) { let absolutePath = getValidPath(contentMap, targetInputPath); if (absolutePath) { return absolutePath; } } // Relative to project input directory let relativeToInputDir = getValidPath(contentMap, TemplatePath.join(inputDir, targetInputPath)); if (relativeToInputDir) { return relativeToInputDir; } if (targetInputPath && !path.isAbsolute(targetInputPath)) { // Relative to source file’s input path let sourceInputDir = TemplatePath.getDirFromFilePath(sourceInputPath); let relativeToSourceFile = getValidPath( contentMap, TemplatePath.join(sourceInputDir, targetInputPath), ); if (relativeToSourceFile) { return relativeToSourceFile; } } // the transform may have sent in a URL so we just return it as-is return targetInputPath; } function parseFilePath(filepath) { try { /* u: URL { href: 'file:///tmpl.njk#anchor', origin: 'null', protocol: 'file:', username: '', password: '', host: '', hostname: '', port: '', pathname: '/tmpl.njk', search: '', searchParams: URLSearchParams {}, hash: '#anchor' } */ // Note that `node:url` -> pathToFileURL creates an absolute path, which we don’t want // URL(`file:#anchor`) gives back a pathname of `/` let u = new URL(`file:${filepath}`); filepath = filepath.replace(u.search, ""); filepath = filepath.replace(u.hash, ""); return [ // search includes ?, hash includes # u.search + u.hash, filepath, ]; } catch (e) { return ["", filepath]; } } function FilterPlugin(eleventyConfig) { let contentMap; eleventyConfig.on("eleventy.contentMap", function ({ inputPathToUrl }) { contentMap = inputPathToUrl; }); eleventyConfig.addFilter("inputPathToUrl", function (targetFilePath) { if (!contentMap) { throw new Error("Internal error: contentMap not available for `inputPathToUrl` filter."); } if (isValidUrl(targetFilePath)) { return targetFilePath; } let inputDir = eleventyConfig.directories.input; let suffix = ""; [suffix, targetFilePath] = parseFilePath(targetFilePath); // @ts-ignore targetFilePath = normalizeInputPath(targetFilePath, inputDir, this.page.inputPath, contentMap); let urls = contentMap[targetFilePath]; if (!urls || urls.length === 0) { throw new Error( "`inputPathToUrl` filter could not find a matching target for " + targetFilePath, ); } return `${urls[0]}${suffix}`; }); } function TransformPlugin(eleventyConfig, defaultOptions = {}) { let opts = Object.assign( { extensions: "html", }, defaultOptions, ); let contentMap = null; eleventyConfig.on("eleventy.contentMap", function ({ inputPathToUrl }) { contentMap = inputPathToUrl; }); eleventyConfig.htmlTransformer.addUrlTransform(opts.extensions, function (targetFilepathOrUrl) { if (!contentMap) { throw new Error("Internal error: contentMap not available for the `pathToUrl` Transform."); } if (isValidUrl(targetFilepathOrUrl)) { return targetFilepathOrUrl; } let inputDir = eleventyConfig.directories.input; let suffix = ""; [suffix, targetFilepathOrUrl] = parseFilePath(targetFilepathOrUrl); targetFilepathOrUrl = normalizeInputPath( targetFilepathOrUrl, inputDir, // @ts-ignore this.page.inputPath, contentMap, ); let urls = contentMap[targetFilepathOrUrl]; if (!targetFilepathOrUrl || !urls || urls.length === 0) { // fallback, transforms don’t error on missing paths (though the pathToUrl filter does) return `${targetFilepathOrUrl}${suffix}`; } return `${urls[0]}${suffix}`; }); } Object.defineProperty(FilterPlugin, "eleventyPackage", { value: "@11ty/eleventy/inputpath-to-url-filter-plugin", }); Object.defineProperty(FilterPlugin, "eleventyPluginOptions", { value: { unique: true, }, }); Object.defineProperty(TransformPlugin, "eleventyPackage", { value: "@11ty/eleventy/inputpath-to-url-transform-plugin", }); Object.defineProperty(TransformPlugin, "eleventyPluginOptions", { value: { unique: true, }, }); export default TransformPlugin; export { FilterPlugin, TransformPlugin };