mirror of
https://github.com/withastro/astro.git
synced 2025-01-13 22:11:20 -05:00
feat: stabilise the rewrite APIs (#11542)
* feat: stabilise the rewrite APIs * chore: rewrite changeset * oops * Apply suggestions from code review Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> * chore: fix linting * fix: update exemple * code formatting * edit changeset code examples --------- Co-authored-by: Sarah Rainsberger <sarah@rainsberger.ca> Co-authored-by: Erika <3019731+Princesseuh@users.noreply.github.com>
This commit is contained in:
parent
a62345fd18
commit
45ad326932
20 changed files with 85 additions and 155 deletions
63
.changeset/thick-cats-compete.md
Normal file
63
.changeset/thick-cats-compete.md
Normal file
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
'astro': minor
|
||||
---
|
||||
|
||||
The `experimental.rewriting` feature introduced behind a flag in [v4.8.0](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md#480) is no longer experimental and is available for general use.
|
||||
|
||||
`Astro.rewrite()` and `context.rewrite()` allow you to render a different page without changing the URL in the browser. Unlike using a redirect, your visitor is kept on the original page they visited.
|
||||
|
||||
Rewrites can be useful for showing the same content at multiple paths (e.g. /products/shoes/men/ and /products/men/shoes/) without needing to maintain two identical source files.
|
||||
|
||||
Rewrites are supported in Astro pages, endpoints, and middleware.
|
||||
|
||||
Return `Astro.rewrite()` in the frontmatter of a `.astro` page component to display a different page's content, such as fallback localized content:
|
||||
|
||||
```astro
|
||||
---
|
||||
---
|
||||
// src/pages/es-cu/articles/introduction.astro
|
||||
return Astro.rewrite("/es/articles/introduction")
|
||||
---
|
||||
}
|
||||
---
|
||||
```
|
||||
|
||||
Use `context.rewrite()` in endpoints, for example to reroute to a different page:
|
||||
|
||||
```js
|
||||
// src/pages/api.js
|
||||
export function GET(context) {
|
||||
if (!context.locals.allowed) {
|
||||
return context.rewrite('/');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The middleware `next()` function now accepts a parameter with the same type as the `rewrite()` function. For example, with `next("/")`, you can call the next middleware function with a new `Request`.
|
||||
|
||||
```js
|
||||
// src/middleware.js
|
||||
export function onRequest(context, next) {
|
||||
if (!context.cookies.get('allowed')) {
|
||||
return next('/'); // new signature
|
||||
}
|
||||
return next();
|
||||
}
|
||||
```
|
||||
|
||||
If you were previously using this feature, please remove the experimental flag from your Astro config:
|
||||
|
||||
```diff
|
||||
// astro.config.mjs
|
||||
export default defineConfig({
|
||||
- experimental: {
|
||||
- rewriting: true
|
||||
- }
|
||||
})
|
||||
```
|
||||
|
||||
If you have been waiting for stabilization before using rewrites in Astro, you can now do so.
|
||||
|
||||
Please see [the routing guide in docs](https://docs.astro.build/en/guides/routing/#rewrites) for more about using this feature.
|
||||
|
||||
|
|
@ -1962,62 +1962,6 @@ export interface AstroUserConfig {
|
|||
*/
|
||||
globalRoutePriority?: boolean;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @name experimental.rewriting
|
||||
* @type {boolean}
|
||||
* @default `false`
|
||||
* @version 4.8.0
|
||||
* @description
|
||||
*
|
||||
* Enables a routing feature for rewriting requests in Astro pages, endpoints and Astro middleware, giving you programmatic control over your routes.
|
||||
*
|
||||
* ```js
|
||||
* {
|
||||
* experimental: {
|
||||
* rewriting: true,
|
||||
* },
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Use `Astro.rewrite` in your `.astro` files to reroute to a different page:
|
||||
*
|
||||
* ```astro "rewrite"
|
||||
* ---
|
||||
* // src/pages/dashboard.astro
|
||||
* if (!Astro.props.allowed) {
|
||||
* return Astro.rewrite("/")
|
||||
* }
|
||||
* ---
|
||||
* ```
|
||||
*
|
||||
* Use `context.rewrite` in your endpoint files to reroute to a different page:
|
||||
*
|
||||
* ```js
|
||||
* // src/pages/api.js
|
||||
* export function GET(ctx) {
|
||||
* if (!ctx.locals.allowed) {
|
||||
* return ctx.rewrite("/")
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Use `next("/")` in your middleware file to reroute to a different page, and then call the next middleware function:
|
||||
*
|
||||
* ```js
|
||||
* // src/middleware.js
|
||||
* export function onRequest(ctx, next) {
|
||||
* if (!ctx.cookies.get("allowed")) {
|
||||
* return next("/") // new signature
|
||||
* }
|
||||
* return next();
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* For a complete overview, and to give feedback on this experimental API, see the [Rerouting RFC](https://github.com/withastro/roadmap/blob/feat/reroute/proposals/0047-rerouting.md).
|
||||
*/
|
||||
rewriting?: boolean;
|
||||
|
||||
/**
|
||||
* @docs
|
||||
* @name experimental.env
|
||||
|
|
|
@ -113,7 +113,6 @@ function createManifest(
|
|||
|
||||
return {
|
||||
hrefRoot: import.meta.url,
|
||||
rewritingEnabled: false,
|
||||
trailingSlash: manifest?.trailingSlash ?? ASTRO_CONFIG_DEFAULTS.trailingSlash,
|
||||
buildFormat: manifest?.buildFormat ?? ASTRO_CONFIG_DEFAULTS.build.format,
|
||||
compressHTML: manifest?.compressHTML ?? ASTRO_CONFIG_DEFAULTS.compressHTML,
|
||||
|
|
|
@ -69,8 +69,6 @@ export type SSRManifest = {
|
|||
i18n: SSRManifestI18n | undefined;
|
||||
middleware: MiddlewareHandler;
|
||||
checkOrigin: boolean;
|
||||
// TODO: remove once the experimental flag is removed
|
||||
rewritingEnabled: boolean;
|
||||
// TODO: remove experimental prefix
|
||||
experimentalEnvGetSecretEnabled: boolean;
|
||||
};
|
||||
|
|
|
@ -578,7 +578,6 @@ function createBuildManifest(
|
|||
i18n: i18nManifest,
|
||||
buildFormat: settings.config.build.format,
|
||||
middleware,
|
||||
rewritingEnabled: settings.config.experimental.rewriting,
|
||||
checkOrigin: settings.config.security?.checkOrigin ?? false,
|
||||
experimentalEnvGetSecretEnabled: false,
|
||||
};
|
||||
|
|
|
@ -279,7 +279,6 @@ function buildManifest(
|
|||
i18n: i18nManifest,
|
||||
buildFormat: settings.config.build.format,
|
||||
checkOrigin: settings.config.security?.checkOrigin ?? false,
|
||||
rewritingEnabled: settings.config.experimental.rewriting,
|
||||
serverIslandNameMap: Array.from(settings.serverIslandNameMap),
|
||||
experimentalEnvGetSecretEnabled:
|
||||
settings.config.experimental.env !== undefined &&
|
||||
|
|
|
@ -36,6 +36,7 @@ import { appendForwardSlash, prependForwardSlash, removeTrailingForwardSlash } f
|
|||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
interface ComplexifyUnionObj {}
|
||||
|
||||
type ComplexifyWithUnion<T> = T & ComplexifyUnionObj;
|
||||
type ComplexifyWithOmit<T> = Omit<T, '__nonExistent'>;
|
||||
|
||||
|
@ -87,7 +88,6 @@ export const ASTRO_CONFIG_DEFAULTS = {
|
|||
contentCollectionCache: false,
|
||||
clientPrerender: false,
|
||||
globalRoutePriority: false,
|
||||
rewriting: false,
|
||||
serverIslands: false,
|
||||
env: {
|
||||
validateSecrets: false,
|
||||
|
@ -524,7 +524,6 @@ export const AstroConfigSchema = z.object({
|
|||
.boolean()
|
||||
.optional()
|
||||
.default(ASTRO_CONFIG_DEFAULTS.experimental.globalRoutePriority),
|
||||
rewriting: z.boolean().optional().default(ASTRO_CONFIG_DEFAULTS.experimental.rewriting),
|
||||
env: z
|
||||
.object({
|
||||
schema: EnvSchema.optional(),
|
||||
|
|
|
@ -5,7 +5,6 @@ import type {
|
|||
RewritePayload,
|
||||
} from '../../@types/astro.js';
|
||||
import { AstroError, AstroErrorData } from '../errors/index.js';
|
||||
import type { Logger } from '../logger/core.js';
|
||||
|
||||
/**
|
||||
* Utility function that is in charge of calling the middleware.
|
||||
|
@ -47,27 +46,13 @@ export async function callMiddleware(
|
|||
responseFunction: (
|
||||
apiContext: APIContext,
|
||||
rewritePayload?: RewritePayload
|
||||
) => Promise<Response> | Response,
|
||||
// TODO: remove these two arguments once rerouting goes out of experimental
|
||||
enableRerouting: boolean,
|
||||
logger: Logger
|
||||
) => Promise<Response> | Response
|
||||
): Promise<Response> {
|
||||
let nextCalled = false;
|
||||
let responseFunctionPromise: Promise<Response> | Response | undefined = undefined;
|
||||
const next: MiddlewareNext = async (payload) => {
|
||||
nextCalled = true;
|
||||
|
||||
if (enableRerouting) {
|
||||
responseFunctionPromise = responseFunction(apiContext, payload);
|
||||
} else {
|
||||
if (payload) {
|
||||
logger.warn(
|
||||
'router',
|
||||
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.'
|
||||
);
|
||||
}
|
||||
responseFunctionPromise = responseFunction(apiContext);
|
||||
}
|
||||
responseFunctionPromise = responseFunction(apiContext, payload);
|
||||
// We need to pass the APIContext pass to `callMiddleware` because it can be mutated across middleware functions
|
||||
return responseFunctionPromise;
|
||||
};
|
||||
|
|
|
@ -137,24 +137,17 @@ export class RenderContext {
|
|||
}
|
||||
const lastNext = async (ctx: APIContext, payload?: RewritePayload) => {
|
||||
if (payload) {
|
||||
if (this.pipeline.manifest.rewritingEnabled) {
|
||||
pipeline.logger.debug('router', 'Called rewriting to:', payload);
|
||||
// we intentionally let the error bubble up
|
||||
const [routeData, component] = await pipeline.tryRewrite(
|
||||
payload,
|
||||
this.request,
|
||||
this.originalRoute
|
||||
);
|
||||
this.routeData = routeData;
|
||||
componentInstance = component;
|
||||
this.isRewriting = true;
|
||||
this.status = 200;
|
||||
} else {
|
||||
this.pipeline.logger.error(
|
||||
'router',
|
||||
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.'
|
||||
);
|
||||
}
|
||||
pipeline.logger.debug('router', 'Called rewriting to:', payload);
|
||||
// we intentionally let the error bubble up
|
||||
const [routeData, component] = await pipeline.tryRewrite(
|
||||
payload,
|
||||
this.request,
|
||||
this.originalRoute
|
||||
);
|
||||
this.routeData = routeData;
|
||||
componentInstance = component;
|
||||
this.isRewriting = true;
|
||||
this.status = 200;
|
||||
}
|
||||
let response: Response;
|
||||
|
||||
|
@ -207,13 +200,7 @@ export class RenderContext {
|
|||
return response;
|
||||
};
|
||||
|
||||
const response = await callMiddleware(
|
||||
middleware,
|
||||
apiContext,
|
||||
lastNext,
|
||||
this.pipeline.manifest.rewritingEnabled,
|
||||
this.pipeline.logger
|
||||
);
|
||||
const response = await callMiddleware(middleware, apiContext, lastNext);
|
||||
if (response.headers.get(ROUTE_TYPE_HEADER)) {
|
||||
response.headers.delete(ROUTE_TYPE_HEADER);
|
||||
}
|
||||
|
@ -234,20 +221,6 @@ export class RenderContext {
|
|||
|
||||
async #executeRewrite(reroutePayload: RewritePayload) {
|
||||
this.pipeline.logger.debug('router', 'Calling rewrite: ', reroutePayload);
|
||||
if (!this.pipeline.manifest.rewritingEnabled) {
|
||||
this.pipeline.logger.error(
|
||||
'router',
|
||||
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.'
|
||||
);
|
||||
return new Response(
|
||||
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.',
|
||||
{
|
||||
status: 500,
|
||||
statusText:
|
||||
'The rewrite API is experimental. To use this feature, add the `rewriting` flag to the `experimental` object in your Astro config.',
|
||||
}
|
||||
);
|
||||
}
|
||||
const [routeData, component, newURL] = await this.pipeline.tryRewrite(
|
||||
reroutePayload,
|
||||
this.request,
|
||||
|
|
|
@ -147,7 +147,6 @@ export function createDevelopmentManifest(settings: AstroSettings): SSRManifest
|
|||
inlinedScripts: new Map(),
|
||||
i18n: i18nManifest,
|
||||
checkOrigin: settings.config.security?.checkOrigin ?? false,
|
||||
rewritingEnabled: settings.config.experimental.rewriting,
|
||||
experimentalEnvGetSecretEnabled: false,
|
||||
middleware(_, next) {
|
||||
return next();
|
||||
|
|
|
@ -10,9 +10,6 @@ describe('Astro Actions', () => {
|
|||
fixture = await loadFixture({
|
||||
root: './fixtures/actions/',
|
||||
adapter: testAdapter(),
|
||||
experimental: {
|
||||
rewriting: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -13,9 +13,6 @@ describe('Astro.cookies', () => {
|
|||
root: './fixtures/astro-cookies/',
|
||||
output: 'server',
|
||||
adapter: testAdapter(),
|
||||
experimental: {
|
||||
rewriting: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import { defineConfig } from 'astro/config';
|
|||
export default defineConfig({
|
||||
output: 'server',
|
||||
experimental: {
|
||||
rewriting: true,
|
||||
actions: true,
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import {defineConfig} from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
experimental: {
|
||||
rewriting: true
|
||||
},
|
||||
site: "https://example.com"
|
||||
});
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import {defineConfig} from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
experimental: {
|
||||
rewriting: true
|
||||
},
|
||||
site: "https://example.com"
|
||||
});
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import {defineConfig} from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
experimental: {
|
||||
rewriting: true
|
||||
},
|
||||
i18n: {
|
||||
routing: "manual",
|
||||
locales: ["en", "es"],
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import {defineConfig} from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
experimental: {
|
||||
rewriting: true
|
||||
},
|
||||
site: "https://example.com"
|
||||
});
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import {defineConfig} from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
experimental: {
|
||||
rewriting: true
|
||||
},
|
||||
site: "https://example.com"
|
||||
});
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import {defineConfig} from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
output: "server",
|
||||
experimental: {
|
||||
rewriting: true
|
||||
},
|
||||
site: "https://example.com"
|
||||
});
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import {defineConfig} from 'astro/config';
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
trailingSlash: "never",
|
||||
experimental: {
|
||||
rewriting: true
|
||||
},
|
||||
base: "base",
|
||||
site: "https://example.com"
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue