diff --git a/packages/next/build/webpack/loaders/next-serverless-loader.ts b/packages/next/build/webpack/loaders/next-serverless-loader.ts index 669594fdb2b3b..d81de6a9b5deb 100644 --- a/packages/next/build/webpack/loaders/next-serverless-loader.ts +++ b/packages/next/build/webpack/loaders/next-serverless-loader.ts @@ -772,7 +772,7 @@ const nextServerlessLoader: loader.Loader = function () { if (!renderMode) { if (_nextData || getStaticProps || getServerSideProps) { - if (renderOpts.ssgNotFound) { + if (renderOpts.isNotFound) { res.statusCode = 404 const NotFoundComponent = ${ diff --git a/packages/next/export/worker.ts b/packages/next/export/worker.ts index f13b2c817187f..0645fc552972f 100644 --- a/packages/next/export/worker.ts +++ b/packages/next/export/worker.ts @@ -263,7 +263,7 @@ export default async function exportPage({ html = (result as any).html } - if (!html && !(curRenderOpts as any).ssgNotFound) { + if (!html && !(curRenderOpts as any).isNotFound) { throw new Error(`Failed to render serverless page`) } } else { @@ -318,7 +318,7 @@ export default async function exportPage({ html = await renderMethod(req, res, page, query, curRenderOpts) } } - results.ssgNotFound = (curRenderOpts as any).ssgNotFound + results.ssgNotFound = (curRenderOpts as any).isNotFound const validateAmp = async ( rawAmpHtml: string, diff --git a/packages/next/next-server/server/next-server.ts b/packages/next/next-server/server/next-server.ts index e478f1da66bc0..1ecf546d58176 100644 --- a/packages/next/next-server/server/next-server.ts +++ b/packages/next/next-server/server/next-server.ts @@ -1351,7 +1351,7 @@ export default class Server { html = renderResult.html pageData = renderResult.renderOpts.pageData sprRevalidate = renderResult.renderOpts.revalidate - isNotFound = renderResult.renderOpts.ssgNotFound + isNotFound = renderResult.renderOpts.isNotFound } else { const origQuery = parseUrl(req.url || '', true).query const resolvedUrl = formatUrl({ @@ -1393,7 +1393,7 @@ export default class Server { // TODO: change this to a different passing mechanism pageData = (renderOpts as any).pageData sprRevalidate = (renderOpts as any).revalidate - isNotFound = (renderOpts as any).ssgNotFound + isNotFound = (renderOpts as any).isNotFound } return { html, pageData, sprRevalidate, isNotFound } diff --git a/packages/next/next-server/server/render.tsx b/packages/next/next-server/server/render.tsx index 1cd05e6b21823..7aa8cf64faafd 100644 --- a/packages/next/next-server/server/render.tsx +++ b/packages/next/next-server/server/render.tsx @@ -643,7 +643,7 @@ export async function renderToHTML( ) } - ;(renderOpts as any).ssgNotFound = true + ;(renderOpts as any).isNotFound = true ;(renderOpts as any).revalidate = false return null } @@ -753,21 +753,35 @@ export async function renderToHTML( } const invalidKeys = Object.keys(data).filter( - (key) => key !== 'props' && key !== 'unstable_redirect' + (key) => + key !== 'props' && + key !== 'unstable_redirect' && + key !== 'unstable_notFound' ) if (invalidKeys.length) { throw new Error(invalidKeysMsg('getServerSideProps', invalidKeys)) } + if ('unstable_notFound' in data) { + if (pathname === '/404') { + throw new Error( + `The /404 page can not return unstable_notFound in "getStaticProps", please remove it to continue!` + ) + } + + ;(renderOpts as any).isNotFound = true + return null + } + if ( - data.unstable_redirect && + 'unstable_redirect' in data && typeof data.unstable_redirect === 'object' ) { checkRedirectValues(data.unstable_redirect, req) if (isDataReq) { - data.props = { + ;(data as any).props = { __N_REDIRECT: data.unstable_redirect.destination, } } else { @@ -778,7 +792,11 @@ export async function renderToHTML( if ( (dev || isBuildTimeSSG) && - !isSerializableProps(pathname, 'getServerSideProps', data.props) + !isSerializableProps( + pathname, + 'getServerSideProps', + (data as any).props + ) ) { // this fn should throw an error instead of ever returning `false` throw new Error( @@ -786,7 +804,7 @@ export async function renderToHTML( ) } - props.pageProps = Object.assign({}, props.pageProps, data.props) + props.pageProps = Object.assign({}, props.pageProps, (data as any).props) ;(renderOpts as any).pageData = props } } catch (dataFetchError) { diff --git a/packages/next/types/index.d.ts b/packages/next/types/index.d.ts index 24e8619f1ede4..f834d3114fa0e 100644 --- a/packages/next/types/index.d.ts +++ b/packages/next/types/index.d.ts @@ -132,10 +132,16 @@ export type GetServerSidePropsContext< locales?: string[] } -export type GetServerSidePropsResult
= { - props?: P - unstable_redirect?: Redirect -} +export type GetServerSidePropsResult
= + | { + props: P + } + | { + unstable_redirect: Redirect + } + | { + unstable_notFound: true + } export type GetServerSideProps< P extends { [key: string]: any } = { [key: string]: any }, diff --git a/test/integration/getserversideprops/pages/not-found/[slug].js b/test/integration/getserversideprops/pages/not-found/[slug].js new file mode 100644 index 0000000000000..158be98be1cef --- /dev/null +++ b/test/integration/getserversideprops/pages/not-found/[slug].js @@ -0,0 +1,34 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' + +export default function Page(props) { + const router = useRouter() + + return ( + <> +
gssp page
+{JSON.stringify(props)}
+{JSON.stringify(router.query)}
+{router.pathname}
+{router.asPath}
+ + to / + +gssp page
+{JSON.stringify(props)}
+{JSON.stringify(router.query)}
+{router.pathname}
+{router.asPath}
+ + to / + +