Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/smooth-kids-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': major
---

Removes the `experimental.failOnPrerenderConflict` flag and replaces it with a new configuration option `prerenderConflictBehavior` - ([v6 upgrade guidance](https://deploy-preview-12322--astro-docs-2.netlify.app/en/guides/upgrade-to/v6/#experimental-flags))
21 changes: 21 additions & 0 deletions .changeset/thin-hands-find.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'astro': minor
---

Adds an option `prerenderConflictBehavior` to configure the behavior of conflicting prerendered routes

By default, Astro warns you during the build about any conflicts between multiple dynamic routes that can result in the same output path. For example `/blog/[slug]` and `/blog/[...all]` both could try to prerender the `/blog/post-1` path. In such cases, Astro renders only the [highest priority route](https://docs.astro.build/en/guides/routing/#route-priority-order) for the conflicting path. This allows your site to build successfully, although you may discover that some pages are rendered by unexpected routes.

With the new `prerenderConflictBehavior` configuration option, you can now configure this further:

- `prerenderConflictBehavior: 'error'` fails the build
- `prerenderConflictBehavior: 'warn'` (default) logs a warning and the highest-priority route wins
- `prerenderConflictBehavior: 'ignore'` silently picks the highest-priority route when conflicts occur

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

export default defineConfig({
+ prerenderConflictBehavior: 'error',
});
```
4 changes: 2 additions & 2 deletions packages/astro/src/core/build/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ async function getPathsForRoute(

// Current route is lower-priority than matchedRoute.
// Path will be skipped due to collision.
if (config.experimental.failOnPrerenderConflict) {
if (config.prerenderConflictBehavior === 'error') {
throw new AstroError({
...AstroErrorData.PrerenderRouteConflict,
message: AstroErrorData.PrerenderRouteConflict.message(
Expand All @@ -422,7 +422,7 @@ async function getPathsForRoute(
),
hint: AstroErrorData.PrerenderRouteConflict.hint(matchedRoute.route, route.route),
});
} else {
} else if (config.prerenderConflictBehavior === 'warn') {
const msg = AstroErrorData.PrerenderRouteConflict.message(
matchedRoute.route,
route.route,
Expand Down
10 changes: 5 additions & 5 deletions packages/astro/src/core/config/schemas/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,12 @@ export const ASTRO_CONFIG_DEFAULTS = {
validateSecrets: false,
},
session: undefined,
prerenderConflictBehavior: 'warn',
experimental: {
clientPrerender: false,
contentIntellisense: false,
csp: false,
chromeDevtoolsWorkspace: false,
failOnPrerenderConflict: false,
svgo: false,
},
} satisfies AstroUserConfig & { server: { open: boolean } };
Expand Down Expand Up @@ -463,6 +463,10 @@ export const AstroConfigSchema = z.object({
ttl: z.number().optional(),
})
.optional(),
prerenderConflictBehavior: z
.enum(['error', 'warn', 'ignore'])
.optional()
.default(ASTRO_CONFIG_DEFAULTS.prerenderConflictBehavior),
experimental: z
.object({
clientPrerender: z
Expand Down Expand Up @@ -501,10 +505,6 @@ export const AstroConfigSchema = z.object({
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.chromeDevtoolsWorkspace),
failOnPrerenderConflict: z
.boolean()
.optional()
.default(ASTRO_CONFIG_DEFAULTS.experimental.failOnPrerenderConflict),
svgo: z
.union([z.boolean(), z.custom<SvgoConfig>((value) => value && typeof value === 'object')])
.optional()
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/src/core/errors/errors-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,8 +616,8 @@ export const PrerenderDynamicEndpointPathCollide = {
/**
* @docs
* @see
* - [`getStaticPaths()`](https://docs.astro.build/en/reference/routing-reference/#getstaticpaths)
* - [`params`](https://docs.astro.build/en/reference/api-reference/#params)
* - [Route Priority Order](https://docs.astro.build/en/guides/routing/#route-priority-order)
* - [`prerenderConflictBehavior`](https://docs.astro.build/en/reference/configuration-reference/#prerenderconflictbehavior)
* @description
* Two prerendered routes generate the same path, resulting in a collision.
* A static path can only be generated by one route.
Expand Down
26 changes: 14 additions & 12 deletions packages/astro/src/types/public/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,20 @@ export interface AstroUserConfig<
*/
scopedStyleStrategy?: 'where' | 'class' | 'attribute';

/**
*
* @name prerenderConflictBehavior
* @type {'error' | 'warn' | 'ignore'}
* @default `'warn'`
* @version 6.0
* @description
* Determines the default behavior when two routes generate the same prerendered URL:
* - `error`: fail the build and display an error, forcing you to resolve the conflict
* - `warn` (default): log a warning when conflicts occur, but build using the highest-priority route
* - `ignore`: silently build using the highest-priority route when conflicts occur
*/
prerenderConflictBehavior?: 'error' | 'warn' | 'ignore';

/**
* @docs
* @name security
Expand Down Expand Up @@ -2085,18 +2099,6 @@ export interface AstroUserConfig<
*/
clientPrerender?: boolean;

/**
*
* @name experimental.failOnPrerenderConflict
* @type {boolean}
* @default `false`
* @version 5.x
* @description
* When two routes generate the same prerendered URL, fail the build instead of skipping one.
* If disabled (default), a warning is logged when conflicts occur and the highest-priority route wins.
*/
failOnPrerenderConflict?: boolean;

/**
*
* @name experimental.contentIntellisense
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { defineConfig } from 'astro/config';

export default defineConfig({
experimental: {
failOnPrerenderConflict: false,
},
prerenderConflictBehavior: 'warn',
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import { defineConfig } from 'astro/config';

export default defineConfig({
experimental: {
failOnPrerenderConflict: false,
},
prerenderConflictBehavior: 'warn',
});
14 changes: 7 additions & 7 deletions packages/astro/test/prerender-conflict.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { loadFixture } from './test-utils.js';

/**
* Dynamic vs dynamic duplication should warn by default and succeed.
* When experimental.failOnPrerenderConflict is true, it should fail.
* When prerenderConflictBehavior is set to 'error', it should fail.
* Static vs dynamic should also warn/fail similarly.
*/

Expand Down Expand Up @@ -43,14 +43,14 @@ describe('Prerender conflicts', () => {
);
});

it('fails when experimental.failOnPrerenderConflict = true', async () => {
it('fails when prerenderConflictBehavior is set to error', async () => {
let err;
try {
await fixture.build({ experimental: { failOnPrerenderConflict: true } });
await fixture.build({ prerenderConflictBehavior: 'error' });
} catch (e) {
err = e;
}
assert.ok(err, 'Build should fail when failOnPrerenderConflict is true');
assert.ok(err, 'Build should fail when prerenderConflictBehavior is set to error');
assert.equal(
String(err),
'PrerenderRouteConflict: Could not render `/c` from route `/[foo]` as it conflicts with higher priority route `/[bar]`.',
Expand Down Expand Up @@ -91,14 +91,14 @@ describe('Prerender conflicts', () => {
);
});

it('fails when experimental.failOnPrerenderConflict = true', async () => {
it('fails when prerenderConflictBehavior is set to error', async () => {
let err;
try {
await fixture.build({ experimental: { failOnPrerenderConflict: true } });
await fixture.build({ prerenderConflictBehavior: 'error' });
} catch (e) {
err = e;
}
assert.ok(err, 'Build should fail when failOnPrerenderConflict is true');
assert.ok(err, 'Build should fail when prerenderConflictBehavior is set to error');
assert.equal(
String(err),
'PrerenderRouteConflict: Could not render `/c` from route `/[foo]` as it conflicts with higher priority route `/c`.',
Expand Down