Skip to content

Commit

Permalink
Add support for getStaticProps in pages/404 (#10984)
Browse files Browse the repository at this point in the history
* Make sure to not show pages/404 GIP error from _app having GIP

* Add error for getStaticProps in pages/404 too

* Add support for getStaticProps in pages/404

* Update test
  • Loading branch information
ijjk authored Mar 12, 2020
1 parent 9d9f7a5 commit ccb6ce7
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 12 deletions.
4 changes: 2 additions & 2 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {}
6 changes: 6 additions & 0 deletions test/integration/404-page-ssg/pages/404.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const getStaticProps = () => ({
props: { hello: 'world', random: Math.random() },
})

const page = ({ random }) => `custom 404 page ${random}`
export default page
12 changes: 12 additions & 0 deletions test/integration/404-page-ssg/pages/_app.js
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => 'hello from index'
180 changes: 180 additions & 0 deletions test/integration/404-page-ssg/test/index.test.js
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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

0 comments on commit ccb6ce7

Please sign in to comment.