Skip to content

Commit

Permalink
Ensure getStaticProps is called for SSG 404 in blocking mode (#18300)
Browse files Browse the repository at this point in the history
This ensures that when using a `pages/404` file with `getStaticProps` that we call `getStaticProps` in `fallback: 'blocking'` mode

Fixes: #18293
  • Loading branch information
ijjk committed Oct 27, 2020
1 parent f7ba546 commit 0f25051
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 4 deletions.
10 changes: 6 additions & 4 deletions packages/next/build/webpack/loaders/next-serverless-loader.ts
Expand Up @@ -492,7 +492,7 @@ const nextServerlessLoader: loader.Loader = function () {
export async function renderReqToHTML(req, res, renderMode, _renderOpts, _params) {
let Document
let Error
let NotFound
let notFoundMod
;[
getStaticProps,
getServerSideProps,
Expand All @@ -502,7 +502,7 @@ const nextServerlessLoader: loader.Loader = function () {
config,
{ default: Document },
{ default: Error },
${absolute404Path ? `{ default: NotFound }, ` : ''}
${absolute404Path ? `notFoundMod, ` : ''}
] = await Promise.all([
getStaticProps,
getServerSideProps,
Expand Down Expand Up @@ -772,13 +772,15 @@ const nextServerlessLoader: loader.Loader = function () {
res.statusCode = 404
const NotFoundComponent = ${
absolute404Path ? 'NotFound' : 'Error'
absolute404Path ? 'notFoundMod.default' : 'Error'
}
const errPathname = "${absolute404Path ? '/404' : '/_error'}"
const result = await renderToHTML(req, res, errPathname, parsedUrl.query, Object.assign({}, options, {
getStaticProps: undefined,
getStaticProps: ${
absolute404Path ? `notFoundMod.getStaticProps` : 'undefined'
},
getStaticPaths: undefined,
getServerSideProps: undefined,
Component: NotFoundComponent,
Expand Down
@@ -0,0 +1,48 @@
import Link from 'next/link'
import { useRouter } from 'next/router'

export default function Page(props) {
const router = useRouter()

return (
<>
<p id="gsp">gsp page</p>
<p id="props">{JSON.stringify(props)}</p>
<p id="router-locale">{router.locale}</p>
<p id="router-locales">{JSON.stringify(router.locales)}</p>
<p id="router-query">{JSON.stringify(router.query)}</p>
<p id="router-pathname">{router.pathname}</p>
<p id="router-as-path">{router.asPath}</p>
<Link href="/">
<a id="to-index">to /</a>
</Link>
<br />
</>
)
}

export const getStaticProps = ({ params, locale, locales }) => {
if (locale === 'en' || locale === 'nl') {
return {
notFound: true,
}
}

return {
props: {
params,
locale,
locales,
},
}
}

export const getStaticPaths = () => {
return {
// the default locale will be used since one isn't defined here
paths: ['first', 'second'].map((slug) => ({
params: { slug },
})),
fallback: 'blocking',
}
}
86 changes: 86 additions & 0 deletions test/integration/i18n-support/test/index.test.js
Expand Up @@ -106,6 +106,16 @@ function runTests(isDev) {
initialRevalidateSeconds: false,
srcRoute: '/gsp/no-fallback/[slug]',
},
'/en-US/not-found/blocking-fallback/first': {
dataRoute: `/_next/data/${buildId}/en-US/not-found/blocking-fallback/first.json`,
initialRevalidateSeconds: false,
srcRoute: '/not-found/blocking-fallback/[slug]',
},
'/en-US/not-found/blocking-fallback/second': {
dataRoute: `/_next/data/${buildId}/en-US/not-found/blocking-fallback/second.json`,
initialRevalidateSeconds: false,
srcRoute: '/not-found/blocking-fallback/[slug]',
},
'/en-US/not-found/fallback/first': {
dataRoute: `/_next/data/${buildId}/en-US/not-found/fallback/first.json`,
initialRevalidateSeconds: false,
Expand Down Expand Up @@ -157,6 +167,18 @@ function runTests(isDev) {
)}/gsp/no\\-fallback/([^/]+?)\\.json$`
),
},
'/not-found/blocking-fallback/[slug]': {
dataRoute: `/_next/data/${buildId}/not-found/blocking-fallback/[slug].json`,
dataRouteRegex: normalizeRegEx(
`^\\/_next\\/data\\/${escapeRegex(
buildId
)}\\/not\\-found\\/blocking\\-fallback\\/([^\\/]+?)\\.json$`
),
fallback: null,
routeRegex: normalizeRegEx(
`^\\/not\\-found\\/blocking\\-fallback\\/([^\\/]+?)(?:\\/)?$`
),
},
'/not-found/fallback/[slug]': {
dataRoute: `/_next/data/${buildId}/not-found/fallback/[slug].json`,
dataRouteRegex: normalizeRegEx(
Expand Down Expand Up @@ -957,6 +979,70 @@ function runTests(isDev) {
expect(await browser.eval('window.beforeNav')).toBe(1)
})

it('should render 404 for blocking fallback page that returned 404 on client transition', async () => {
const browser = await webdriver(appPort, '/en', true, true)
await browser.eval(`(function() {
next.router.push('/not-found/blocking-fallback/first')
})()`)
await browser.waitForElementByCss('h1')
await browser.eval('window.beforeNav = 1')

expect(await browser.elementByCss('html').text()).toContain(
'This page could not be found'
)
const props = JSON.parse(await browser.elementByCss('#props').text())

expect(props.is404).toBe(true)
expect(props.locale).toBe('en')
expect(await browser.elementByCss('html').getAttribute('lang')).toBe('en')

const parsedUrl = url.parse(
await browser.eval('window.location.href'),
true
)
expect(parsedUrl.pathname).toBe('/en/not-found/blocking-fallback/first')
expect(parsedUrl.query).toEqual({})

if (isDev) {
// make sure page doesn't reload un-necessarily in development
await waitFor(10 * 1000)
}
expect(await browser.eval('window.beforeNav')).toBe(1)
})

it('should render 404 for blocking fallback page that returned 404', async () => {
const browser = await webdriver(
appPort,
'/en/not-found/blocking-fallback/first',
true,
true
)
await browser.waitForElementByCss('h1')
await browser.eval('window.beforeNav = 1')

expect(await browser.elementByCss('html').text()).toContain(
'This page could not be found'
)
const props = JSON.parse(await browser.elementByCss('#props').text())

expect(props.is404).toBe(true)
expect(props.locale).toBe('en')
expect(await browser.elementByCss('html').getAttribute('lang')).toBe('en')

const parsedUrl = url.parse(
await browser.eval('window.location.href'),
true
)
expect(parsedUrl.pathname).toBe('/en/not-found/blocking-fallback/first')
expect(parsedUrl.query).toEqual({})

if (isDev) {
// make sure page doesn't reload un-necessarily in development
await waitFor(10 * 1000)
}
expect(await browser.eval('window.beforeNav')).toBe(1)
})

it('should not remove locale prefix for default locale', async () => {
const res = await fetchViaHTTP(appPort, '/en-US', undefined, {
redirect: 'manual',
Expand Down

0 comments on commit 0f25051

Please sign in to comment.