From 41ef76e225421548a831a8b598ebce943d895c37 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Fri, 7 Feb 2020 12:47:47 -0600 Subject: [PATCH 1/2] Add paths field for unstable_getStaticPaths --- packages/next/build/utils.ts | 37 ++++++++++++++++--- .../next-server/server/load-components.ts | 4 +- .../pages/[slug].js | 2 +- .../pages/p1/p2/predefined-ssg/[...rest].js | 10 +++-- .../pages/[...slug].js | 2 +- .../pages/[foo]/[post].js | 2 +- .../prerender/pages/blog/[post]/[comment].js | 10 +++-- .../prerender/pages/blog/[post]/index.js | 18 +++++---- .../prerender/pages/catchall/[...slug].js | 14 ++++--- .../prerender/pages/user/[user]/profile.js | 2 +- 10 files changed, 68 insertions(+), 33 deletions(-) diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index 7263af07d6514..a3cde43d939b7 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -15,6 +15,7 @@ import { recursiveReadDir } from '../lib/recursive-readdir' import { getRouteMatcher, getRouteRegex } from '../next-server/lib/router/utils' import { isDynamicRoute } from '../next-server/lib/router/utils/is-dynamic' import { findPageFile } from '../server/lib/find-page-file' +import { Unstable_getStaticPaths } from '../next-server/server/load-components' const fileGzipStats: { [k: string]: Promise } = {} const fsStatGzip = (file: string) => { @@ -559,9 +560,35 @@ export async function isPageStatic( // Get the default list of allowed params. const _validParamKeys = Object.keys(_routeMatcher(page)) - const toPrerender: Array< - { params?: { [key: string]: string } } | string - > = await mod.unstable_getStaticPaths() + const staticPathsResult = await (mod.unstable_getStaticPaths as Unstable_getStaticPaths)() + + if (!staticPathsResult || typeof staticPathsResult !== 'object') { + throw new Error( + `Invalid value returned from unstable_getStaticPaths in ${page}. Received ${typeof staticPathsResult} Expected: { paths: [] }` + ) + } + + const invalidStaticPathKeys = Object.keys(staticPathsResult).filter( + key => key !== 'paths' + ) + + if (invalidStaticPathKeys.length > 0) { + throw new Error( + `Extra keys returned from unstable_getStaticPaths (${invalidStaticPathKeys.join( + ', ' + )}) The only field allowed currently is \`paths\`` + ) + } + + const toPrerender = staticPathsResult.paths + + if (!Array.isArray(toPrerender)) { + throw new Error( + `Invalid \`paths\` value returned from unstable_getStaticProps.\n` + + `\`paths\` must be an array of strings or objects of shape { params: [key: string]: string }` + ) + } + toPrerender.forEach(entry => { // For a string-provided path, we must make sure it matches the dynamic // route. @@ -594,9 +621,7 @@ export async function isPageStatic( let builtPage = page _validParamKeys.forEach(validParamKey => { const { repeat } = _routeRegex.groups[validParamKey] - const paramValue: string | string[] = params[validParamKey] as - | string - | string[] + const paramValue = params[validParamKey] if ( (repeat && !Array.isArray(paramValue)) || (!repeat && typeof paramValue !== 'string') diff --git a/packages/next/next-server/server/load-components.ts b/packages/next/next-server/server/load-components.ts index 78995e867d3db..4f4378a24b8a1 100644 --- a/packages/next/next-server/server/load-components.ts +++ b/packages/next/next-server/server/load-components.ts @@ -32,7 +32,9 @@ type Unstable_getStaticProps = (params: { revalidate?: number | boolean }> -type Unstable_getStaticPaths = () => Promise> +export type Unstable_getStaticPaths = () => Promise<{ + paths: Array +}> type Unstable_getServerProps = (context: { params: ParsedUrlQuery | undefined diff --git a/test/integration/catches-missing-getStaticProps/pages/[slug].js b/test/integration/catches-missing-getStaticProps/pages/[slug].js index a0df9abccc1c3..9c4668013529d 100644 --- a/test/integration/catches-missing-getStaticProps/pages/[slug].js +++ b/test/integration/catches-missing-getStaticProps/pages/[slug].js @@ -1,5 +1,5 @@ export async function unstable_getStaticPaths() { - return ['/hello', '/world'] + return { paths: ['/hello', '/world'] } } export default () =>

something is missing 🤔

diff --git a/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js b/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js index 79201e8469b7d..72abcefb08521 100644 --- a/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js +++ b/test/integration/dynamic-routing/pages/p1/p2/predefined-ssg/[...rest].js @@ -7,10 +7,12 @@ export function unstable_getStaticProps({ params }) { } export function unstable_getStaticPaths() { - return [ - `/p1/p2/predefined-ssg/one-level`, - `/p1/p2/predefined-ssg/1st-level/2nd-level`, - ] + return { + paths: [ + `/p1/p2/predefined-ssg/one-level`, + `/p1/p2/predefined-ssg/1st-level/2nd-level`, + ], + } } export default All diff --git a/test/integration/prerender-invalid-catchall-params/pages/[...slug].js b/test/integration/prerender-invalid-catchall-params/pages/[...slug].js index 4f81e509688bf..8ef33a1937191 100644 --- a/test/integration/prerender-invalid-catchall-params/pages/[...slug].js +++ b/test/integration/prerender-invalid-catchall-params/pages/[...slug].js @@ -2,7 +2,7 @@ import React from 'react' // eslint-disable-next-line camelcase export async function unstable_getStaticPaths() { - return [{ params: { slug: 'hello' } }] + return { paths: [{ params: { slug: 'hello' } }] } } // eslint-disable-next-line camelcase diff --git a/test/integration/prerender-invalid-paths/pages/[foo]/[post].js b/test/integration/prerender-invalid-paths/pages/[foo]/[post].js index 7a6870ff5b93b..4e0933f554fc9 100644 --- a/test/integration/prerender-invalid-paths/pages/[foo]/[post].js +++ b/test/integration/prerender-invalid-paths/pages/[foo]/[post].js @@ -2,7 +2,7 @@ import React from 'react' // eslint-disable-next-line camelcase export async function unstable_getStaticPaths() { - return [{ foo: 'bad', baz: 'herro' }] + return { paths: [{ foo: 'bad', baz: 'herro' }] } } // eslint-disable-next-line camelcase diff --git a/test/integration/prerender/pages/blog/[post]/[comment].js b/test/integration/prerender/pages/blog/[post]/[comment].js index 9d49339ca9b49..a64b89b540f2b 100644 --- a/test/integration/prerender/pages/blog/[post]/[comment].js +++ b/test/integration/prerender/pages/blog/[post]/[comment].js @@ -3,10 +3,12 @@ import Link from 'next/link' // eslint-disable-next-line camelcase export async function unstable_getStaticPaths() { - return [ - '/blog/post-1/comment-1', - { params: { post: 'post-2', comment: 'comment-2' } }, - ] + return { + paths: [ + '/blog/post-1/comment-1', + { params: { post: 'post-2', comment: 'comment-2' } }, + ], + } } // eslint-disable-next-line camelcase diff --git a/test/integration/prerender/pages/blog/[post]/index.js b/test/integration/prerender/pages/blog/[post]/index.js index 89ed5a94c9d63..6f73040dd7430 100644 --- a/test/integration/prerender/pages/blog/[post]/index.js +++ b/test/integration/prerender/pages/blog/[post]/index.js @@ -4,14 +4,16 @@ import { useRouter } from 'next/router' // eslint-disable-next-line camelcase export async function unstable_getStaticPaths() { - return [ - '/blog/post-1', - { params: { post: 'post-2' } }, - '/blog/[post3]', - '/blog/post-4', - '/blog/post.1', - '/blog/post.1', // handle duplicates - ] + return { + paths: [ + '/blog/post-1', + { params: { post: 'post-2' } }, + '/blog/[post3]', + '/blog/post-4', + '/blog/post.1', + '/blog/post.1', // handle duplicates + ], + } } // eslint-disable-next-line camelcase diff --git a/test/integration/prerender/pages/catchall/[...slug].js b/test/integration/prerender/pages/catchall/[...slug].js index 4a01f9e698076..f1dd8038a6b93 100644 --- a/test/integration/prerender/pages/catchall/[...slug].js +++ b/test/integration/prerender/pages/catchall/[...slug].js @@ -8,12 +8,14 @@ export async function unstable_getStaticProps({ params: { slug } }) { } export async function unstable_getStaticPaths() { - return [ - { params: { slug: ['first'] } }, - '/catchall/second', - { params: { slug: ['another', 'value'] } }, - '/catchall/hello/another', - ] + return { + paths: [ + { params: { slug: ['first'] } }, + '/catchall/second', + { params: { slug: ['another', 'value'] } }, + '/catchall/hello/another', + ], + } } export default ({ slug }) =>

Hi {slug?.join('/')}

diff --git a/test/integration/prerender/pages/user/[user]/profile.js b/test/integration/prerender/pages/user/[user]/profile.js index 59f09fa3701c1..47f3bcd3cd62b 100644 --- a/test/integration/prerender/pages/user/[user]/profile.js +++ b/test/integration/prerender/pages/user/[user]/profile.js @@ -3,7 +3,7 @@ import Link from 'next/link' // eslint-disable-next-line camelcase export async function unstable_getStaticPaths() { - return [] + return { paths: [] } } // eslint-disable-next-line camelcase From 070d5aac76ecbef1d9d3a501fdcf2c59fbc52118 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Fri, 7 Feb 2020 12:50:33 -0600 Subject: [PATCH 2/2] Make sure to specify page in getStaticPaths errors --- packages/next/build/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/next/build/utils.ts b/packages/next/build/utils.ts index a3cde43d939b7..5f76e1d087b97 100644 --- a/packages/next/build/utils.ts +++ b/packages/next/build/utils.ts @@ -574,7 +574,7 @@ export async function isPageStatic( if (invalidStaticPathKeys.length > 0) { throw new Error( - `Extra keys returned from unstable_getStaticPaths (${invalidStaticPathKeys.join( + `Extra keys returned from unstable_getStaticPaths in ${page} (${invalidStaticPathKeys.join( ', ' )}) The only field allowed currently is \`paths\`` ) @@ -584,7 +584,7 @@ export async function isPageStatic( if (!Array.isArray(toPrerender)) { throw new Error( - `Invalid \`paths\` value returned from unstable_getStaticProps.\n` + + `Invalid \`paths\` value returned from unstable_getStaticProps in ${page}.\n` + `\`paths\` must be an array of strings or objects of shape { params: [key: string]: string }` ) }