Skip to content

Commit

Permalink
Redirects (#7067)
Browse files Browse the repository at this point in the history
* Redirects spike

* Allow redirects in static mode

* Support in Netlify as well

* Adding a changeset

* Rename file

* Fix build problem

* Refactor to be more modular

* Fix location ref

* Late test should only run in SSR

* Support redirects in Netlify SSR configuration (#7167)

* Implement support for dynamic routes in redirects (#7173)

* Implement support for dynamic routes in redirects

* Remove the .only

* No need to special-case redirects in static build

* Implement support for redirects config in the Vercel adapter (#7182)

* Implement support for redirects config in the Vercel adapter

* Remove unused condition

* Move to a internal helper package

* Add support for the object notation in redirects

* Use status 308 for non-GET redirects (#7186)

* Implement redirects in Cloudflare (#7198)

* Implement redirects in Cloudflare

* Fix build

* Update tests b/c of new ordering

* Debug issue

* Use posix.join

* Update packages/underscore-redirects/package.json

Co-authored-by: Emanuele Stoppa <[email protected]>

* Update based on review comments

* Update broken test

---------

Co-authored-by: Emanuele Stoppa <[email protected]>

* Test that redirects can come from middleware (#7213)

* Test that redirects can come from middleware

* Allow non-promise returns for middleware

* Implement priority (#7210)

* Refactor

* Fix netlify test ordering

* Fix ordering again

* Redirects: Allow preventing the output of the static HTML file (#7245)

* Do a simple push for priority

* Adding changesets

* Put the implementation behind a flag.

* Self review

* Update .changeset/chatty-actors-stare.md

Co-authored-by: Chris Swithinbank <[email protected]>

* Update packages/astro/src/@types/astro.ts

Co-authored-by: Chris Swithinbank <[email protected]>

* Update packages/astro/src/@types/astro.ts

Co-authored-by: Chris Swithinbank <[email protected]>

* Update packages/astro/src/@types/astro.ts

Co-authored-by: Chris Swithinbank <[email protected]>

* Update packages/astro/src/@types/astro.ts

Co-authored-by: Chris Swithinbank <[email protected]>

* Update docs on dynamic restrictions.

* Update packages/astro/src/@types/astro.ts

Co-authored-by: Sarah Rainsberger <[email protected]>

* Update packages/astro/src/@types/astro.ts

Co-authored-by: Sarah Rainsberger <[email protected]>

* Code review changes

* Document netlify static adapter

* Update packages/astro/src/@types/astro.ts

Co-authored-by: Sarah Rainsberger <[email protected]>

* Slight reword

* Update .changeset/twenty-suns-vanish.md

Co-authored-by: Sarah Rainsberger <[email protected]>

* Add a note about public/_redirects file

* Update packages/astro/src/@types/astro.ts

Co-authored-by: Sarah Rainsberger <[email protected]>

---------

Co-authored-by: Emanuele Stoppa <[email protected]>
Co-authored-by: Chris Swithinbank <[email protected]>
Co-authored-by: Sarah Rainsberger <[email protected]>
Co-authored-by: Nate Moore <[email protected]>
  • Loading branch information
5 people authored Jun 5, 2023
1 parent a39fd40 commit 21a0fdc
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 15 deletions.
1 change: 1 addition & 0 deletions packages/integrations/vercel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"test": "mocha --exit --timeout 20000 test/"
},
"dependencies": {
"@astrojs/internal-helpers": "^0.1.0",
"@astrojs/webapi": "^2.2.0",
"@vercel/analytics": "^0.1.8",
"@vercel/nft": "^0.22.1",
Expand Down
58 changes: 43 additions & 15 deletions packages/integrations/vercel/src/lib/redirects.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { AstroConfig, RouteData, RoutePart } from 'astro';
import { appendForwardSlash } from '@astrojs/internal-helpers/path';
import nodePath from 'node:path';

const pathJoin = nodePath.posix.join;


// https://vercel.com/docs/project-configuration#legacy/routes
interface VercelRoute {
Expand Down Expand Up @@ -54,28 +59,51 @@ 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' ? appendForwardSlash(pattern) : pattern);
return pathJoin(config.base, path);
} else if(typeof route.redirect === 'object') {
return pathJoin(config.base, route.redirect.destination);
} else {
return pathJoin(config.base, route.redirect || '');
}
}

function getRedirectStatus(route: RouteData): number {
if(typeof route.redirect === 'object') {
return route.redirect.status;
}
return 301;
}

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;

for(const route of routes) {
if(route.type === 'redirect') {
redirects.push({
src: config.base + getMatchPattern(route.segments) + '/',
headers: { Location: config.base + getReplacePattern(route.segments) },
status: 308,
src: config.base + getMatchPattern(route.segments),
headers: { Location: getRedirectLocation(route, config) },
status: getRedirectStatus(route)
});
} 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,
});
}
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/integrations/vercel/src/static/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export default function vercelStatic({
outDir,
build: {
format: 'directory',
redirects: false,
},
vite: {
define: viteDefine,
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@test/astro-vercel-redirects",
"version": "0.0.0",
"private": true,
"dependencies": {
"@astrojs/vercel": "workspace:*",
"astro": "workspace:*"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html>
<head>
<title>Testing</title>
</head>
<body>
<h1>Testing</h1>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -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>
59 changes: 59 additions & 0 deletions packages/integrations/vercel/test/redirects.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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': '/',
'/three': {
status: 302,
destination: '/'
},
'/blog/[...slug]': '/team/articles/[...slug]',
},
experimental: {
redirects: true,
},
});
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 === '/\\/two');
expect(twoRoute.headers.Location).to.equal('/');
expect(twoRoute.status).to.equal(301);

const threeRoute = config.routes.find(r => r.src === '/\\/three');
expect(threeRoute.headers.Location).to.equal('/');
expect(threeRoute.status).to.equal(302);
});

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);
});
});

0 comments on commit 21a0fdc

Please sign in to comment.