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

Update /500 page exporting when _error has custom GIP (#22887)

This updates to not automatically export `/500` from `_error` if a custom `getInitialProps` is used since logic may be used inside of this method that causes the export to fail. Users can still opt-in to the static `/500` by adding a `pages/500.js` file. 

This also refactors checking `_app` for custom `getInitialProps` to outside of the static check loop to prevent a potential race condition where we could run this check multiple times un-necessarily.  

Fixes: https://github.com/vercel/next.js/issues/22815
上级 635a98e2
......@@ -605,11 +605,43 @@ export default async function build(
async () =>
hasCustomErrorPage &&
(await hasCustomGetInitialProps(
getPagePath('/_error', distDir, isLikeServerless),
'/_error',
distDir,
isLikeServerless,
runtimeEnvConfig,
false
))
)
// we don't output _app in serverless mode so use _app export
// from _error instead
const appPageToCheck = isLikeServerless ? '/_error' : '/_app'
customAppGetInitialProps = await hasCustomGetInitialProps(
appPageToCheck,
distDir,
isLikeServerless,
runtimeEnvConfig,
true
)
namedExports = await getNamedExports(
appPageToCheck,
distDir,
isLikeServerless,
runtimeEnvConfig
)
if (customAppGetInitialProps) {
console.warn(
chalk.bold.yellow(`Warning: `) +
chalk.yellow(
`You have opted-out of Automatic Static Optimization due to \`getInitialProps\` in \`pages/_app\`. This does not opt-out pages with \`getStaticProps\``
)
)
console.warn(
'Read more: https://err.sh/next.js/opt-out-auto-static-optimization\n'
)
}
await Promise.all(
pageKeys.map(async (page) => {
......@@ -633,41 +665,6 @@ export default async function build(
)
if (nonReservedPage) {
const serverBundle = getPagePath(
page,
distDir,
isLikeServerless
)
if (customAppGetInitialProps === undefined) {
customAppGetInitialProps = await hasCustomGetInitialProps(
isLikeServerless
? serverBundle
: getPagePath('/_app', distDir, isLikeServerless),
runtimeEnvConfig,
true
)
namedExports = getNamedExports(
isLikeServerless
? serverBundle
: getPagePath('/_app', distDir, isLikeServerless),
runtimeEnvConfig
)
if (customAppGetInitialProps) {
console.warn(
chalk.bold.yellow(`Warning: `) +
chalk.yellow(
`You have opted-out of Automatic Static Optimization due to \`getInitialProps\` in \`pages/_app\`. This does not opt-out pages with \`getStaticProps\``
)
)
console.warn(
'Read more: https://err.sh/next.js/opt-out-auto-static-optimization\n'
)
}
}
try {
let workerResult = await traceAsyncFn(
tracer.startSpan('is-page-static'),
......@@ -917,6 +914,7 @@ export default async function build(
})
const hasPages500 = usedStaticStatusPages.includes('/500')
const useDefaultStatic500 = !hasPages500 && !hasNonStaticErrorPage
await traceAsyncFn(tracer.startSpan('static-generation'), async () => {
const combinedPages = [...staticPages, ...ssgPages]
......@@ -994,9 +992,7 @@ export default async function build(
}
}
// ensure 500.html is always generated even if pages/500.html
// doesn't exist
if (!hasPages500) {
if (useDefaultStatic500) {
defaultMap['/500'] = {
page: '/_error',
}
......@@ -1007,7 +1003,7 @@ export default async function build(
...staticPages,
...ssgPages,
...(useStatic404 ? ['/404'] : []),
...(!hasPages500 ? ['/500'] : []),
...(useDefaultStatic500 ? ['/500'] : []),
]) {
const isSsg = ssgPages.has(page)
const isDynamic = isDynamicRoute(page)
......@@ -1175,7 +1171,7 @@ export default async function build(
await moveExportedPage('/_error', '/404', '/404', false, 'html')
}
if (!hasPages500) {
if (useDefaultStatic500) {
await moveExportedPage('/_error', '/500', '/500', false, 'html')
}
......
......@@ -860,12 +860,16 @@ export async function isPageStatic(
}
export async function hasCustomGetInitialProps(
bundle: string,
page: string,
distDir: string,
isLikeServerless: boolean,
runtimeEnvConfig: any,
checkingApp: boolean
): Promise<boolean> {
require('../next-server/lib/runtime-config').setConfig(runtimeEnvConfig)
let mod = require(bundle)
const components = await loadComponents(distDir, page, isLikeServerless)
let mod = components.ComponentMod
if (checkingApp) {
mod = (await mod._app) || mod.default || mod
......@@ -876,12 +880,17 @@ export async function hasCustomGetInitialProps(
return mod.getInitialProps !== mod.origGetInitialProps
}
export function getNamedExports(
bundle: string,
export async function getNamedExports(
page: string,
distDir: string,
isLikeServerless: boolean,
runtimeEnvConfig: any
): Array<string> {
): Promise<Array<string>> {
require('../next-server/lib/runtime-config').setConfig(runtimeEnvConfig)
return Object.keys(require(bundle))
const components = await loadComponents(distDir, page, isLikeServerless)
let mod = components.ComponentMod
return Object.keys(mod)
}
export function detectConflictingPaths(
......
......@@ -66,6 +66,7 @@ const runTests = (mode = 'server') => {
describe('500 Page Support', () => {
describe('dev mode', () => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
appPort = await findPort()
app = await launchApp(appDir, appPort)
})
......@@ -76,6 +77,7 @@ describe('500 Page Support', () => {
describe('server mode', () => {
beforeAll(async () => {
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
......@@ -96,6 +98,7 @@ describe('500 Page Support', () => {
}
`
)
await fs.remove(join(appDir, '.next'))
await nextBuild(appDir)
appPort = await findPort()
app = await nextStart(appDir, appPort)
......@@ -119,6 +122,7 @@ describe('500 Page Support', () => {
export default page
`
)
await fs.remove(join(appDir, '.next'))
const { stderr, stdout: buildStdout, code } = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
......@@ -148,6 +152,7 @@ describe('500 Page Support', () => {
it('builds 500 statically by default with no pages/500', async () => {
await fs.rename(pages500, `${pages500}.bak`)
await fs.remove(join(appDir, '.next'))
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.rename(`${pages500}.bak`, pages500)
......@@ -188,7 +193,7 @@ describe('500 Page Support', () => {
}
})
it('builds 500 statically by default with no pages/500 and custom _error', async () => {
it('builds 500 statically by default with no pages/500 and custom _error without getInitialProps', async () => {
await fs.rename(pages500, `${pages500}.bak`)
await fs.writeFile(
pagesError,
......@@ -196,15 +201,49 @@ describe('500 Page Support', () => {
function Error({ statusCode }) {
return <p>Error status: {statusCode}</p>
}
Error.getInitialProps = ({ res, err }) => {
export default Error
`
)
await fs.remove(join(appDir, '.next'))
const { stderr: buildStderr, code } = await nextBuild(appDir, [], {
stderr: true,
})
await fs.rename(`${pages500}.bak`, pages500)
await fs.remove(pagesError)
console.log(buildStderr)
expect(buildStderr).not.toMatch(gip500Err)
expect(code).toBe(0)
expect(
await fs.pathExists(join(appDir, '.next/server/pages/500.html'))
).toBe(true)
})
it('does not build 500 statically with no pages/500 and custom getInitialProps in _error', async () => {
await fs.rename(pages500, `${pages500}.bak`)
await fs.writeFile(
pagesError,
`
function Error({ statusCode }) {
return <p>Error status: {statusCode}</p>
}
Error.getInitialProps = ({ req, res, err }) => {
console.error('called _error.getInitialProps')
if (req.url === '/500') {
throw new Error('should not export /500')
}
return {
statusCode: res && res.statusCode ? res.statusCode : err ? err.statusCode : 404
}
}
export default Error
`
)
await fs.remove(join(appDir, '.next'))
const { stderr: buildStderr, code } = await nextBuild(appDir, [], {
stderr: true,
})
......@@ -215,7 +254,7 @@ describe('500 Page Support', () => {
expect(code).toBe(0)
expect(
await fs.pathExists(join(appDir, '.next/server/pages/500.html'))
).toBe(true)
).toBe(false)
let appStderr = ''
const appPort = await findPort()
......@@ -241,6 +280,7 @@ describe('500 Page Support', () => {
export default page
`
)
await fs.remove(join(appDir, '.next'))
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages500)
await fs.move(`${pages500}.bak`, pages500)
......@@ -288,6 +328,7 @@ describe('500 Page Support', () => {
export default page
`
)
await fs.remove(join(appDir, '.next'))
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages500)
await fs.move(`${pages500}.bak`, pages500)
......@@ -335,6 +376,7 @@ describe('500 Page Support', () => {
export default page
`
)
await fs.remove(join(appDir, '.next'))
const { stderr, code } = await nextBuild(appDir, [], { stderr: true })
await fs.remove(pages500)
await fs.move(`${pages500}.bak`, pages500)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册