Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redirects #7067

Merged
merged 49 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
46e7269
Redirects spike
matthewp May 10, 2023
ef3ea94
Allow redirects in static mode
matthewp May 11, 2023
d6b7104
Support in Netlify as well
matthewp May 11, 2023
f52116a
Adding a changeset
matthewp May 11, 2023
a70820b
Rename file
matthewp May 11, 2023
475294a
Merge branch 'main' into redirects-ssg
matthewp May 19, 2023
eed6a72
Fix build problem
matthewp May 19, 2023
1749ce5
Refactor to be more modular
matthewp May 19, 2023
ab0539b
Fix location ref
matthewp May 19, 2023
83ed366
Late test should only run in SSR
matthewp May 19, 2023
e9e4d72
Merge branch 'main' into redirects-ssg
matthewp May 22, 2023
4857c7d
Support redirects in Netlify SSR configuration (#7167)
matthewp May 23, 2023
25d7d20
Implement support for dynamic routes in redirects (#7173)
matthewp May 23, 2023
11a517b
Merge branch 'main' into redirects-ssg
matthewp May 23, 2023
ffc771e
Implement support for redirects config in the Vercel adapter (#7182)
matthewp May 23, 2023
f55e422
Add support for the object notation in redirects
matthewp May 23, 2023
2904ced
Merge branch 'main' into redirects-ssg
matthewp May 23, 2023
c2f889b
Use status 308 for non-GET redirects (#7186)
matthewp May 24, 2023
af2ceea
Merge branch 'main' into redirects-ssg
matthewp May 24, 2023
8b4d248
Implement redirects in Cloudflare (#7198)
matthewp May 25, 2023
ef9a456
Test that redirects can come from middleware (#7213)
matthewp May 26, 2023
fa03a41
Implement priority (#7210)
matthewp May 30, 2023
02a8506
Merge branch 'main' into redirects-ssg
matthewp May 30, 2023
eb7617d
Refactor
matthewp May 30, 2023
d7d0b22
Fix netlify test ordering
matthewp May 30, 2023
fd52bd6
Merge branch 'main' into redirects-ssg
matthewp May 30, 2023
a39eb51
Fix ordering again
matthewp May 30, 2023
d3895a2
Redirects: Allow preventing the output of the static HTML file (#7245)
matthewp May 30, 2023
2700c12
Do a simple push for priority
matthewp May 30, 2023
60dcfb6
Adding changesets
matthewp May 30, 2023
c040279
Put the implementation behind a flag.
matthewp May 31, 2023
79263e3
Self review
matthewp May 31, 2023
5198529
Update .changeset/chatty-actors-stare.md
matthewp May 31, 2023
63b5cfa
Update packages/astro/src/@types/astro.ts
matthewp May 31, 2023
17d0538
Update packages/astro/src/@types/astro.ts
matthewp May 31, 2023
7b64d65
Update packages/astro/src/@types/astro.ts
matthewp May 31, 2023
cd8e703
Update packages/astro/src/@types/astro.ts
matthewp May 31, 2023
7fd2a0a
Update docs on dynamic restrictions.
matthewp May 31, 2023
42bfa65
Update packages/astro/src/@types/astro.ts
matthewp Jun 1, 2023
4da0c7b
Update packages/astro/src/@types/astro.ts
matthewp Jun 1, 2023
3eaf936
Code review changes
matthewp Jun 1, 2023
7c0905b
Document netlify static adapter
matthewp Jun 1, 2023
63210fe
Update packages/astro/src/@types/astro.ts
matthewp Jun 1, 2023
57e8105
Slight reword
matthewp Jun 1, 2023
131f9d2
Update .changeset/twenty-suns-vanish.md
matthewp Jun 1, 2023
afcc209
Add a note about public/_redirects file
matthewp Jun 1, 2023
4afaa37
Merge branch 'main' into redirects-ssg
natemoo-re Jun 1, 2023
303b79a
Update packages/astro/src/@types/astro.ts
matthewp Jun 1, 2023
33a9d5f
Merge branch 'main' into redirects-ssg
matthewp Jun 5, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .changeset/chatty-actors-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
'astro': minor
---

Experimental redirects support

This change adds support for the redirects RFC, currently in stage 3: https://github.com/withastro/roadmap/pull/587

Now you can specify redirects in your Astro config:

```js
import { defineConfig } from 'astro/config';

export defineConfig({
redirects: {
'/blog/old-post': '/blog/new-post'
}
});
```

You can also specify spread routes using the same syntax as in file-based routing:

```js
import { defineConfig } from 'astro/config';

export defineConfig({
redirects: {
'/blog/[...slug]': '/articles/[...slug]'
}
});
```

By default Astro will build HTML files that contain the `<meta http-equiv="refresh">` tag. Adapters can also support redirect routes and create configuration for real HTTP-level redirects in production.
7 changes: 7 additions & 0 deletions .changeset/fuzzy-ladybugs-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astrojs/cloudflare': minor
---

Support for experimental redirects

This adds support for the redirects RFC in the Cloudflare adapter. No changes are necessary, simply use configured redirects and the adapter will update your `_redirects` file.
7 changes: 7 additions & 0 deletions .changeset/hip-news-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@astrojs/vercel': minor
---

Support for experimental redirects

This adds support for the redirects RFC in the Vercel adapter. No changes are necessary, simply use configured redirects and the adapter will output the vercel.json file with the configuration values.
9 changes: 9 additions & 0 deletions .changeset/twenty-suns-vanish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@astrojs/netlify': minor
---

Support for experimental redirects

This adds support for the redirects RFC in the Netlify adapter, including a new `@astrojs/netlify/static` adapter for static sites.

No changes are necessary when using SSR. Simply use configured redirects and the adapter will update your `_redirects` file.
1 change: 1 addition & 0 deletions packages/astro/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@
},
"dependencies": {
"@astrojs/compiler": "^1.4.0",
"@astrojs/internal-helpers": "^0.1.0",
"@astrojs/language-server": "^1.0.0",
"@astrojs/markdown-remark": "^2.2.1",
"@astrojs/telemetry": "^2.1.1",
Expand Down
111 changes: 108 additions & 3 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ export interface CLIFlags {
open?: boolean;
experimentalAssets?: boolean;
experimentalMiddleware?: boolean;
experimentalRedirects?: boolean;
}

export interface BuildConfig {
Expand Down Expand Up @@ -452,6 +453,53 @@ export interface AstroUserConfig {
*/
cacheDir?: string;



/**
* @docs
* @name redirects (Experimental)
* @type {RedirectConfig}
* @default `{}`
matthewp marked this conversation as resolved.
Show resolved Hide resolved
* @version 2.6.0
* @description Specify a mapping of redirects where the key is the route to match
* and the value is the path to redirect to.
*
* You can redirect both static and dynamic routes, but only to the same kind of route.
* For example you cannot have a `'/article': '/blog/[...slug]'` redirect.
*
*
* ```js
* {
* redirects: {
* '/old': '/new',
* '/blog/[...slug]': '/articles/[...slug]',
* }
* }
* ```
*
*
* For statically-generated sites with no adapter installed, this will produce a client redirect using a [`<meta http-equiv="refresh">` tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta#http-equiv) and does not support status codes.
*
* When using SSR or with a static adapter in `output: static`
* mode (Netlify, Cloudflare, and Vercel), status codes are supported.
matthewp marked this conversation as resolved.
Show resolved Hide resolved
* Astro will serve redirected GET requests with a status of `301`
* and use a status of `308` for any other request method.
*
* You can customize the [redirection status code](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages) using an object in the redirect config:
*
* ```js
* {
* redirects: {
* '/other': {
* status: 302,
* destination: '/place',
* },
* }
* }
* ```
*/
redirects?: RedirectConfig;

/**
* @docs
* @name site
Expand Down Expand Up @@ -733,6 +781,29 @@ export interface AstroUserConfig {
* ```
*/
serverEntry?: string;
/**
* @docs
* @name build.redirects
* @type {boolean}
* @default `true`
matthewp marked this conversation as resolved.
Show resolved Hide resolved
* @version 2.6.0
* @description
* Specifies whether redirects will be output to HTML during the build.
* This option only applies to `output: 'static'` mode; in SSR redirects
* are treated the same as all responses.
*
* This option is mostly meant to be used by adapters that have special
* configuration files for redirects and do not need/want HTML based redirects.
*
* ```js
* {
* build: {
* redirects: false
* }
* }
* ```
*/
redirects?: boolean;
};

/**
Expand Down Expand Up @@ -1179,6 +1250,27 @@ export interface AstroUserConfig {
* ```
*/
hybridOutput?: boolean;

/**
* @docs
* @name experimental.redirects
* @type {boolean}
* @default `false`
* @version 2.6.0
* @description
* Enable experimental support for redirect configuration. With this enabled
* you can set redirects via the top-level `redirects` property. To enable
* this feature, set `experimental.redirects` to `true`.
*
* ```js
* {
* experimental: {
* redirects: true,
* },
* }
* ```
*/
redirects?: boolean;
};

// Legacy options to be removed
Expand Down Expand Up @@ -1578,6 +1670,8 @@ export interface AstroAdapter {

type Body = string;

export type ValidRedirectStatus = 300 | 301 | 302 | 303 | 304 | 307 | 308;

// Shared types between `Astro` global and API context object
interface AstroSharedContext<Props extends Record<string, any> = Record<string, any>> {
/**
Expand Down Expand Up @@ -1607,7 +1701,7 @@ interface AstroSharedContext<Props extends Record<string, any> = Record<string,
/**
* Redirect to another page (**SSR Only**).
*/
redirect(path: string, status?: 301 | 302 | 303 | 307 | 308): Response;
redirect(path: string, status?: ValidRedirectStatus): Response;

/**
* Object accessed via Astro middleware
Expand Down Expand Up @@ -1805,7 +1899,7 @@ export type MiddlewareNext<R> = () => Promise<R>;
export type MiddlewareHandler<R> = (
context: APIContext,
next: MiddlewareNext<R>
) => Promise<R> | Promise<void> | void;
) => Promise<R> | R | Promise<void> | void;

export type MiddlewareResponseHandler = MiddlewareHandler<Response>;
export type MiddlewareEndpointHandler = MiddlewareHandler<Response | EndpointOutput>;
Expand All @@ -1822,14 +1916,19 @@ export interface AstroPluginOptions {
logging: LogOptions;
}

export type RouteType = 'page' | 'endpoint';
export type RouteType = 'page' | 'endpoint' | 'redirect';

export interface RoutePart {
content: string;
dynamic: boolean;
spread: boolean;
}

type RedirectConfig = string | {
status: ValidRedirectStatus;
destination: string;
}

export interface RouteData {
route: string;
component: string;
Expand All @@ -1842,6 +1941,12 @@ export interface RouteData {
segments: RoutePart[][];
type: RouteType;
prerender: boolean;
redirect?: RedirectConfig;
redirectRoute?: RouteData;
}

export type RedirectRouteData = RouteData & {
redirect: string;
}

export type SerializedRouteData = Omit<RouteData, 'generate' | 'pattern'> & {
Expand Down
28 changes: 20 additions & 8 deletions packages/astro/src/core/app/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
createStylesheetElementSet,
} from '../render/ssr-element.js';
import { matchRoute } from '../routing/match.js';
import { RedirectComponentInstance } from '../redirects/index.js';
export { deserializeManifest } from './common.js';

const clientLocalsSymbol = Symbol.for('astro.locals');
Expand Down Expand Up @@ -137,22 +138,20 @@ export class App {
defaultStatus = 404;
}

let page = await this.#manifest.pageMap.get(routeData.component)!();
let mod = await page.page();
let mod = await this.#getModuleForRoute(routeData);

if (routeData.type === 'page') {
if (routeData.type === 'page' || routeData.type === 'redirect') {
let response = await this.#renderPage(request, routeData, mod, defaultStatus);

// If there was a known error code, try sending the according page (e.g. 404.astro / 500.astro).
if (response.status === 500 || response.status === 404) {
const errorPageData = matchRoute('/' + response.status, this.#manifestData);
if (errorPageData && errorPageData.route !== routeData.route) {
page = await this.#manifest.pageMap.get(errorPageData.component)!();
mod = await page.page();
const errorRouteData = matchRoute('/' + response.status, this.#manifestData);
if (errorRouteData && errorRouteData.route !== routeData.route) {
mod = await this.#getModuleForRoute(errorRouteData);
try {
let errorResponse = await this.#renderPage(
request,
errorPageData,
errorRouteData,
mod,
response.status
);
Expand All @@ -172,6 +171,19 @@ export class App {
return getSetCookiesFromResponse(response);
}

async #getModuleForRoute(route: RouteData): Promise<ComponentInstance> {
if(route.type === 'redirect') {
return RedirectComponentInstance;
} else {
const importComponentInstance = this.#manifest.pageMap.get(route.component);
if(!importComponentInstance) {
throw new Error(`Unexpectedly unable to find a component instance for route ${route.route}`);
}
const built = await importComponentInstance();
return built.page();
}
}

async #renderPage(
request: Request,
routeData: RouteData,
Expand Down
2 changes: 2 additions & 0 deletions packages/astro/src/core/build/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function getOutFolder(
case 'endpoint':
return new URL('.' + appendForwardSlash(npath.dirname(pathname)), outRoot);
case 'page':
case 'redirect':
switch (astroConfig.build.format) {
case 'directory': {
if (STATUS_CODE_PAGES.has(pathname)) {
Expand All @@ -51,6 +52,7 @@ export function getOutFile(
case 'endpoint':
return new URL(npath.basename(pathname), outFolder);
case 'page':
case 'redirect':
switch (astroConfig.build.format) {
case 'directory': {
if (STATUS_CODE_PAGES.has(pathname)) {
Expand Down
Loading