/
extensionRedirects.ts
96 lines (83 loc) · 2.76 KB
/
extensionRedirects.ts
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
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import {
addTrailingSlash,
removeSuffix,
removeTrailingSlash,
} from '@docusaurus/utils';
import type {RedirectItem} from './types';
const ExtensionAdditionalMessage =
'If the redirect extension system is not good enough for your use case, you can create redirects yourself with the "createRedirects" plugin option.';
const validateExtension = (ext: string) => {
if (!ext) {
throw new Error(
`Extension "${ext}" is not allowed.\n${ExtensionAdditionalMessage}`,
);
}
if (ext.includes('.')) {
throw new Error(
`Extension "${ext}" contains a "." (dot) which is not allowed.\n${ExtensionAdditionalMessage}`,
);
}
if (ext.includes('/')) {
throw new Error(
`Extension "${ext}" contains a "/" (slash) which is not allowed.\n${ExtensionAdditionalMessage}`,
);
}
if (encodeURIComponent(ext) !== ext) {
throw new Error(
`Extension "${ext}" contains invalid URI characters.\n${ExtensionAdditionalMessage}`,
);
}
};
const addLeadingDot = (extension: string) => `.${extension}`;
/**
* Create new `/path` that redirects to existing an `/path.html`
*/
export function createToExtensionsRedirects(
paths: string[],
extensions: string[],
): RedirectItem[] {
extensions.forEach(validateExtension);
const dottedExtensions = extensions.map(addLeadingDot);
const createPathRedirects = (path: string): RedirectItem[] => {
const extensionFound = dottedExtensions.find((ext) => path.endsWith(ext));
if (extensionFound) {
return [{from: removeSuffix(path, extensionFound), to: path}];
}
return [];
};
return paths.flatMap(createPathRedirects);
}
/**
* Create new `/path.html/index.html` that redirects to existing an `/path`
* The filename pattern might look weird but it's on purpose (see
* https://github.com/facebook/docusaurus/issues/5055)
*/
export function createFromExtensionsRedirects(
paths: string[],
extensions: string[],
): RedirectItem[] {
extensions.forEach(validateExtension);
const dottedExtensions = extensions.map(addLeadingDot);
const alreadyEndsWithAnExtension = (str: string) =>
dottedExtensions.some((ext) => str.endsWith(ext));
const createPathRedirects = (path: string): RedirectItem[] => {
if (path === '' || path === '/' || alreadyEndsWithAnExtension(path)) {
return [];
}
return extensions.map((ext) => ({
// /path => /path.html
// /path/ => /path.html/
from: path.endsWith('/')
? addTrailingSlash(`${removeTrailingSlash(path)}.${ext}`)
: `${path}.${ext}`,
to: path,
}));
};
return paths.flatMap(createPathRedirects);
}