2023-01-23 14:47:33 +00:00
|
|
|
import type { AstroConfig, RouteData } from 'astro';
|
2022-05-11 15:13:23 +00:00
|
|
|
import fs from 'fs';
|
|
|
|
|
2023-01-23 14:47:33 +00:00
|
|
|
type RedirectDefinition = {
|
|
|
|
dynamic: boolean;
|
|
|
|
input: string;
|
|
|
|
target: string;
|
|
|
|
weight: 0 | 1;
|
|
|
|
status: 200 | 404;
|
|
|
|
};
|
|
|
|
|
2022-05-11 15:13:23 +00:00
|
|
|
export async function createRedirects(
|
2023-01-23 14:47:33 +00:00
|
|
|
config: AstroConfig,
|
2022-05-11 15:14:43 +00:00
|
|
|
routes: RouteData[],
|
|
|
|
dir: URL,
|
|
|
|
entryFile: string,
|
2023-01-27 15:20:34 +00:00
|
|
|
type: 'functions' | 'edge-functions' | 'builders'
|
2022-05-11 15:14:43 +00:00
|
|
|
) {
|
|
|
|
const _redirectsURL = new URL('./_redirects', dir);
|
2023-01-27 15:20:34 +00:00
|
|
|
const kind = type ?? 'functions';
|
2022-05-11 15:13:23 +00:00
|
|
|
|
2023-01-23 14:47:33 +00:00
|
|
|
const definitions: RedirectDefinition[] = [];
|
|
|
|
|
2022-05-11 15:14:43 +00:00
|
|
|
for (const route of routes) {
|
|
|
|
if (route.pathname) {
|
2022-12-16 16:38:37 +00:00
|
|
|
if (route.distURL) {
|
2023-01-23 14:47:33 +00:00
|
|
|
definitions.push({
|
|
|
|
dynamic: false,
|
|
|
|
input: route.pathname,
|
|
|
|
target: prependForwardSlash(route.distURL.toString().replace(dir.toString(), '')),
|
|
|
|
status: 200,
|
2023-01-23 14:49:20 +00:00
|
|
|
weight: 1,
|
2023-01-23 14:47:33 +00:00
|
|
|
});
|
2022-12-16 16:38:37 +00:00
|
|
|
} else {
|
2023-01-23 14:47:33 +00:00
|
|
|
definitions.push({
|
|
|
|
dynamic: false,
|
|
|
|
input: route.pathname,
|
|
|
|
target: `/.netlify/${kind}/${entryFile}`,
|
|
|
|
status: 200,
|
|
|
|
weight: 1,
|
|
|
|
});
|
2022-08-11 23:26:32 +00:00
|
|
|
|
2022-12-16 16:38:37 +00:00
|
|
|
if (route.route === '/404') {
|
2023-01-23 14:47:33 +00:00
|
|
|
definitions.push({
|
|
|
|
dynamic: true,
|
|
|
|
input: '/*',
|
|
|
|
target: `/.netlify/${kind}/${entryFile}`,
|
|
|
|
status: 404,
|
2023-01-23 14:49:20 +00:00
|
|
|
weight: 0,
|
2023-01-23 14:47:33 +00:00
|
|
|
});
|
2022-12-16 16:38:37 +00:00
|
|
|
}
|
2022-08-11 23:26:32 +00:00
|
|
|
}
|
2022-05-11 15:14:43 +00:00
|
|
|
} else {
|
|
|
|
const pattern =
|
2023-01-23 14:49:20 +00:00
|
|
|
'/' +
|
|
|
|
route.segments
|
|
|
|
.map(([part]) => {
|
|
|
|
//(part.dynamic ? '*' : part.content)
|
|
|
|
if (part.dynamic) {
|
|
|
|
if (part.spread) {
|
|
|
|
return '*';
|
|
|
|
} else {
|
|
|
|
return ':' + part.content;
|
|
|
|
}
|
2023-01-23 14:47:33 +00:00
|
|
|
} else {
|
2023-01-23 14:49:20 +00:00
|
|
|
return part.content;
|
2023-01-23 14:47:33 +00:00
|
|
|
}
|
2023-01-23 14:49:20 +00:00
|
|
|
})
|
|
|
|
.join('/');
|
2023-01-23 14:47:33 +00:00
|
|
|
|
2022-12-16 16:38:37 +00:00
|
|
|
if (route.distURL) {
|
2023-01-23 14:49:20 +00:00
|
|
|
const target =
|
|
|
|
`${pattern}` + (config.build.format === 'directory' ? '/index.html' : '.html');
|
2023-01-23 14:47:33 +00:00
|
|
|
definitions.push({
|
|
|
|
dynamic: true,
|
|
|
|
input: pattern,
|
|
|
|
target,
|
|
|
|
status: 200,
|
2023-01-23 14:49:20 +00:00
|
|
|
weight: 1,
|
2023-01-23 14:47:33 +00:00
|
|
|
});
|
2022-12-16 16:38:37 +00:00
|
|
|
} else {
|
2023-01-23 14:47:33 +00:00
|
|
|
definitions.push({
|
|
|
|
dynamic: true,
|
|
|
|
input: pattern,
|
|
|
|
target: `/.netlify/${kind}/${entryFile}`,
|
|
|
|
status: 200,
|
2023-01-23 14:49:20 +00:00
|
|
|
weight: 1,
|
2023-01-23 14:47:33 +00:00
|
|
|
});
|
2022-12-16 16:38:37 +00:00
|
|
|
}
|
2022-05-11 15:14:43 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-11 15:13:23 +00:00
|
|
|
|
2023-01-23 14:47:33 +00:00
|
|
|
let _redirects = prettify(definitions);
|
|
|
|
|
2022-05-11 15:14:43 +00:00
|
|
|
// 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');
|
|
|
|
}
|
2023-01-23 14:47:33 +00:00
|
|
|
|
|
|
|
function prettify(definitions: RedirectDefinition[]) {
|
2023-01-23 14:49:20 +00:00
|
|
|
let minInputLength = 0,
|
|
|
|
minTargetLength = 0;
|
2023-01-23 14:47:33 +00:00
|
|
|
definitions.sort((a, b) => {
|
|
|
|
// Find the longest input, so we can format things nicely
|
2023-01-23 14:49:20 +00:00
|
|
|
if (a.input.length > minInputLength) {
|
2023-01-23 14:47:33 +00:00
|
|
|
minInputLength = a.input.length;
|
|
|
|
}
|
2023-01-23 14:49:20 +00:00
|
|
|
if (b.input.length > minInputLength) {
|
2023-01-23 14:47:33 +00:00
|
|
|
minInputLength = b.input.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Same for the target
|
2023-01-23 14:49:20 +00:00
|
|
|
if (a.target.length > minTargetLength) {
|
2023-01-23 14:47:33 +00:00
|
|
|
minTargetLength = a.target.length;
|
|
|
|
}
|
2023-01-23 14:49:20 +00:00
|
|
|
if (b.target.length > minTargetLength) {
|
2023-01-23 14:47:33 +00:00
|
|
|
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.
|
2023-01-23 14:49:20 +00:00
|
|
|
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;
|
2023-01-23 14:47:33 +00:00
|
|
|
});
|
|
|
|
return _redirects;
|
|
|
|
}
|
|
|
|
|
|
|
|
function prependForwardSlash(str: string) {
|
|
|
|
return str[0] === '/' ? str : '/' + str;
|
|
|
|
}
|