Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for getStaticProps in pages/404 #10984

Merged
merged 7 commits into from Mar 12, 2020
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/next/build/index.ts
Expand Up @@ -549,12 +549,12 @@ export default async function build(dir: string, conf = null): Promise<void> {
}

if (hasPages404 && page === '/404') {
if (!result.isStatic) {
if (!result.isStatic && !result.hasStaticProps) {
throw new Error(PAGES_404_GET_INITIAL_PROPS_ERROR)
}
// we need to ensure the 404 lambda is present since we use
// it when _app has getInitialProps
if (customAppGetInitialProps) {
if (customAppGetInitialProps && !result.hasStaticProps) {
staticPages.delete(page)
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/next/lib/constants.ts
Expand Up @@ -30,4 +30,4 @@ export const SERVER_PROPS_GET_INIT_PROPS_CONFLICT = `You can not use getInitialP

export const SERVER_PROPS_SSG_CONFLICT = `You can not use getStaticProps with getServerSideProps. To use SSG, please remove getServerSideProps`

export const PAGES_404_GET_INITIAL_PROPS_ERROR = `\`pages/404\` can not have getInitialProps/getServerSideProps/getStaticProps, https://err.sh/zeit/next.js/404-get-initial-props`
export const PAGES_404_GET_INITIAL_PROPS_ERROR = `\`pages/404\` can not have getInitialProps/getServerSideProps, https://err.sh/zeit/next.js/404-get-initial-props`
5 changes: 1 addition & 4 deletions packages/next/next-server/server/render.tsx
Expand Up @@ -418,10 +418,7 @@ export async function renderToHTML(
renderOpts.nextExport = true
}

if (
pathname === '/404' &&
(hasPageGetInitialProps || getServerSideProps || isSSG)
) {
if (pathname === '/404' && (hasPageGetInitialProps || getServerSideProps)) {
throw new Error(PAGES_404_GET_INITIAL_PROPS_ERROR)
}
}
Expand Down
1 change: 1 addition & 0 deletions test/integration/404-page-ssg/next.config.js
@@ -0,0 +1 @@
module.exports = {}
4 changes: 4 additions & 0 deletions test/integration/404-page-ssg/pages/404.js
@@ -0,0 +1,4 @@
export const getStaticProps = () => ({ props: { hello: 'world' } })

const page = () => `custom 404 page ${Math.random()}`
ijjk marked this conversation as resolved.
Show resolved Hide resolved
export default page
12 changes: 12 additions & 0 deletions test/integration/404-page-ssg/pages/_app.js
@@ -0,0 +1,12 @@
const App = ({ Component, pageProps }) => <Component {...pageProps} />

App.getInitialProps = async ({ Component, ctx }) => {
if (Component.getInitialProps) {
await Component.getInitialProps(ctx)
}
return {
pageProps: {},
}
}

export default App
5 changes: 5 additions & 0 deletions test/integration/404-page-ssg/pages/err.js
@@ -0,0 +1,5 @@
const page = () => 'err page'
page.getInitialProps = () => {
throw new Error('oops')
}
export default page
1 change: 1 addition & 0 deletions test/integration/404-page-ssg/pages/index.js
@@ -0,0 +1 @@
export default () => 'hello from index'
180 changes: 180 additions & 0 deletions test/integration/404-page-ssg/test/index.test.js
@@ -0,0 +1,180 @@
/* eslint-env jest */
/* global jasmine */
import fs from 'fs-extra'
import { join } from 'path'
import {
killApp,
findPort,
launchApp,
nextStart,
nextBuild,
renderViaHTTP,
fetchViaHTTP,
} from 'next-test-utils'

jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2

const appDir = join(__dirname, '../')
const nextConfig = join(appDir, 'next.config.js')
const gip404Err = /`pages\/404` can not have getInitialProps\/getServerSideProps/

let nextConfigContent
let stdout
let stderr
let buildId
let appPort
let app

const runTests = isDev => {
it('should respond to 404 correctly', async () => {
const res = await fetchViaHTTP(appPort, '/404')
expect(res.status).toBe(404)
expect(await res.text()).toContain('custom 404 page')
})

it('should render error correctly', async () => {
const text = await renderViaHTTP(appPort, '/err')
expect(text).toContain(isDev ? 'oops' : 'An unexpected error has occurred')
})

it('should not show an error in the logs for 404 SSG', async () => {
await renderViaHTTP(appPort, '/non-existent')
expect(stderr).not.toMatch(gip404Err)
expect(stdout).not.toMatch(gip404Err)
})

it('should render index page normal', async () => {
const html = await renderViaHTTP(appPort, '/')
expect(html).toContain('hello from index')
})

if (!isDev) {
it('should not revalidate custom 404 page', async () => {
const res1 = await renderViaHTTP(appPort, '/non-existent')
const res2 = await renderViaHTTP(appPort, '/non-existent')
const res3 = await renderViaHTTP(appPort, '/non-existent')
const res4 = await renderViaHTTP(appPort, '/non-existent')

expect(res1 === res2 && res2 === res3 && res3 === res4).toBe(true)

expect(res1).toContain('custom 404 page')
})

it('should set pages404 in routes-manifest correctly', async () => {
const data = await fs.readJSON(join(appDir, '.next/routes-manifest.json'))
expect(data.pages404).toBe(true)
})

it('should have 404 page in prerender-manifest', async () => {
const data = await fs.readJSON(
join(appDir, '.next/prerender-manifest.json')
)
expect(data.routes['/404']).toEqual({
initialRevalidateSeconds: false,
srcRoute: null,
dataRoute: `/_next/data/${buildId}/404.json`,
})
})
}
}

describe('404 Page Support SSG', () => {
describe('server mode', () => {
afterAll(() => killApp(app))

it('should build successfully', async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')
const {
code,
stderr: buildStderr,
stdout: buildStdout,
} = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})

expect(code).toBe(0)
expect(buildStderr).not.toMatch(gip404Err)
expect(buildStdout).not.toMatch(gip404Err)

appPort = await findPort()
stderr = ''
stdout = ''

app = await nextStart(appDir, appPort, {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})

runTests()
})

describe('serverless mode', () => {
afterAll(async () => {
await fs.writeFile(nextConfig, nextConfigContent)
await killApp(app)
})

it('should build successfully', async () => {
nextConfigContent = await fs.readFile(nextConfig, 'utf8')
await fs.writeFile(
nextConfig,
`
module.exports = { target: 'experimental-serverless-trace' }
`
)
const {
code,
stderr: buildStderr,
stdout: buildStdout,
} = await nextBuild(appDir, [], {
stderr: true,
stdout: true,
})

expect(code).toBe(0)
expect(buildStderr).not.toMatch(gip404Err)
expect(buildStdout).not.toMatch(gip404Err)

appPort = await findPort()
stderr = ''
stdout = ''
app = await nextStart(appDir, appPort, {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
buildId = await fs.readFile(join(appDir, '.next/BUILD_ID'), 'utf8')
})

runTests()
})

describe('dev mode', () => {
beforeAll(async () => {
appPort = await findPort()
stderr = ''
stdout = ''
app = await launchApp(appDir, appPort, {
onStdout(msg) {
stdout += msg
},
onStderr(msg) {
stderr += msg
},
})
})
afterAll(() => killApp(app))

runTests(true)
})
})
10 changes: 5 additions & 5 deletions test/integration/404-page/test/index.test.js
Expand Up @@ -182,7 +182,7 @@ describe('404 Page Support', () => {
expect(stderr).toMatch(gip404Err)
})

it('shows error with getStaticProps in pages/404 build', async () => {
it('does not show error with getStaticProps in pages/404 build', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
Expand All @@ -196,11 +196,11 @@ describe('404 Page Support', () => {
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)

expect(stderr).toMatch(gip404Err)
expect(code).toBe(1)
expect(stderr).not.toMatch(gip404Err)
expect(code).toBe(0)
})

it('shows error with getStaticProps in pages/404 dev', async () => {
it('does not show error with getStaticProps in pages/404 dev', async () => {
await fs.move(pages404, `${pages404}.bak`)
await fs.writeFile(
pages404,
Expand All @@ -226,7 +226,7 @@ describe('404 Page Support', () => {
await fs.remove(pages404)
await fs.move(`${pages404}.bak`, pages404)

expect(stderr).toMatch(gip404Err)
expect(stderr).not.toMatch(gip404Err)
})

it('shows error with getServerSideProps in pages/404 build', async () => {
Expand Down