0
Fork 0
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:
Emanuele Stoppa 2024-07-31 11:51:13 +01:00 committed by GitHub
parent a62345fd18
commit 45ad326932
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 85 additions and 155 deletions

View 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.

View file

@ -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

View file

@ -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,

View file

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

View file

@ -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,
};

View file

@ -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 &&

View file

@ -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(),

View file

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

View file

@ -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,

View file

@ -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();

View file

@ -10,9 +10,6 @@ describe('Astro Actions', () => {
fixture = await loadFixture({
root: './fixtures/actions/',
adapter: testAdapter(),
experimental: {
rewriting: true,
},
});
});

View file

@ -13,9 +13,6 @@ describe('Astro.cookies', () => {
root: './fixtures/astro-cookies/',
output: 'server',
adapter: testAdapter(),
experimental: {
rewriting: true,
},
});
});

View file

@ -4,7 +4,6 @@ import { defineConfig } from 'astro/config';
export default defineConfig({
output: 'server',
experimental: {
rewriting: true,
actions: true,
},
});

View file

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

View file

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

View file

@ -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"],

View file

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

View file

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

View file

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

View file

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