未验证 提交 ef422467 编写于 作者: J JJ Kasper 提交者: GitHub

Add error when exporting pages with fallback: true (#13063)

上级 d16fb63c
# SSG `fallback: true` Export Error
#### Why This Error Occurred
You attempted to export a page with a `fallback: true` return value from `getStaticPaths` which is invalid. `fallback: true` is meant for building pages on-demand after a build has occurred, exporting disables this functionality
#### Possible Ways to Fix It
If you would like the `fallback: true` behavior, `next export` should not be used. Instead follow the [deployment documentation](https://nextjs.org/docs/deployment) to deploy your incrementally generated static site.
### Useful Links
- [deployment documentation](https://nextjs.org/docs/deployment#vercel-recommended)
- [`fallback: true` documentation](https://nextjs.org/docs/basic-features/data-fetching#fallback-true)
......@@ -13,7 +13,7 @@ import { dirname, join, resolve, sep } from 'path'
import { promisify } from 'util'
import { AmpPageStatus, formatAmpMessages } from '../build/output/index'
import createSpinner from '../build/spinner'
import { API_ROUTE } from '../lib/constants'
import { API_ROUTE, SSG_FALLBACK_EXPORT_ERROR } from '../lib/constants'
import { recursiveCopy } from '../lib/recursive-copy'
import { recursiveDelete } from '../lib/recursive-delete'
import {
......@@ -35,6 +35,7 @@ import { eventCliSession } from '../telemetry/events'
import { Telemetry } from '../telemetry/storage'
import { normalizePagePath } from '../next-server/server/normalize-page-path'
import { loadEnvConfig } from '../lib/load-env-config'
import { PrerenderManifest } from '../build'
const exists = promisify(existsOrig)
......@@ -134,7 +135,7 @@ export default async function (
PAGES_MANIFEST
))
let prerenderManifest
let prerenderManifest: PrerenderManifest | undefined = undefined
try {
prerenderManifest = require(join(distDir, PRERENDER_MANIFEST))
} catch (_) {}
......@@ -147,6 +148,7 @@ export default async function (
'pages'
)
const excludedPrerenderRoutes = new Set<string>()
const pages = options.pages || Object.keys(pagesManifest)
const defaultPathMap: ExportPathMap = {}
let hasApiRoutes = false
......@@ -170,6 +172,7 @@ export default async function (
// could run `getStaticProps`. If users make their page work lazily, they
// can manually add it to the `exportPathMap`.
if (prerenderManifest?.dynamicRoutes[page]) {
excludedPrerenderRoutes.add(page)
continue
}
......@@ -277,6 +280,29 @@ export default async function (
hasApiRoutes = true
}
if (prerenderManifest && !options.buildExport) {
const fallbackTruePages = new Set()
for (const key of Object.keys(prerenderManifest.dynamicRoutes)) {
// only error if page is included in path map
if (!exportPathMap[key] && !excludedPrerenderRoutes.has(key)) {
continue
}
if (prerenderManifest.dynamicRoutes[key].fallback !== false) {
fallbackTruePages.add(key)
}
}
if (fallbackTruePages.size) {
throw new Error(
`Found pages with \`fallback: true\`:\n${[...fallbackTruePages].join(
'\n'
)}\n${SSG_FALLBACK_EXPORT_ERROR}\n`
)
}
}
// Warn if the user defines a path for an API page
if (hasApiRoutes) {
log(
......
......@@ -43,3 +43,5 @@ export const UNSTABLE_REVALIDATE_RENAME_ERROR =
export const GSSP_COMPONENT_MEMBER_ERROR = `can not be attached to a page's component and must be exported from the page. See more info here: https://err.sh/next.js/gssp-component-member`
export const NON_STANDARD_NODE_ENV = `You are using a non-standard "NODE_ENV" value in your environment. This creates inconsistencies in the project and is strongly advised against. https://err.sh/next.js/non-standard-node-env`
export const SSG_FALLBACK_EXPORT_ERROR = `Pages with \`fallback: true\` in \`getStaticPaths\` can not be exported. See more info here: https://err.sh/next.js/ssg-fallback-true-export`
export const getStaticProps = () => ({
hello: 'world',
})
export const getStaticPaths = () => ({
fallback: true,
paths: [],
})
export default () => 'hi'
/* eslint-env jest */
import fs from 'fs-extra'
import { join } from 'path'
import { nextBuild, nextExport } from 'next-test-utils'
jest.setTimeout(1000 * 60 * 1)
const appDir = join(__dirname, '../')
const outdir = join(appDir, 'out')
describe('Export error for fallback: true', () => {
it('should build successfully', async () => {
await fs.remove(join(appDir, '.next'))
const { code } = await nextBuild(appDir)
if (code !== 0) throw new Error(`build failed with status ${code}`)
})
it('should have error during next export', async () => {
const { stderr } = await nextExport(appDir, { outdir }, { stderr: true })
expect(stderr).toContain('Found pages with `fallback: true`')
expect(stderr).toContain(
'Pages with `fallback: true` in `getStaticPaths` can not be exported'
)
expect(stderr).toContain('/[slug]')
})
})
......@@ -1281,6 +1281,16 @@ describe('SSG Prerender', () => {
})
describe('export mode', () => {
// disable fallback: true since this is an error during `next export`
const fallbackTruePages = [
'/blog/[post]/[comment].js',
'/user/[user]/profile.js',
'/catchall/[...slug].js',
'/non-json/[p].js',
'/blog/[post]/index.js',
]
const fallbackTruePageContents = {}
beforeAll(async () => {
exportDir = join(appDir, 'out')
await fs.writeFile(
......@@ -1296,6 +1306,19 @@ describe('SSG Prerender', () => {
}`
)
await fs.remove(join(appDir, '.next'))
for (const page of fallbackTruePages) {
const pagePath = join(appDir, 'pages', page)
fallbackTruePageContents[page] = await fs.readFile(pagePath, 'utf8')
await fs.writeFile(
pagePath,
fallbackTruePageContents[page].replace(
'fallback: true',
'fallback: false'
)
)
}
await nextBuild(appDir)
await nextExport(appDir, { outdir: exportDir })
app = await startStaticServer(exportDir)
......@@ -1305,6 +1328,12 @@ describe('SSG Prerender', () => {
afterAll(async () => {
await stopApp(app)
await fs.remove(nextConfig)
for (const page of fallbackTruePages) {
const pagePath = join(appDir, 'pages', page)
await fs.writeFile(pagePath, fallbackTruePageContents[page])
}
})
it('should copy prerender files and honor exportTrailingSlash', async () => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册