Implement support for redirects config in the Vercel adapter

This commit is contained in:
Matthew Phillips 2023-05-23 12:34:11 -04:00
parent 11a517b1f1
commit b8ef648f8c
8 changed files with 148 additions and 20 deletions

View file

@ -177,7 +177,7 @@ async function generatePage(
if(pageData.route.redirectRoute) {
pageModulePromise = ssrEntry.pageMap?.get(pageData.route.redirectRoute!.component);
} else {
pageModulePromise = { default: () => {} } as any;
pageModulePromise = () => Promise.resolve<any>({ default: () => {} });
}
}
if (!pageModulePromise) {

View file

@ -36,6 +36,10 @@ function getMatchPattern(segments: RoutePart[][]) {
.join('');
}
function appendTrailingSlash(route: string): string {
return route.at(-1) === '/' ? route : route + '/';
}
function getReplacePattern(segments: RoutePart[][]) {
let n = 0;
let result = '';
@ -54,28 +58,44 @@ function getReplacePattern(segments: RoutePart[][]) {
return result;
}
function getRedirectLocation(route: RouteData, config: AstroConfig): string {
if(route.redirectRoute) {
const pattern = getReplacePattern(route.redirectRoute.segments);
const path = (config.trailingSlash === 'always' ? appendTrailingSlash(pattern) : pattern);
return config.base + path;
} else {
return config.base + route.redirect;
}
}
export function getRedirects(routes: RouteData[], config: AstroConfig): VercelRoute[] {
let redirects: VercelRoute[] = [];
if (config.trailingSlash === 'always') {
for (const route of routes) {
if (route.type !== 'page' || route.segments.length === 0) continue;
redirects.push({
src: config.base + getMatchPattern(route.segments),
headers: { Location: config.base + getReplacePattern(route.segments) + '/' },
status: 308,
});
}
} else if (config.trailingSlash === 'never') {
for (const route of routes) {
if (route.type !== 'page' || route.segments.length === 0) continue;
redirects.push({
src: config.base + getMatchPattern(route.segments) + '/',
headers: { Location: config.base + getReplacePattern(route.segments) },
status: 308,
});
for(const route of routes) {
if(route.type === 'redirect') {
if(true || route.pathname) {
redirects.push({
src: config.base + getMatchPattern(route.segments),
headers: { Location: getRedirectLocation(route, config) },
status: 301
})
} else {
console.error(`Dynamic routes not yet supported`);
}
} else if (route.type === 'page') {
if (config.trailingSlash === 'always') {
redirects.push({
src: config.base + getMatchPattern(route.segments),
headers: { Location: config.base + getReplacePattern(route.segments) + '/' },
status: 308,
});
} else if (config.trailingSlash === 'never') {
redirects.push({
src: config.base + getMatchPattern(route.segments) + '/',
headers: { Location: config.base + getReplacePattern(route.segments) },
status: 308,
});
}
}
}

View file

@ -0,0 +1,9 @@
import vercel from '@astrojs/vercel/static';
import { defineConfig } from 'astro/config';
export default defineConfig({
adapter: vercel({imageService: true}),
experimental: {
assets: true
}
});

View file

@ -0,0 +1,9 @@
{
"name": "@test/astro-vercel-redirects",
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
"astro": "workspace:*"
}
}

View file

@ -0,0 +1,8 @@
<html>
<head>
<title>Testing</title>
</head>
<body>
<h1>Testing</h1>
</body>
</html>

View file

@ -0,0 +1,25 @@
---
export const getStaticPaths = (async () => {
const posts = [
{ slug: 'one', data: {draft: false, title: 'One'} },
{ slug: 'two', data: {draft: false, title: 'Two'} }
];
return posts.map((post) => {
return {
params: { slug: post.slug },
props: { draft: post.data.draft, title: post.data.title },
};
});
})
const { slug } = Astro.params;
const { title } = Astro.props;
---
<html>
<head>
<title>{ title }</title>
</head>
<body>
<h1>{ title }</h1>
</body>
</html>

View file

@ -0,0 +1,48 @@
import { expect } from 'chai';
import * as cheerio from 'cheerio';
import { loadFixture } from './test-utils.js';
describe('Redirects', () => {
/** @type {import('../../../astro/test/test-utils.js').Fixture} */
let fixture;
before(async () => {
fixture = await loadFixture({
root: './fixtures/redirects/',
redirects: {
'/one': '/',
'/two': '/',
'/blog/[...slug]': '/team/articles/[...slug]',
}
});
await fixture.build();
});
async function getConfig() {
const json = await fixture.readFile('../.vercel/output/config.json');
const config = JSON.parse(json);
return config;
}
it('define static routes', async () => {
const config = await getConfig();
const oneRoute = config.routes.find(r => r.src === '/\\/one');
expect(oneRoute.headers.Location).to.equal('/');
expect(oneRoute.status).to.equal(301);
const twoRoute = config.routes.find(r => r.src === '/\\/one');
expect(twoRoute.headers.Location).to.equal('/');
expect(twoRoute.status).to.equal(301);
});
it('defines dynamic routes', async () => {
const config = await getConfig();
const blogRoute = config.routes.find(r => r.src.startsWith('/\\/blog'));
expect(blogRoute).to.not.be.undefined;
expect(blogRoute.headers.Location.startsWith('/team/articles')).to.equal(true);
expect(blogRoute.status).to.equal(301);
});
});

View file

@ -4868,6 +4868,15 @@ importers:
specifier: workspace:*
version: link:../../../../../astro
packages/integrations/vercel/test/fixtures/redirects:
dependencies:
'@astrojs/vercel':
specifier: workspace:*
version: link:../../..
astro:
specifier: workspace:*
version: link:../../../../../astro
packages/integrations/vercel/test/fixtures/serverless-prerender:
dependencies:
'@astrojs/vercel':