Skip to content

Commit 9804c5f

Browse files
committed
Add fetching 404 SSG data on fallback notFound
1 parent 6d3b065 commit 9804c5f

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
@@ -936,13 +936,23 @@ export default class Router implements BaseRouter {
936936
try {
937937
let Component: ComponentType
938938
let styleSheets: StyleSheetTuple[]
939+
let props: Record<string, any> | undefined
939940
const ssg404 = err.message === SSG_DATA_NOT_FOUND_ERROR
940941

941942
if (ssg404) {
942943
try {
943-
;({ page: Component, styleSheets } = await this.fetchComponent(
944+
let mod: any
945+
;({ page: Component, styleSheets, mod } = await this.fetchComponent(
944946
'/404'
945947
))
948+
949+
// TODO: should we tolerate these props missing and still render the
950+
// page instead of falling back to _error?
951+
if (mod && mod.__N_SSG) {
952+
props = await this._getStaticData(
953+
this.pageLoader.getDataHref('/404', '/404', true, this.locale)
954+
)
955+
}
946956
} catch (_err) {
947957
// non-fatal fallback to _error
948958
}
@@ -958,26 +968,24 @@ export default class Router implements BaseRouter {
958968
}
959969

960970
const routeInfo: PrivateRouteInfo = {
971+
props,
961972
Component,
962973
styleSheets,
963974
err: ssg404 ? undefined : err,
964975
error: ssg404 ? undefined : err,
965976
}
966977

967-
try {
968-
routeInfo.props = await this.getInitialProps(Component, {
969-
err,
970-
pathname,
971-
query,
972-
} as any)
973-
974-
if (ssg404 && routeInfo.props && routeInfo.props.pageProps) {
975-
routeInfo.props.pageProps.statusCode = 404
978+
if (!routeInfo.props) {
979+
try {
980+
routeInfo.props = await this.getInitialProps(Component, {
981+
err,
982+
pathname,
983+
query,
984+
} as any)
985+
} catch (gipErr) {
986+
console.error('Error in error page `getInitialProps`: ', gipErr)
987+
routeInfo.props = {}
976988
}
977-
console.log(routeInfo)
978-
} catch (gipErr) {
979-
console.error('Error in error page `getInitialProps`: ', gipErr)
980-
routeInfo.props = {}
981989
}
982990

983991
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
@@ -824,6 +824,11 @@ function runTests(isDev) {
824824
await browser.eval('document.documentElement.innerHTML')
825825
).toContain('This page could not be found')
826826

827+
const props = JSON.parse(await browser.elementByCss('#props').text())
828+
829+
expect(props.is404).toBe(true)
830+
expect(props.locale).toBe(locale)
831+
827832
const parsedUrl = url.parse(
828833
await browser.eval('window.location.href'),
829834
true
@@ -847,9 +852,44 @@ function runTests(isDev) {
847852
expect(await browser.elementByCss('html').text()).toContain(
848853
'This page could not be found'
849854
)
855+
const props = JSON.parse(await browser.elementByCss('#props').text())
856+
857+
expect(props.is404).toBe(true)
858+
expect(props.locale).toBe('en')
850859
expect(await browser.eval('window.beforeNav')).toBe(null)
851860
})
852861

862+
it('should render 404 for fallback page that returned 404 on client transition', async () => {
863+
const browser = await webdriver(appPort, '/en', true, true)
864+
await browser.eval(`(function() {
865+
next.router.push('/not-found/fallback/first')
866+
})()`)
867+
await browser.waitForElementByCss('h1')
868+
await browser.eval('window.beforeNav = 1')
869+
870+
expect(await browser.elementByCss('html').text()).toContain(
871+
'This page could not be found'
872+
)
873+
const props = JSON.parse(await browser.elementByCss('#props').text())
874+
875+
expect(props.is404).toBe(true)
876+
expect(props.locale).toBe('en')
877+
expect(await browser.elementByCss('html').getAttribute('lang')).toBe('en')
878+
879+
const parsedUrl = url.parse(
880+
await browser.eval('window.location.href'),
881+
true
882+
)
883+
expect(parsedUrl.pathname).toBe('/en/not-found/fallback/first')
884+
expect(parsedUrl.query).toEqual({})
885+
886+
if (isDev) {
887+
// make sure page doesn't reload un-necessarily in development
888+
await waitFor(10 * 1000)
889+
}
890+
expect(await browser.eval('window.beforeNav')).toBe(1)
891+
})
892+
853893
it('should render 404 for fallback page that returned 404', async () => {
854894
const browser = await webdriver(
855895
appPort,
@@ -863,6 +903,10 @@ function runTests(isDev) {
863903
expect(await browser.elementByCss('html').text()).toContain(
864904
'This page could not be found'
865905
)
906+
const props = JSON.parse(await browser.elementByCss('#props').text())
907+
908+
expect(props.is404).toBe(true)
909+
expect(props.locale).toBe('en')
866910
expect(await browser.elementByCss('html').getAttribute('lang')).toBe('en')
867911

868912
const parsedUrl = url.parse(

0 commit comments

Comments
 (0)