Skip to content

Commit 9a770bd

Browse files
authored
Add fetching 404 SSG data on fallback notFound (#18214)
1 parent 11fce3a commit 9a770bd

File tree

4 files changed

+81
-29
lines changed

4 files changed

+81
-29
lines changed

packages/next/next-server/lib/router/router.ts

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -931,13 +931,23 @@ export default class Router implements BaseRouter {
931931
try {
932932
let Component: ComponentType
933933
let styleSheets: StyleSheetTuple[]
934+
let props: Record<string, any> | undefined
934935
const ssg404 = err.message === SSG_DATA_NOT_FOUND_ERROR
935936

936937
if (ssg404) {
937938
try {
938-
;({ page: Component, styleSheets } = await this.fetchComponent(
939+
let mod: any
940+
;({ page: Component, styleSheets, mod } = await this.fetchComponent(
939941
'/404'
940942
))
943+
944+
// TODO: should we tolerate these props missing and still render the
945+
// page instead of falling back to _error?
946+
if (mod && mod.__N_SSG) {
947+
props = await this._getStaticData(
948+
this.pageLoader.getDataHref('/404', '/404', true, this.locale)
949+
)
950+
}
941951
} catch (_err) {
942952
// non-fatal fallback to _error
943953
}
@@ -953,26 +963,24 @@ export default class Router implements BaseRouter {
953963
}
954964

955965
const routeInfo: PrivateRouteInfo = {
966+
props,
956967
Component,
957968
styleSheets,
958969
err: ssg404 ? undefined : err,
959970
error: ssg404 ? undefined : err,
960971
}
961972

962-
try {
963-
routeInfo.props = await this.getInitialProps(Component, {
964-
err,
965-
pathname,
966-
query,
967-
} as any)
968-
969-
if (ssg404 && routeInfo.props && routeInfo.props.pageProps) {
970-
routeInfo.props.pageProps.statusCode = 404
973+
if (!routeInfo.props) {
974+
try {
975+
routeInfo.props = await this.getInitialProps(Component, {
976+
err,
977+
pathname,
978+
query,
979+
} as any)
980+
} catch (gipErr) {
981+
console.error('Error in error page `getInitialProps`: ', gipErr)
982+
routeInfo.props = {}
971983
}
972-
console.log(routeInfo)
973-
} catch (gipErr) {
974-
console.error('Error in error page `getInitialProps`: ', gipErr)
975-
routeInfo.props = {}
976984
}
977985

978986
return routeInfo

packages/next/next-server/server/next-server.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,8 +1185,19 @@ export default class Server {
11851185
): Promise<string | null> {
11861186
const is404Page = pathname === '/404'
11871187

1188+
const isLikeServerless =
1189+
typeof components.Component === 'object' &&
1190+
typeof (components.Component as any).renderReqToHTML === 'function'
1191+
const isSSG = !!components.getStaticProps
1192+
const isServerProps = !!components.getServerSideProps
1193+
const hasStaticPaths = !!components.getStaticPaths
1194+
1195+
// Toggle whether or not this is a Data request
1196+
const isDataReq = !!query._nextDataReq && (isSSG || isServerProps)
1197+
delete query._nextDataReq
1198+
11881199
// we need to ensure the status code if /404 is visited directly
1189-
if (is404Page) {
1200+
if (is404Page && !isDataReq) {
11901201
res.statusCode = 404
11911202
}
11921203

@@ -1195,22 +1206,10 @@ export default class Server {
11951206
return components.Component
11961207
}
11971208

1198-
// check request state
1199-
const isLikeServerless =
1200-
typeof components.Component === 'object' &&
1201-
typeof (components.Component as any).renderReqToHTML === 'function'
1202-
const isSSG = !!components.getStaticProps
1203-
const isServerProps = !!components.getServerSideProps
1204-
const hasStaticPaths = !!components.getStaticPaths
1205-
12061209
if (!query.amp) {
12071210
delete query.amp
12081211
}
12091212

1210-
// Toggle whether or not this is a Data request
1211-
const isDataReq = !!query._nextDataReq && (isSSG || isServerProps)
1212-
delete query._nextDataReq
1213-
12141213
const locale = query.__nextLocale as string
12151214
delete query.__nextLocale
12161215

test/integration/i18n-support/pages/404.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ export default function NotFound(props) {
22
return (
33
<>
44
<h1 id="not-found">This page could not be found | 404</h1>
5-
<p id="prop">{JSON.stringify(props)}</p>
5+
<p id="props">{JSON.stringify(props)}</p>
66
</>
77
)
88
}
99

10-
export const getStaticProps = () => {
10+
export const getStaticProps = ({ locale }) => {
1111
return {
1212
props: {
13+
locale,
1314
is404: true,
1415
},
1516
}

test/integration/i18n-support/test/index.test.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,11 @@ function runTests(isDev) {
858858
await browser.eval('document.documentElement.innerHTML')
859859
).toContain('This page could not be found')
860860

861+
const props = JSON.parse(await browser.elementByCss('#props').text())
862+
863+
expect(props.is404).toBe(true)
864+
expect(props.locale).toBe(locale)
865+
861866
const parsedUrl = url.parse(
862867
await browser.eval('window.location.href'),
863868
true
@@ -881,9 +886,44 @@ function runTests(isDev) {
881886
expect(await browser.elementByCss('html').text()).toContain(
882887
'This page could not be found'
883888
)
889+
const props = JSON.parse(await browser.elementByCss('#props').text())
890+
891+
expect(props.is404).toBe(true)
892+
expect(props.locale).toBe('en')
884893
expect(await browser.eval('window.beforeNav')).toBe(null)
885894
})
886895

896+
it('should render 404 for fallback page that returned 404 on client transition', async () => {
897+
const browser = await webdriver(appPort, '/en', true, true)
898+
await browser.eval(`(function() {
899+
next.router.push('/not-found/fallback/first')
900+
})()`)
901+
await browser.waitForElementByCss('h1')
902+
await browser.eval('window.beforeNav = 1')
903+
904+
expect(await browser.elementByCss('html').text()).toContain(
905+
'This page could not be found'
906+
)
907+
const props = JSON.parse(await browser.elementByCss('#props').text())
908+
909+
expect(props.is404).toBe(true)
910+
expect(props.locale).toBe('en')
911+
expect(await browser.elementByCss('html').getAttribute('lang')).toBe('en')
912+
913+
const parsedUrl = url.parse(
914+
await browser.eval('window.location.href'),
915+
true
916+
)
917+
expect(parsedUrl.pathname).toBe('/en/not-found/fallback/first')
918+
expect(parsedUrl.query).toEqual({})
919+
920+
if (isDev) {
921+
// make sure page doesn't reload un-necessarily in development
922+
await waitFor(10 * 1000)
923+
}
924+
expect(await browser.eval('window.beforeNav')).toBe(1)
925+
})
926+
887927
it('should render 404 for fallback page that returned 404', async () => {
888928
const browser = await webdriver(
889929
appPort,
@@ -897,6 +937,10 @@ function runTests(isDev) {
897937
expect(await browser.elementByCss('html').text()).toContain(
898938
'This page could not be found'
899939
)
940+
const props = JSON.parse(await browser.elementByCss('#props').text())
941+
942+
expect(props.is404).toBe(true)
943+
expect(props.locale).toBe('en')
900944
expect(await browser.elementByCss('html').getAttribute('lang')).toBe('en')
901945

902946
const parsedUrl = url.parse(

0 commit comments

Comments
 (0)