@flyewi/eleventy-plugin-redirects 0.1.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- package/lib/formats.js +36 -5
- package/lib/redirects.js +7 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,9 +37,11 @@ By default this writes an `_redirects` file into your output directory after eve
|
|
|
37
37
|
| `defaultStatus` | `301` | Status code used when an entry doesn't specify one. |
|
|
38
38
|
| `netlify` | `true` | Write a Netlify `_redirects` file. |
|
|
39
39
|
| `netlifyFile` | `"_redirects"` | Filename for the Netlify output, relative to the output directory. |
|
|
40
|
-
| `apache` | `false` | Write an Apache `.htaccess` with `
|
|
40
|
+
| `apache` | `false` | Write an Apache `.htaccess` with exact-match `RedirectMatch` directives. |
|
|
41
41
|
| `apacheFile` | `".htaccess"` | Filename for the Apache output. |
|
|
42
42
|
| `html` | `false` | Also write a static HTML fallback page at each `from` URL (meta-refresh + `rel=canonical`), for hosts (e.g. GitHub Pages) that can't act on `_redirects`/`.htaccess`. |
|
|
43
|
+
| `htmlLang` | `"en"` | `lang` attribute of the HTML fallback pages. |
|
|
44
|
+
| `htmlMessage` | `"This page has moved to"` | Text shown (followed by the link) on the HTML fallback pages, for the moment before the redirect or when it doesn't fire. |
|
|
43
45
|
| `log` | `true` | Log a one-line summary after each build. |
|
|
44
46
|
|
|
45
47
|
## Netlify output
|
|
@@ -54,10 +56,16 @@ If your output directory already contains an `_redirects` file (e.g. copied thro
|
|
|
54
56
|
## Apache output
|
|
55
57
|
|
|
56
58
|
```
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
RedirectMatch 301 ^/old-path/$ /new-path/
|
|
60
|
+
RedirectMatch 302 ^/legacy-page/$ /new-page/
|
|
59
61
|
```
|
|
60
62
|
|
|
63
|
+
Each rule matches its path **exactly**, the same as in Netlify's `_redirects`: `/old-path/` redirects, `/old-path/sub/` does not. Regex characters in the path (e.g. the `.` in `/old.html`) are escaped, and paths containing spaces are quoted. The query string is passed on to the target unchanged.
|
|
64
|
+
|
|
65
|
+
> **Changed in 0.3.0:** earlier versions wrote `Redirect` directives, which Apache treats as a *prefix* match (`/old-path/` also redirected `/old-path/anything` to `/new-path/anything`). If you relied on that, add the deeper paths as separate entries.
|
|
66
|
+
|
|
67
|
+
Requires `mod_alias`, which is enabled on practically every Apache host, and `.htaccess` overrides (`AllowOverride FileInfo`) — on shared hosting this is usually the case.
|
|
68
|
+
|
|
61
69
|
## HTML fallback pages
|
|
62
70
|
|
|
63
71
|
With `html: true`, each `from` path also gets a real HTML file at that URL (e.g. `/old-path/` → `old-path/index.html` in the output directory) containing:
|
|
@@ -65,8 +73,11 @@ With `html: true`, each `from` path also gets a real HTML file at that URL (e.g.
|
|
|
65
73
|
```html
|
|
66
74
|
<meta http-equiv="refresh" content="0; url=/new-path/">
|
|
67
75
|
<link rel="canonical" href="/new-path/">
|
|
76
|
+
<script>location.replace("/new-path/" + location.hash);</script>
|
|
68
77
|
```
|
|
69
78
|
|
|
79
|
+
The script redirects immediately and keeps the URL fragment (`/old-path/#section` → `/new-path/#section`), like a server-side redirect would; the meta refresh is the fallback when JavaScript is off. If the target already contains a `#fragment`, that one is kept instead.
|
|
80
|
+
|
|
70
81
|
This is a static fallback for platforms without server-side redirect support — a real `_redirects`/`.htaccess` (or your host/CDN's redirect rules) is always the better option when available, since the HTML approach is a moment slower and depends on the browser executing the refresh.
|
|
71
82
|
|
|
72
83
|
## License
|
package/lib/formats.js
CHANGED
|
@@ -4,8 +4,28 @@ function toNetlify(list) {
|
|
|
4
4
|
return list.map(({ from, to, status }) => `${from} ${to} ${status}`).join("\n") + "\n";
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
+
function escapeRegex(str) {
|
|
8
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Apache splits directive arguments on whitespace; quote those that contain it.
|
|
12
|
+
function apacheArg(str) {
|
|
13
|
+
return /[\s"]/.test(str) ? `"${str.replace(/"/g, '\\"')}"` : str;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// RedirectMatch with an anchored, escaped pattern matches the path exactly,
|
|
17
|
+
// like Netlify's _redirects. Plain `Redirect` would be a prefix match
|
|
18
|
+
// (/old/ would also catch /old/anything), so the same config would behave
|
|
19
|
+
// differently depending on the host.
|
|
7
20
|
function toApache(list) {
|
|
8
|
-
return
|
|
21
|
+
return (
|
|
22
|
+
list
|
|
23
|
+
.map(({ from, to, status }) => {
|
|
24
|
+
const pattern = apacheArg(`^${escapeRegex(from)}$`);
|
|
25
|
+
return `RedirectMatch ${status} ${pattern} ${apacheArg(to)}`;
|
|
26
|
+
})
|
|
27
|
+
.join("\n") + "\n"
|
|
28
|
+
);
|
|
9
29
|
}
|
|
10
30
|
|
|
11
31
|
function escapeAttr(str) {
|
|
@@ -16,21 +36,32 @@ function escapeAttr(str) {
|
|
|
16
36
|
.replace(/>/g, ">");
|
|
17
37
|
}
|
|
18
38
|
|
|
39
|
+
// JSON.stringify yields a valid JS string literal; escaping "<" additionally
|
|
40
|
+
// keeps a target containing "</script>" from closing the inline script early.
|
|
41
|
+
function toScriptString(str) {
|
|
42
|
+
return JSON.stringify(String(str)).replace(/</g, "\\u003c");
|
|
43
|
+
}
|
|
44
|
+
|
|
19
45
|
// A static fallback for hosts that can't act on _redirects/.htaccess (e.g.
|
|
20
46
|
// GitHub Pages): an actual HTML file at the old URL that both redirects
|
|
21
47
|
// browsers instantly and points crawlers at the new canonical URL.
|
|
22
|
-
|
|
48
|
+
// The script runs before the meta refresh fires and carries the URL fragment
|
|
49
|
+
// (#section) over, as a server-side 301 would; the meta refresh remains as the
|
|
50
|
+
// no-JS fallback. A target that already has its own fragment keeps it.
|
|
51
|
+
function toHtmlPage(to, { lang = "en", message = "This page has moved to" } = {}) {
|
|
23
52
|
const target = escapeAttr(to);
|
|
53
|
+
const hash = String(to).includes("#") ? "" : " + location.hash";
|
|
24
54
|
return `<!doctype html>
|
|
25
|
-
<html lang="
|
|
55
|
+
<html lang="${escapeAttr(lang)}">
|
|
26
56
|
<head>
|
|
27
57
|
<meta charset="utf-8">
|
|
28
|
-
<title
|
|
58
|
+
<title>${escapeAttr(message)} ${target}</title>
|
|
29
59
|
<meta http-equiv="refresh" content="0; url=${target}">
|
|
30
60
|
<link rel="canonical" href="${target}">
|
|
61
|
+
<script>location.replace(${toScriptString(to)}${hash});</script>
|
|
31
62
|
</head>
|
|
32
63
|
<body>
|
|
33
|
-
<p
|
|
64
|
+
<p>${escapeAttr(message)} <a href="${target}">${target}</a>.</p>
|
|
34
65
|
</body>
|
|
35
66
|
</html>
|
|
36
67
|
`;
|
package/lib/redirects.js
CHANGED
|
@@ -10,6 +10,8 @@ const DEFAULTS = {
|
|
|
10
10
|
netlify: true,
|
|
11
11
|
apache: false,
|
|
12
12
|
html: false,
|
|
13
|
+
htmlLang: "en",
|
|
14
|
+
htmlMessage: "This page has moved to",
|
|
13
15
|
netlifyFile: "_redirects",
|
|
14
16
|
apacheFile: ".htaccess",
|
|
15
17
|
log: true,
|
|
@@ -50,7 +52,7 @@ module.exports = function redirectsPlugin(eleventyConfig, userOptions = {}) {
|
|
|
50
52
|
// Preserve (and prepend ahead of) any existing .htaccess content, e.g.
|
|
51
53
|
// hand-maintained directives (ErrorDocument, mod_deflate, mod_expires)
|
|
52
54
|
// brought in via a static passthrough copy — this plugin only owns the
|
|
53
|
-
//
|
|
55
|
+
// RedirectMatch lines, not the whole file.
|
|
54
56
|
const existing = readExisting(apachePath);
|
|
55
57
|
fs.writeFileSync(apachePath, toApache(list) + existing);
|
|
56
58
|
}
|
|
@@ -59,7 +61,10 @@ module.exports = function redirectsPlugin(eleventyConfig, userOptions = {}) {
|
|
|
59
61
|
for (const { from, to } of list) {
|
|
60
62
|
const outPath = path.join(outputDir, toOutputPath(from));
|
|
61
63
|
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
62
|
-
fs.writeFileSync(
|
|
64
|
+
fs.writeFileSync(
|
|
65
|
+
outPath,
|
|
66
|
+
toHtmlPage(to, { lang: options.htmlLang, message: options.htmlMessage })
|
|
67
|
+
);
|
|
63
68
|
}
|
|
64
69
|
}
|
|
65
70
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flyewi/eleventy-plugin-redirects",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Eleventy plugin that generates Netlify _redirects, Apache .htaccess, and static HTML fallback pages from a single redirects config.",
|
|
5
5
|
"main": "lib/redirects.js",
|
|
6
6
|
"type": "commonjs",
|