From 91dba7d125a7a04af40c1cf8fa1b64a33b017f21 Mon Sep 17 00:00:00 2001 From: k-taro56 <121674121+k-taro56@users.noreply.github.com> Date: Fri, 5 Jan 2024 01:45:02 +0900 Subject: [PATCH] Fix: Throw an error for empty array return in `generateStaticParams` with `output:export` (#57053) Fixes #57038 # What? Added an error message when `generateStaticParams` returns an empty array with `output:export`. # Why? To provide developers with clear feedback when `generateStaticParams` is not used correctly. # How? Modified the condition checks around the use of `generateStaticParams` to include a check for an empty array and added a corresponding error message. --------- Co-authored-by: Steven --- packages/next/src/build/index.ts | 22 +++++++++++-------- packages/next/src/server/base-server.ts | 7 +++++- .../test/dynamic-missing-gsp-dev.test.ts | 10 +++++++++ .../test/dynamic-missing-gsp-prod.test.ts | 10 +++++++++ test/integration/app-dir-export/test/utils.ts | 7 +++++- 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/packages/next/src/build/index.ts b/packages/next/src/build/index.ts index 352724f84d249..f19fde79196f5 100644 --- a/packages/next/src/build/index.ts +++ b/packages/next/src/build/index.ts @@ -1897,15 +1897,19 @@ export default async function build( const isDynamic = isDynamicRoute(page) const hasGenerateStaticParams = !!workerResult.prerenderRoutes?.length - - if ( - config.output === 'export' && - isDynamic && - !hasGenerateStaticParams - ) { - throw new Error( - `Page "${page}" is missing "generateStaticParams()" so it cannot be used with "output: export" config.` - ) + const isEmptyGenerateStaticParams = + workerResult.prerenderRoutes?.length === 0 + + if (config.output === 'export' && isDynamic) { + if (isEmptyGenerateStaticParams) { + throw new Error( + `Page "${page}"'s "generateStaticParams()" returned an empty array, which is not allowed with "output: export" config.` + ) + } else if (!hasGenerateStaticParams) { + throw new Error( + `Page "${page}" is missing "generateStaticParams()" so it cannot be used with "output: export" config.` + ) + } } if ( diff --git a/packages/next/src/server/base-server.ts b/packages/next/src/server/base-server.ts index 7a2ec83cc4431..70b77e1b056de 100644 --- a/packages/next/src/server/base-server.ts +++ b/packages/next/src/server/base-server.ts @@ -1794,7 +1794,12 @@ export default abstract class Server { ) } const resolvedWithoutSlash = removeTrailingSlash(resolvedUrlPathname) - if (!staticPaths?.includes(resolvedWithoutSlash)) { + if (!staticPaths || staticPaths.length === 0) { + throw new Error( + `Page "${page}"'s "generateStaticParams()" returned an empty array, which is not allowed with "output: export" config.` + ) + } + if (!staticPaths.includes(resolvedWithoutSlash)) { throw new Error( `Page "${page}" is missing param "${resolvedWithoutSlash}" in "generateStaticParams()", which is required with "output: export" config.` ) diff --git a/test/integration/app-dir-export/test/dynamic-missing-gsp-dev.test.ts b/test/integration/app-dir-export/test/dynamic-missing-gsp-dev.test.ts index c5c23a71e5fcd..e9697b920cf40 100644 --- a/test/integration/app-dir-export/test/dynamic-missing-gsp-dev.test.ts +++ b/test/integration/app-dir-export/test/dynamic-missing-gsp-dev.test.ts @@ -21,5 +21,15 @@ describe('app dir - with output export - dynamic missing gsp dev', () => { 'Page "/another/[slug]/page" cannot use both "use client" and export function "generateStaticParams()".', }) }) + + it('should error when generateStaticParams returns an empty array', async () => { + await runTests({ + isDev: true, + dynamicPage: 'undefined', + generateStaticParamsOpt: 'set empty', + expectedErrMsg: + 'Page "/another/[slug]/page"\'s "generateStaticParams()" returned an empty array, which is not allowed with "output: export" config.', + }) + }) }) }) diff --git a/test/integration/app-dir-export/test/dynamic-missing-gsp-prod.test.ts b/test/integration/app-dir-export/test/dynamic-missing-gsp-prod.test.ts index 0bca7526b3e3d..ffa6b4d92c19d 100644 --- a/test/integration/app-dir-export/test/dynamic-missing-gsp-prod.test.ts +++ b/test/integration/app-dir-export/test/dynamic-missing-gsp-prod.test.ts @@ -21,5 +21,15 @@ describe('app dir - with output export - dynamic missing gsp prod', () => { 'Page "/another/[slug]/page" cannot use both "use client" and export function "generateStaticParams()".', }) }) + + it('should error when generateStaticParams returns an empty array', async () => { + await runTests({ + isDev: false, + dynamicPage: 'undefined', + generateStaticParamsOpt: 'set empty', + expectedErrMsg: + 'Page "/another/[slug]"\'s "generateStaticParams()" returned an empty array, which is not allowed with "output: export" config.', + }) + }) }) }) diff --git a/test/integration/app-dir-export/test/utils.ts b/test/integration/app-dir-export/test/utils.ts index b317259793140..de8f6b9a74b35 100644 --- a/test/integration/app-dir-export/test/utils.ts +++ b/test/integration/app-dir-export/test/utils.ts @@ -99,7 +99,7 @@ export async function runTests({ trailingSlash?: boolean dynamicPage?: string dynamicApiRoute?: string - generateStaticParamsOpt?: 'set noop' | 'set client' + generateStaticParamsOpt?: 'set noop' | 'set client' | 'set empty' expectedErrMsg?: string }) { if (trailingSlash !== undefined) { @@ -124,6 +124,11 @@ export async function runTests({ slugPage.replace('export function generateStaticParams', 'function noop') } else if (generateStaticParamsOpt === 'set client') { slugPage.prepend('"use client"\n') + } else if (generateStaticParamsOpt === 'set empty') { + slugPage.replace( + "return [{ slug: 'first' }, { slug: 'second' }]", + 'return []' + ) } await fs.remove(distDir) await fs.remove(exportDir)