summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/proud-games-repair.md7
-rw-r--r--packages/astro/src/core/errors/errors-data.ts17
-rw-r--r--packages/astro/src/core/middleware/sequence.ts18
-rw-r--r--packages/astro/src/core/render-context.ts32
-rw-r--r--packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro4
-rw-r--r--packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro3
-rw-r--r--packages/astro/test/rewrite.test.js7
7 files changed, 88 insertions, 0 deletions
diff --git a/.changeset/proud-games-repair.md b/.changeset/proud-games-repair.md
new file mode 100644
index 000000000..8c342e2a3
--- /dev/null
+++ b/.changeset/proud-games-repair.md
@@ -0,0 +1,7 @@
+---
+'astro': patch
+---
+
+Adds an error when `Astro.rewrite()` is used to rewrite an on-demand route with a static route when using the `"server"` output.
+
+This is a forbidden rewrite because Astro can't retrieve the emitted static route at runtime. This route is served by the hosting platform, and not Astro itself.
diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts
index 6b3c7c141..7c5479a66 100644
--- a/packages/astro/src/core/errors/errors-data.ts
+++ b/packages/astro/src/core/errors/errors-data.ts
@@ -1263,6 +1263,23 @@ export const RewriteWithBodyUsed = {
/**
* @docs
* @description
+ * `Astro.rewrite()` can't be used to rewrite an on-demand route with a static route when using the `"server"` output.
+ *
+ */
+export const ForbiddenRewrite = {
+ name: 'ForbiddenRewrite',
+ title: 'Forbidden rewrite to a static route.',
+ message: (from: string, to: string, component: string) =>
+ `You tried to rewrite the on-demand route '${from}' with the static route '${to}', when using the 'server' output. \n\nThe static route '${to}' is rendered by the component
+'${component}', which is marked as prerendered. This is a forbidden operation because during the build the component '${component}' is compiled to an
+HTML file, which can't be retrieved at runtime by Astro.`,
+ hint: (component: string) =>
+ `Add \`export const prerender = false\` to the component '${component}', or use a Astro.redirect().`,
+} satisfies ErrorData;
+
+/**
+ * @docs
+ * @description
* An unknown error occurred while reading or writing files to disk. It can be caused by many things, eg. missing permissions or a file not existing we attempt to read.
*/
export const UnknownFilesystemError = {
diff --git a/packages/astro/src/core/middleware/sequence.ts b/packages/astro/src/core/middleware/sequence.ts
index 1fbba7c66..99d506695 100644
--- a/packages/astro/src/core/middleware/sequence.ts
+++ b/packages/astro/src/core/middleware/sequence.ts
@@ -1,6 +1,8 @@
import type { MiddlewareHandler, RewritePayload } from '../../types/public/common.js';
import type { APIContext } from '../../types/public/context.js';
import { AstroCookies } from '../cookies/cookies.js';
+import { ForbiddenRewrite } from '../errors/errors-data.js';
+import { AstroError } from '../errors/index.js';
import { apiContextRoutesSymbol } from '../render-context.js';
import { type Pipeline, getParams } from '../render/index.js';
import { defineMiddleware } from './index.js';
@@ -49,6 +51,22 @@ export function sequence(...handlers: MiddlewareHandler[]): MiddlewareHandler {
payload,
handleContext.request,
);
+
+ // This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG).
+ // This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file,
+ // so Astro can't retrieve it from the emitted manifest.
+ if (
+ pipeline.serverLike === true &&
+ handleContext.isPrerendered === false &&
+ routeData.prerender === true
+ ) {
+ throw new AstroError({
+ ...ForbiddenRewrite,
+ message: ForbiddenRewrite.message(pathname, pathname, routeData.component),
+ hint: ForbiddenRewrite.hint(routeData.component),
+ });
+ }
+
carriedPayload = payload;
handleContext.request = newRequest;
handleContext.url = new URL(newRequest.url);
diff --git a/packages/astro/src/core/render-context.ts b/packages/astro/src/core/render-context.ts
index 49f174c33..691613412 100644
--- a/packages/astro/src/core/render-context.ts
+++ b/packages/astro/src/core/render-context.ts
@@ -23,6 +23,7 @@ import {
} from './constants.js';
import { AstroCookies, attachCookiesToResponse } from './cookies/index.js';
import { getCookiesFromResponse } from './cookies/response.js';
+import { ForbiddenRewrite } from './errors/errors-data.js';
import { AstroError, AstroErrorData } from './errors/index.js';
import { callMiddleware } from './middleware/callMiddleware.js';
import { sequence } from './middleware/index.js';
@@ -145,6 +146,22 @@ export class RenderContext {
pathname,
newUrl,
} = await pipeline.tryRewrite(payload, this.request);
+
+ // This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG).
+ // This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file,
+ // so Astro can't retrieve it from the emitted manifest.
+ if (
+ this.pipeline.serverLike === true &&
+ this.routeData.prerender === false &&
+ routeData.prerender === true
+ ) {
+ throw new AstroError({
+ ...ForbiddenRewrite,
+ message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component),
+ hint: ForbiddenRewrite.hint(routeData.component),
+ });
+ }
+
this.routeData = routeData;
componentInstance = newComponent;
if (payload instanceof Request) {
@@ -246,6 +263,21 @@ export class RenderContext {
reroutePayload,
this.request,
);
+ // This is a case where the user tries to rewrite from a SSR route to a prerendered route (SSG).
+ // This case isn't valid because when building for SSR, the prerendered route disappears from the server output because it becomes an HTML file,
+ // so Astro can't retrieve it from the emitted manifest.
+ if (
+ this.pipeline.serverLike === true &&
+ this.routeData.prerender === false &&
+ routeData.prerender === true
+ ) {
+ throw new AstroError({
+ ...ForbiddenRewrite,
+ message: ForbiddenRewrite.message(this.pathname, pathname, routeData.component),
+ hint: ForbiddenRewrite.hint(routeData.component),
+ });
+ }
+
this.routeData = routeData;
if (reroutePayload instanceof Request) {
this.request = reroutePayload;
diff --git a/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro b/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro
new file mode 100644
index 000000000..3c23c9539
--- /dev/null
+++ b/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/dynamic.astro
@@ -0,0 +1,4 @@
+---
+return Astro.rewrite("/forbidden/static")
+export const prerender = false
+---
diff --git a/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro b/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro
new file mode 100644
index 000000000..3a91bda71
--- /dev/null
+++ b/packages/astro/test/fixtures/rewrite-server/src/pages/forbidden/static.astro
@@ -0,0 +1,3 @@
+---
+export const prerender = true
+---
diff --git a/packages/astro/test/rewrite.test.js b/packages/astro/test/rewrite.test.js
index 273e11d07..cc7508081 100644
--- a/packages/astro/test/rewrite.test.js
+++ b/packages/astro/test/rewrite.test.js
@@ -149,6 +149,13 @@ describe('Dev rewrite, hybrid/server', () => {
assert.equal($('title').text(), 'RewriteWithBodyUsed');
});
+
+ it('should error when rewriting from a SSR route to a SSG route', async () => {
+ const html = await fixture.fetch('/forbidden/dynamic').then((res) => res.text());
+ const $ = cheerioLoad(html);
+
+ assert.match($('title').text(), /ForbiddenRewrite/);
+ });
});
describe('Build reroute', () => {