1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
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 \`<base>\` 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 };
|