diff --git a/.changeset/chilled-ducks-grin.md b/.changeset/chilled-ducks-grin.md new file mode 100644 index 000000000000..b63b1f29c29d --- /dev/null +++ b/.changeset/chilled-ducks-grin.md @@ -0,0 +1,5 @@ +--- +'astro': major +--- + +Removed automatic flattening of `getStaticPaths` result. `.flatMap` and `.flat` should now be used to ensure that you're returning a flat array. diff --git a/packages/astro/src/@types/astro.ts b/packages/astro/src/@types/astro.ts index 59e295ebac66..1a892ab0ed9b 100644 --- a/packages/astro/src/@types/astro.ts +++ b/packages/astro/src/@types/astro.ts @@ -1524,10 +1524,7 @@ export type GetStaticPathsResultKeyed = GetStaticPathsResult & { */ export type GetStaticPaths = ( options: GetStaticPathsOptions -) => - | Promise - | GetStaticPathsResult - | GetStaticPathsResult[]; +) => Promise | GetStaticPathsResult; /** * Infers the shape of the `params` property returned by `getStaticPaths()`. diff --git a/packages/astro/src/core/build/generate.ts b/packages/astro/src/core/build/generate.ts index 708295296399..fbc88d0e48d5 100644 --- a/packages/astro/src/core/build/generate.ts +++ b/packages/astro/src/core/build/generate.ts @@ -300,7 +300,6 @@ async function getPathsForRoute( mod, route, routeCache: opts.routeCache, - isValidate: false, logging: opts.logging, ssr: isServerLikeOutput(opts.settings.config), }).catch((err) => { diff --git a/packages/astro/src/core/errors/errors-data.ts b/packages/astro/src/core/errors/errors-data.ts index 52fdbed6c5e7..dd723d5a4d19 100644 --- a/packages/astro/src/core/errors/errors-data.ts +++ b/packages/astro/src/core/errors/errors-data.ts @@ -231,6 +231,28 @@ but ${plural ? 'none were' : 'it was not'} able to server-side render \`${compon `Invalid params given to \`getStaticPaths\` path. Expected an \`object\`, got \`${paramType}\``, hint: 'See https://docs.astro.build/en/reference/api-reference/#getstaticpaths for more information on getStaticPaths.', }, + /** + * @docs + * @see + * - [`getStaticPaths()`](https://docs.astro.build/en/reference/api-reference/#getstaticpaths) + * @description + * `getStaticPaths`'s return value must be an array of objects. In most cases, this error happens because an array of array was returned. Using [`.flatMap()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap) or a [`.flat()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) call may be useful. + * + * ```ts title="pages/blog/[id].astro" + * export async function getStaticPaths() { + * return [ // <-- Array + * { params: { slug: "blog" } }, // <-- Object + * { params: { slug: "about" } } + * ]; + *} + * ``` + */ + InvalidGetStaticPathsEntry: { + title: "Invalid entry inside getStaticPath's return value", + message: (entryType) => + `Invalid entry returned by getStaticPaths. Expected an object, got \`${entryType}\``, + hint: "If you're using a `.map` call, you might be looking for `.flatMap()` instead. See https://docs.astro.build/en/reference/api-reference/#getstaticpaths for more information on getStaticPaths.", + }, /** * @docs * @see @@ -254,6 +276,7 @@ but ${plural ? 'none were' : 'it was not'} able to server-side render \`${compon `Invalid type returned by \`getStaticPaths\`. Expected an \`array\`, got \`${returnType}\``, hint: 'See https://docs.astro.build/en/reference/api-reference/#getstaticpaths for more information on getStaticPaths.', }, + /** * @docs * @see diff --git a/packages/astro/src/core/render/params-and-props.ts b/packages/astro/src/core/render/params-and-props.ts index a5e4fa222cb6..fc08c495e71f 100644 --- a/packages/astro/src/core/render/params-and-props.ts +++ b/packages/astro/src/core/render/params-and-props.ts @@ -33,7 +33,6 @@ export async function getParamsAndProps(opts: GetParamsAndPropsOptions): Promise mod, route, routeCache, - isValidate: true, logging, ssr, }); diff --git a/packages/astro/src/core/render/route-cache.ts b/packages/astro/src/core/render/route-cache.ts index 7ad247ef8d81..804f09183847 100644 --- a/packages/astro/src/core/render/route-cache.ts +++ b/packages/astro/src/core/render/route-cache.ts @@ -18,7 +18,6 @@ interface CallGetStaticPathsOptions { mod: ComponentInstance; route: RouteData; routeCache: RouteCache; - isValidate: boolean; logging: LogOptions; ssr: boolean; } @@ -27,7 +26,6 @@ export async function callGetStaticPaths({ mod, route, routeCache, - isValidate, logging, ssr, }: CallGetStaticPathsOptions): Promise { @@ -58,14 +56,7 @@ export async function callGetStaticPaths({ }, }); - // Flatten the array before validating the content, otherwise users using `.map` will run into errors - if (Array.isArray(staticPaths)) { - staticPaths = staticPaths.flat(); - } - - if (isValidate) { - validateGetStaticPathsResult(staticPaths, logging, route); - } + validateGetStaticPathsResult(staticPaths, logging, route); const keyedStaticPaths = staticPaths as GetStaticPathsResultKeyed; keyedStaticPaths.keyed = new Map(); diff --git a/packages/astro/src/core/routing/validation.ts b/packages/astro/src/core/routing/validation.ts index 9a562c044aa3..b5c29b16e12e 100644 --- a/packages/astro/src/core/routing/validation.ts +++ b/packages/astro/src/core/routing/validation.ts @@ -54,6 +54,15 @@ export function validateGetStaticPathsResult( } result.forEach((pathObject) => { + if ((typeof pathObject === 'object' && Array.isArray(pathObject)) || pathObject === null) { + throw new AstroError({ + ...AstroErrorData.InvalidGetStaticPathsEntry, + message: AstroErrorData.InvalidGetStaticPathsEntry.message( + Array.isArray(pathObject) ? 'array' : typeof pathObject + ), + }); + } + if ( pathObject.params === undefined || pathObject.params === null || @@ -67,16 +76,6 @@ export function validateGetStaticPathsResult( }); } - if (typeof pathObject.params !== 'object') { - throw new AstroError({ - ...AstroErrorData.InvalidGetStaticPathParam, - message: AstroErrorData.InvalidGetStaticPathParam.message(typeof pathObject.params), - location: { - file: route.component, - }, - }); - } - // TODO: Replace those with errors? They technically don't crash the build, but users might miss the warning. - erika, 2022-11-07 for (const [key, val] of Object.entries(pathObject.params)) { if (!(typeof val === 'undefined' || typeof val === 'string' || typeof val === 'number')) { diff --git a/packages/astro/test/astro-get-static-paths.test.js b/packages/astro/test/astro-get-static-paths.test.js index 784ff17186b9..66aa5b94d042 100644 --- a/packages/astro/test/astro-get-static-paths.test.js +++ b/packages/astro/test/astro-get-static-paths.test.js @@ -1,6 +1,6 @@ import { expect } from 'chai'; -import { loadFixture } from './test-utils.js'; import * as cheerio from 'cheerio'; +import { loadFixture } from './test-utils.js'; describe('getStaticPaths - build calls', () => { /** @type {import('./test-utils').Fixture} */ @@ -92,11 +92,6 @@ describe('getStaticPaths - dev calls', () => { }); describe('route params type validation', () => { - it('resolves 200 on nested array parameters', async () => { - const res = await fixture.fetch('/nested-arrays/slug1'); - expect(res.status).to.equal(200); - }); - it('resolves 200 on matching static path - string params', async () => { // route provided with { params: { year: "2022", slug: "post-2" }} const res = await fixture.fetch('/blog/2022/post-1'); diff --git a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro b/packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro deleted file mode 100644 index 9bd7b4f41aa9..000000000000 --- a/packages/astro/test/fixtures/astro-get-static-paths/src/pages/nested-arrays/[slug].astro +++ /dev/null @@ -1,8 +0,0 @@ ---- - export function getStaticPaths() { - return [ - [ { params: {slug: "slug1"} } ], - [ { params: {slug: "slug2"} } ], - ] - } ---- diff --git a/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro b/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro index c4cc39739021..33df98cf598c 100644 --- a/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro +++ b/packages/astro/test/fixtures/astro-pagination/src/pages/posts/[slug]/[page].astro @@ -1,7 +1,7 @@ --- export async function getStaticPaths({paginate}) { const allPosts = await Astro.glob('../../post/*.md'); - return ['red', 'blue'].map((filter) => { + return ['red', 'blue'].flatMap((filter) => { const filteredPosts = allPosts.filter((post) => post.frontmatter.tag === filter); return paginate(filteredPosts, { params: { slug: filter }, diff --git a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro b/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro deleted file mode 100644 index 25d1bfff4e4d..000000000000 --- a/packages/astro/test/fixtures/ssr-prerender-get-static-paths/src/pages/nested-arrays/[slug].astro +++ /dev/null @@ -1,10 +0,0 @@ ---- - export function getStaticPaths() { - return [ - [ { params: {slug: "slug1"} } ], - [ { params: {slug: "slug2"} } ], - ] - } - - export const prerender = true; ---- diff --git a/packages/astro/test/ssr-prerender-get-static-paths.test.js b/packages/astro/test/ssr-prerender-get-static-paths.test.js index 391e7485dd85..3fe2950cb83d 100644 --- a/packages/astro/test/ssr-prerender-get-static-paths.test.js +++ b/packages/astro/test/ssr-prerender-get-static-paths.test.js @@ -1,7 +1,7 @@ import { expect } from 'chai'; -import { loadFixture } from './test-utils.js'; import * as cheerio from 'cheerio'; import testAdapter from './test-adapter.js'; +import { loadFixture } from './test-utils.js'; describe('Prerender', () => { /** @type {import('./test-utils').Fixture} */ @@ -102,11 +102,6 @@ describe('Prerender', () => { }); describe('route params type validation', () => { - it('resolves 200 on nested array parameters', async () => { - const res = await fixture.fetch('/blog/nested-arrays/slug1'); - expect(res.status).to.equal(200); - }); - it('resolves 200 on matching static path - string params', async () => { // route provided with { params: { year: "2022", slug: "post-2" }} const res = await fixture.fetch('/blog/blog/2022/post-1'); @@ -234,11 +229,6 @@ describe('Prerender', () => { }); describe('route params type validation', () => { - it('resolves 200 on nested array parameters', async () => { - const res = await fixture.fetch('/blog/nested-arrays/slug1'); - expect(res.status).to.equal(200); - }); - it('resolves 200 on matching static path - string params', async () => { // route provided with { params: { year: "2022", slug: "post-2" }} const res = await fixture.fetch('/blog/blog/2022/post-1');