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

Update handling for ENOENT from GSSP methods #11302

Merged
merged 2 commits into from Mar 24, 2020
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion packages/next/lib/coalesced-function.ts
Expand Up @@ -3,7 +3,7 @@ type CoalescedInvoke<T> = {
value: T
}

type UnwrapPromise<T> = T extends Promise<infer U> ? U : T
export type UnwrapPromise<T> = T extends Promise<infer U> ? U : T

const globalInvokeCache = new Map<string, Promise<CoalescedInvoke<unknown>>>()

Expand Down
54 changes: 39 additions & 15 deletions packages/next/next-server/server/render.tsx
Expand Up @@ -38,6 +38,8 @@ import { tryGetPreviewData, __ApiPreviewProps } from './api-utils'
import { getPageFiles } from './get-page-files'
import { LoadComponentsReturnType, ManifestItem } from './load-components'
import optimizeAmp from './optimize-amp'
import { UnwrapPromise } from '../../lib/coalesced-function'
import { GetStaticProps, GetServerSideProps } from '../../types'

function noRouter() {
const message =
Expand Down Expand Up @@ -495,12 +497,23 @@ export async function renderToHTML(
}

if (isSSG && !isFallback) {
const data = await getStaticProps!({
...(pageIsDynamic ? { params: query as ParsedUrlQuery } : undefined),
...(previewData !== false
? { preview: true, previewData: previewData }
: undefined),
})
let data: UnwrapPromise<ReturnType<GetStaticProps>>

try {
data = await getStaticProps!({
...(pageIsDynamic ? { params: query as ParsedUrlQuery } : undefined),
...(previewData !== false
? { preview: true, previewData: previewData }
: undefined),
})
} catch (err) {
// remove not found error code to prevent triggering legacy
// 404 rendering
if (err.code === 'ENOENT') {
delete err.code
}
throw err
}

const invalidKeys = Object.keys(data).filter(
key => key !== 'revalidate' && key !== 'props'
Expand Down Expand Up @@ -563,15 +576,26 @@ export async function renderToHTML(
}

if (getServerSideProps && !isFallback) {
const data = await getServerSideProps({
req,
res,
query,
...(pageIsDynamic ? { params: params as ParsedUrlQuery } : undefined),
...(previewData !== false
? { preview: true, previewData: previewData }
: undefined),
})
let data: UnwrapPromise<ReturnType<GetServerSideProps>>

try {
data = await getServerSideProps({
req,
res,
query,
...(pageIsDynamic ? { params: params as ParsedUrlQuery } : undefined),
...(previewData !== false
? { preview: true, previewData: previewData }
: undefined),
})
} catch (err) {
// remove not found error code to prevent triggering legacy
// 404 rendering
if (err.code === 'ENOENT') {
delete err.code
}
throw err
}

const invalidKeys = Object.keys(data).filter(key => key !== 'props')

Expand Down
7 changes: 7 additions & 0 deletions test/integration/getserversideprops/pages/enoent.js
@@ -0,0 +1,7 @@
export async function getServerSideProps() {
const error = new Error('oof')
error.code = 'ENOENT'
throw error
}

export default () => 'hi'
19 changes: 19 additions & 0 deletions test/integration/getserversideprops/test/index.test.js
Expand Up @@ -78,6 +78,12 @@ const expectedManifestRoutes = () => [
),
page: '/default-revalidate',
},
{
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/enoent.json$`
),
page: '/enoent',
},
{
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(buildId)}\\/invalid-keys.json$`
Expand Down Expand Up @@ -205,6 +211,19 @@ const runTests = (dev = false) => {
expect(html).toMatch(/Post:.*?post-1/)
})

it('should handle throw ENOENT correctly', async () => {
const res = await fetchViaHTTP(appPort, '/enoent')
const html = await res.text()

if (dev) {
expect(html).toContain('oof')
} else {
expect(res.status).toBe(500)
expect(html).toContain('Internal Server Error')
expect(html).not.toContain('This page could not be found')
}
})

it('should have gssp in __NEXT_DATA__', async () => {
const html = await renderViaHTTP(appPort, '/')
const $ = cheerio.load(html)
Expand Down