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
|
import type { AstroConfig, RouteData } from 'astro';
import fs from 'fs';
type RedirectDefinition = {
dynamic: boolean;
input: string;
target: string;
weight: 0 | 1;
status: 200 | 404;
};
export async function createRedirects(
config: AstroConfig,
routes: RouteData[],
dir: URL,
entryFile: string,
edge: boolean
) {
const _redirectsURL = new URL('./_redirects', dir);
const kind = edge ? 'edge-functions' : 'functions';
const definitions: RedirectDefinition[] = [];
for (const route of routes) {
if (route.pathname) {
if (route.distURL) {
definitions.push({
dynamic: false,
input: route.pathname,
target: prependForwardSlash(route.distURL.toString().replace(dir.toString(), '')),
status: 200,
weight: 1
});
} else {
definitions.push({
dynamic: false,
input: route.pathname,
target: `/.netlify/${kind}/${entryFile}`,
status: 200,
weight: 1,
});
if (route.route === '/404') {
definitions.push({
dynamic: true,
input: '/*',
target: `/.netlify/${kind}/${entryFile}`,
status: 404,
weight: 0
});
}
}
} else {
const pattern =
'/' + route.segments.map(([part]) => {
//(part.dynamic ? '*' : part.content)
if(part.dynamic) {
if(part.spread) {
return '*';
} else {
return ':' + part.content;
}
} else {
return part.content;
}
}).join('/');
if (route.distURL) {
const target = `${pattern}` + (config.build.format === 'directory' ? '/index.html' : '.html');
definitions.push({
dynamic: true,
input: pattern,
target,
status: 200,
weight: 1
});
} else {
definitions.push({
dynamic: true,
input: pattern,
target: `/.netlify/${kind}/${entryFile}`,
status: 200,
weight: 1
});
}
}
}
let _redirects = prettify(definitions);
// Always use appendFile() because the redirects file could already exist,
// e.g. due to a `/public/_redirects` file that got copied to the output dir.
// If the file does not exist yet, appendFile() automatically creates it.
await fs.promises.appendFile(_redirectsURL, _redirects, 'utf-8');
}
function prettify(definitions: RedirectDefinition[]) {
let minInputLength = 0, minTargetLength = 0;
definitions.sort((a, b) => {
// Find the longest input, so we can format things nicely
if(a.input.length > minInputLength) {
minInputLength = a.input.length;
}
if(b.input.length > minInputLength) {
minInputLength = b.input.length;
}
// Same for the target
if(a.target.length > minTargetLength) {
minTargetLength = a.target.length;
}
if(b.target.length > minTargetLength) {
minTargetLength = b.target.length;
}
// Sort dynamic routes on top
return b.weight - a.weight;
});
let _redirects = '';
// Loop over the definitions
definitions.forEach((defn, i) => {
// Figure out the number of spaces to add. We want at least 4 spaces
// after the input. This ensure that all targets line up together.
let inputSpaces = (minInputLength - defn.input.length) + 4;
let targetSpaces = (minTargetLength - defn.target.length) + 4;
_redirects += (i === 0 ? '' : '\n') + defn.input + ' '.repeat(inputSpaces) + defn.target + ' '.repeat(Math.abs(targetSpaces)) + defn.status;
});
return _redirects;
}
function prependForwardSlash(str: string) {
return str[0] === '/' ? str : '/' + str;
}
|