vercel / next.js

The React Framework
https://nextjs.org
MIT License
124.87k stars 26.66k forks source link

NextJS export does not recognise generateStaticParams #58171

Open meds opened 10 months ago

meds commented 10 months ago

Link to the code that reproduces this issue

https://github.com/meds/nextjs-broken-export

To Reproduce

  1. Run pnpm build

Current vs. Expected behavior

I expected nextjs to build and export to an output directory with static files.

Instead I got an error:

Error: Page "/test/[id]" is missing "generateStaticParams()" so it cannot be used with "output: export" config.

Verify canary release

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.0.0: Tue Jul 18 20:35:35 PDT 2023; root:xnu-10002.0.199.505.1~3/RELEASE_ARM64_T6000
Binaries:
  Node: 18.12.0
  npm: 8.19.2
  Yarn: 1.22.19
  pnpm: 8.6.5
Relevant Packages:
  next: 14.0.1
  eslint-config-next: N/A
  react: 18.0.0
  react-dom: 18.0.0
  typescript: 5.0.2
Next.js Config:
  output: export

Which area(s) are affected? (Select all that apply)

App Router, Static HTML Export (output: "export")

Additional context

No response

GonzagaGustavo commented 10 months ago

This is not a bug. You need to return the array with an object with a parameter that indicates the dynamic route. Ex:

export function generateStaticParams() {
  return [{ id: "test" }];
}

export default async function Page(props: any) {
  return <div>{JSON.stringify(props.params)}</div>;
}
airtonix commented 8 months ago

> yarn next info  

Operating System:
  Platform: linux
  Arch: x64
  Version: #1 SMP PREEMPT_DYNAMIC Mon Jan  1 20:31:07 UTC 2024
Binaries:
  Node: 18.17.0
  npm: 9.6.7
  Yarn: 4.0.2
  pnpm: N/A
Relevant Packages:
  next: 14.0.4
  eslint-config-next: 14.0.4
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.3.3
Next.js Config:
  output: N/A
// apps/web/src/app/p/[slug]/page.tsx

import { DocumentRenderer } from '@keystatic/core/renderer';

import { NormalPageTemplate } from '@zenobius/ui-web-pagetemplates-normalpage';
import { Site } from '@zenobius/ui-web-components-sitelayout';
import { CmsContent } from '@zenobius/ui-web-components-cmscontent'

import { ContentStore } from '../../../services/ContentStore';

export default async function Post({ params }: {
    params: { slug: string }
}) {
    const post = await ContentStore.collections.pages.read(params.slug);

    if (!post) {
        return (
            <Site>
                <h1>Not found</h1>
            </Site>
        )
    }

    return (
        <Site>
            <NormalPageTemplate
                title={post.title}
                stage={post.stage}
            >
                <CmsContent>
                    <DocumentRenderer document={await post.content()} />
                </CmsContent>
            </NormalPageTemplate>
        </Site>
    );
}

type Params = {
    slug: string;
}

export async function generateStaticParams(): Promise<Params[]> {

    const pages = await ContentStore.collections.pages.all();

    if (!pages) {
        return [{ slug: 'not-found' }];
    }

    return pages.map(page => ({
        slug: page.slug
    }));
}

then

zenobi.us on git feat/keystatic [x!?] via nodejs v18.17.0 took 15s 
x yarn nx build web

> nx run web:build:production

   ▲ Next.js 14.0.4
   - Experiments (use at your own risk):
     · typedRoutes
   Creating an optimized production build ...
 ✓ Compiled successfully
   Skipping linting
   Checking validity of types ...
   Collecting page data ...
> Build error occurred
Error: Page "/p/[slug]" is missing "generateStaticParams()" so it cannot be used with "output: export" config.
    at /mnt/Store/Projects/Mine/Github/zenobi.us/node_modules/next/dist/build/index.js:1012:59
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Span.traceAsyncFn (/mnt/Store/Projects/Mine/Github/zenobi.us/node_modules/next/dist/trace/trace.js:147:20)
    at async Promise.all (index 8)
    at async /mnt/Store/Projects/Mine/Github/zenobi.us/node_modules/next/dist/build/index.js:892:17
    at async Span.traceAsyncFn (/mnt/Store/Projects/Mine/Github/zenobi.us/node_modules/next/dist/trace/trace.js:147:20)
    at async /mnt/Store/Projects/Mine/Github/zenobi.us/node_modules/next/dist/build/index.js:829:124
    at async Span.traceAsyncFn (/mnt/Store/Projects/Mine/Github/zenobi.us/node_modules/next/dist/trace/trace.js:147:20)
    at async build (/mnt/Store/Projects/Mine/Github/zenobi.us/node_modules/next/dist/build/index.js:187:29)
    at async main (/mnt/Store/Projects/Mine/Github/zenobi.us/node_modules/next/dist/bin/next:157:5)
Error occurred while trying to run the build command
1

 ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Ran target build for project web (8s)

    ✖    1/1 failed
    ✔    0/1 succeeded [0 read from cache]

it can happen when you're "thing" only returns empty results.

so make sure you're checking for length too:


type Params = {
    slug: string;
}

export async function generateStaticParams(): Promise<Params[]> {

    const pages = await ContentStore.collections.pages.all();

    if (!pages || pages.length === 0) {
        return [{ slug: 'not-found' }];
    }

    return pages.map(page => ({
        slug: page.slug
    }));
}

now

zenobi.us on git feat/keystatic [x!?] via nodejs v18.17.0 took 7s 
x yarn nx build web

> nx run web:build:production

   ▲ Next.js 14.0.4
   - Experiments (use at your own risk):
     · typedRoutes
   Creating an optimized production build ...
 ✓ Compiled successfully
   Skipping linting
   Checking validity of types ...
   Collecting page data ...
   Generating static pages (0/14) ...
   Generating static pages (3/14) 
   Generating static pages (6/14) 
   Generating static pages (10/14) 
 ✓ Generating static pages (14/14) 
   Finalizing page optimization ...
   Collecting build traces ...
Route (app)                                            Size     First Load JS
┌ ○ /                                                  1.24 kB        83.5 kB
├ ○ /_not-found                                        916 B          83.2 kB
├ ○ /b                                                 8.44 kB         104 kB
├ ● /b/[slug]                                          1.69 kB        97.5 kB
├   ├ /b/2014-05-01-bust-the-cache-out-of-it
├   ├ /b/2014-05-11-wintersmithplugin-externalmounter
├   ├ /b/2016-01-01-automation-what-i-have-learnt
├   └ [+3 more paths]
├ ○ /me                                                1.6 kB         92.4 kB
└ ● /p/[slug]                                          1.6 kB         92.4 kB
    └ /p/not-found
+ First Load JS shared by all                          82.3 kB
  ├ chunks/1dd3208c-db9eec636a334a07.js                53.3 kB
  ├ chunks/53-f2b3fdbbf4bc8eab.js                      26.8 kB
  ├ chunks/main-app-b7bee2021889a694.js                267 B
  └ chunks/webpack-1c9d7ab86a1268c6.js                 1.89 kB
○  (Static)  prerendered as static content
●  (SSG)     prerendered as static HTML (uses getStaticProps)

 ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Successfully ran target build for project web (14s)
luoluo5945 commented 1 month ago

我懂了,你可以根据环境变量或者一些条件判断来按需打包自己需要的页面,不需要的统统打包到指定的垃圾桶目录,例如:'not-found' (I see. You can package the pages you need based on environment variables or some criteria. Everything that is not needed is packed into the designated bin directory, For example: 'not-found')

// apps/web/src/app/p/[slug]/page.tsx

export async function generateStaticParams(): Promise<Params[]> {

    const pages = await ContentStore.collections.pages.all();

    if (!pages || pages.length === 0) {
        return [{ slug: 'not-found' }];
    }

    return pages.map(page => ({
        slug: page.slug
    }));
}

这样打包出来的out里面会有一个 “not-found” 垃圾桶目录,可以在build脚本里面手动删了这个目录就好了! (In this way, there will be a "not-found" garbage bin directory inside the packaged out, then you can manually delete this directory in the build script!)

pthurmond-vmlyr commented 3 weeks ago

This is not a bug. You need to return the array with an object with a parameter that indicates the dynamic route. Ex:

export function generateStaticParams() {
  return [{ id: "test" }];
}

export default async function Page(props: any) {
  return <div>{JSON.stringify(props.params)}</div>;
}

So I have a situation where I have no idea what the value will end up being. I cannot query a collection or DB or whatever. It is truly dynamic. How do I tell it that it needs to just pull it from the URL path on page load? The documentation is VERY unclear on this.