feat: remove automatic flattening of getStaticPaths result (#7845)

* feat: remove automatic flattening of `getStaticPaths` result

* chore: changeset
This commit is contained in:
Erika 2023-07-27 20:49:57 +02:00 committed by Emanuele Stoppa
parent 3dc1ca2fac
commit 76ddef19cc
12 changed files with 42 additions and 62 deletions

View file

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

View file

@ -1525,10 +1525,7 @@ export type GetStaticPathsResultKeyed = GetStaticPathsResult & {
*/
export type GetStaticPaths = (
options: GetStaticPathsOptions
) =>
| Promise<GetStaticPathsResult | GetStaticPathsResult[]>
| GetStaticPathsResult
| GetStaticPathsResult[];
) => Promise<GetStaticPathsResult> | GetStaticPathsResult;
/**
* Infers the shape of the `params` property returned by `getStaticPaths()`.

View file

@ -300,7 +300,6 @@ async function getPathsForRoute(
mod,
route,
routeCache: opts.routeCache,
isValidate: false,
logging: opts.logging,
ssr: isServerLikeOutput(opts.settings.config),
}).catch((err) => {

View file

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

View file

@ -33,7 +33,6 @@ export async function getParamsAndProps(opts: GetParamsAndPropsOptions): Promise
mod,
route,
routeCache,
isValidate: true,
logging,
ssr,
});

View file

@ -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<GetStaticPathsResultKeyed> {
@ -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<string, GetStaticPathsItem>();

View file

@ -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')) {

View file

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

View file

@ -1,8 +0,0 @@
---
export function getStaticPaths() {
return [
[ { params: {slug: "slug1"} } ],
[ { params: {slug: "slug2"} } ],
]
}
---

View file

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

View file

@ -1,10 +0,0 @@
---
export function getStaticPaths() {
return [
[ { params: {slug: "slug1"} } ],
[ { params: {slug: "slug2"} } ],
]
}
export const prerender = true;
---

View file

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