Skip to content

Commit

Permalink
feat: stabilise the rewrite APIs (#11542)
Browse files Browse the repository at this point in the history
* feat: stabilise the rewrite APIs

* chore: rewrite changeset

* oops

* Apply suggestions from code review

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

* chore: fix linting

* fix: update exemple

* code formatting

* edit changeset code examples

---------

Co-authored-by: Sarah Rainsberger <[email protected]>
Co-authored-by: Erika <[email protected]>
  • Loading branch information
3 people authored Jul 31, 2024
1 parent a62345f commit 45ad326
Show file tree
Hide file tree
Showing 20 changed files with 85 additions and 155 deletions.
63 changes: 63 additions & 0 deletions .changeset/thick-cats-compete.md
Original file line number Diff line number Diff line change
@@ -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.


56 changes: 0 additions & 56 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/container/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 0 additions & 2 deletions packages/astro/src/core/app/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/core/build/plugins/plugin-manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Expand Down
3 changes: 1 addition & 2 deletions packages/astro/src/core/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'>;

Expand Down Expand Up @@ -87,7 +88,6 @@ export const ASTRO_CONFIG_DEFAULTS = {
contentCollectionCache: false,
clientPrerender: false,
globalRoutePriority: false,
rewriting: false,
serverIslands: false,
env: {
validateSecrets: false,
Expand Down Expand Up @@ -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(),
Expand Down
19 changes: 2 additions & 17 deletions packages/astro/src/core/middleware/callMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
};
Expand Down
51 changes: 12 additions & 39 deletions packages/astro/src/core/render-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
Expand All @@ -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,
Expand Down
1 change: 0 additions & 1 deletion packages/astro/src/vite-plugin-astro-server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
3 changes: 0 additions & 3 deletions packages/astro/test/actions.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ describe('Astro Actions', () => {
fixture = await loadFixture({
root: './fixtures/actions/',
adapter: testAdapter(),
experimental: {
rewriting: true,
},
});
});

Expand Down
3 changes: 0 additions & 3 deletions packages/astro/test/astro-cookies.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ describe('Astro.cookies', () => {
root: './fixtures/astro-cookies/',
output: 'server',
adapter: testAdapter(),
experimental: {
rewriting: true,
},
});
});

Expand Down
1 change: 0 additions & 1 deletion packages/astro/test/fixtures/actions/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { defineConfig } from 'astro/config';
export default defineConfig({
output: 'server',
experimental: {
rewriting: true,
actions: true,
},
});
5 changes: 1 addition & 4 deletions packages/astro/test/fixtures/reroute/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -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"
});
Original file line number Diff line number Diff line change
@@ -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"
});
Original file line number Diff line number Diff line change
@@ -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"],
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
});
Original file line number Diff line number Diff line change
@@ -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"
});
5 changes: 1 addition & 4 deletions packages/astro/test/fixtures/rewrite-server/astro.config.mjs
Original file line number Diff line number Diff line change
@@ -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"
});
Loading

0 comments on commit 45ad326

Please sign in to comment.