Skip to content

fix: preview mode for odb/fallback pages #564

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

Merged
merged 7 commits into from
Aug 2, 2021
Merged
3 changes: 3 additions & 0 deletions src/lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const CUSTOM_HEADERS_PATH = join('.', '_headers')
// creating the next/image redirect
const NEXT_IMAGE_FUNCTION_NAME = 'next_image'

const PREVIEW_MODE_COOKIES = ['Cookie=__prerender_bypass,__next_preview_data']

const SRC_FILES = [PUBLIC_PATH, NEXT_CONFIG_PATH, CUSTOM_REDIRECTS_PATH, CUSTOM_HEADERS_PATH, ...NEXT_SRC_DIRS]

module.exports = {
Expand All @@ -52,4 +54,5 @@ module.exports = {
CUSTOM_HEADERS_PATH,
NEXT_IMAGE_FUNCTION_NAME,
SRC_FILES,
PREVIEW_MODE_COOKIES,
}
7 changes: 7 additions & 0 deletions src/lib/helpers/convertToBasePathRedirects.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ const convertToBasePathRedirects = ({ basePath, nextRedirects }) => {
},
]
basePathRedirects.push(...indexRedirects)
} else if (r.specialPreviewMode) {
basePathRedirects.push({
route: `${basePath}${r.route}`,
target: r.target,
force: true,
conditions: r.conditions,
})
} else if (!r.route.includes('_next/') && r.target.includes('/.netlify/functions') && r.conditions) {
// If this is a preview mode redirect, we need different behavior than other function targets below
// because the conditions prevent us from doing a route -> basePath/route force
Expand Down
5 changes: 5 additions & 0 deletions src/lib/helpers/getPreviewModeFunctionName.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// Some pages (getStaticProps/withFallback) require a separate standard function for preview mode; this provides the name for that function, to be called in several places

const getPreviewModeFunctionName = (functionName) => `preview-${functionName}`

module.exports = getPreviewModeFunctionName
2 changes: 2 additions & 0 deletions src/lib/helpers/isRouteWithFallback.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
const getPrerenderManifest = require('./getPrerenderManifest')

const isRouteWithFallback = async (route) => {
if (!route) return false

const { dynamicRoutes } = await getPrerenderManifest()

// Fallback "blocking" routes will have fallback: null in manifest
Expand Down
8 changes: 5 additions & 3 deletions src/lib/helpers/setupNetlifyFunctionForPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@ const getTemplate = require('../templates/getTemplate')
const copyDynamicImportChunks = require('./copyDynamicImportChunks')
const getNetlifyFunctionName = require('./getNetlifyFunctionName')
const getNextDistDir = require('./getNextDistDir')
const getPreviewModeFunctionName = require('./getPreviewModeFunctionName')
const { logItem } = require('./logger')

// Create a Netlify Function for the page with the given file path
const setupNetlifyFunctionForPage = async ({ filePath, functionsPath, isApiPage, isISR }) => {
const setupNetlifyFunctionForPage = async ({ filePath, functionsPath, isApiPage, isODB, forFallbackPreviewMode }) => {
// Set function name based on file path
const functionName = getNetlifyFunctionName(filePath, isApiPage)
const defaultFunctionName = getNetlifyFunctionName(filePath, isApiPage)
const functionName = forFallbackPreviewMode ? getPreviewModeFunctionName(defaultFunctionName) : defaultFunctionName
const functionDirectory = join(functionsPath, functionName)

await ensureDir(functionDirectory)
Expand All @@ -24,7 +26,7 @@ const setupNetlifyFunctionForPage = async ({ filePath, functionsPath, isApiPage,

// Write entry point to function directory
const entryPointPath = join(functionDirectory, `${functionName}.js`)
await writeFile(entryPointPath, getTemplate({ filePath, isISR }))
await writeFile(entryPointPath, getTemplate({ filePath, isODB }))

// Copy function helper
await copy(join(TEMPLATES_DIR, 'getHandlerFunction.js'), join(functionDirectory, 'getHandlerFunction.js'))
Expand Down
9 changes: 6 additions & 3 deletions src/lib/pages/getStaticProps/pages.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const asyncForEach = require('../../helpers/asyncForEach')
const getPrerenderManifest = require('../../helpers/getPrerenderManifest')
const isRouteWithFallback = require('../../helpers/isRouteWithFallback')

// Get pages using getStaticProps
const getPages = async () => {
Expand All @@ -7,9 +9,10 @@ const getPages = async () => {
// Collect pages
const pages = []

Object.entries(routes).forEach(([route, { dataRoute, initialRevalidateSeconds, srcRoute }]) => {
// Ignore pages with revalidate, these will need to be SSRed
if (initialRevalidateSeconds) return
await asyncForEach(Object.entries(routes), async ([route, { dataRoute, initialRevalidateSeconds, srcRoute }]) => {
// Ignore pages with revalidate (but no fallback), these will need to be SSRed
const isFallbackRoute = await isRouteWithFallback(srcRoute)
if (initialRevalidateSeconds && !isFallbackRoute) return

pages.push({
route,
Expand Down
10 changes: 8 additions & 2 deletions src/lib/pages/getStaticProps/redirects.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const addDefaultLocaleRedirect = require('../../helpers/addDefaultLocaleRedirect
const asyncForEach = require('../../helpers/asyncForEach')
const getFilePathForRoute = require('../../helpers/getFilePathForRoute')
const getNetlifyFunctionName = require('../../helpers/getNetlifyFunctionName')
const getPreviewModeFunctionName = require('../../helpers/getPreviewModeFunctionName')
const isRouteWithFallback = require('../../helpers/isRouteWithFallback')

const getPages = require('./pages')

Expand Down Expand Up @@ -35,11 +37,15 @@ const getRedirects = async () => {
const relativePath = getFilePathForRoute(srcRoute || route, 'js')
const filePath = slash(join('pages', relativePath))
const functionName = getNetlifyFunctionName(filePath)
const isODB = await isRouteWithFallback(srcRoute)

// Preview mode conditions
const conditions = ['Cookie=__prerender_bypass,__next_preview_data']
// ODB pages' preview mode needs a special flagged standard function because
// their default function (an ODB) is not functional for preview mode
const target = `/.netlify/functions/${functionName}`
const previewModeRedirect = { conditions, force: true, target }
const previewModeTarget = isODB ? `/.netlify/functions/${getPreviewModeFunctionName(functionName)}` : target
const previewModeRedirect = { conditions, force: true, target: previewModeTarget }

// Add a preview mode redirect for the standard route
redirects.push({
Expand All @@ -54,7 +60,7 @@ const getRedirects = async () => {
})

// Preview mode default locale redirect must precede normal default locale redirect
await addDefaultLocaleRedirect(redirects, route, target, previewModeRedirect)
await addDefaultLocaleRedirect(redirects, route, target, { conditions, force: true })
await addDefaultLocaleRedirect(redirects, route)
})

Expand Down
14 changes: 13 additions & 1 deletion src/lib/pages/getStaticPropsWithFallback/redirects.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ const { join } = require('path')

const slash = require('slash')

const { PREVIEW_MODE_COOKIES } = require('../../config')
const addLocaleRedirects = require('../../helpers/addLocaleRedirects')
const asyncForEach = require('../../helpers/asyncForEach')
const getFilePathForRoute = require('../../helpers/getFilePathForRoute')
const getNetlifyFunctionName = require('../../helpers/getNetlifyFunctionName')
const getPreviewModeFunctionName = require('../../helpers/getPreviewModeFunctionName')

const getPages = require('./pages')

Expand All @@ -18,10 +20,20 @@ const getRedirects = async () => {
const filePath = slash(join('pages', relativePath))
const functionName = getNetlifyFunctionName(filePath)
const target = `/.netlify/functions/${functionName}`
const previewModeTarget = `/.netlify/functions/${getPreviewModeFunctionName(functionName)}`

await addLocaleRedirects(redirects, route, target)

// Add one redirect for the page
// Add a redirect for preview mode pointing to the standard function
redirects.push({
route,
target: previewModeTarget,
conditions: PREVIEW_MODE_COOKIES,
force: true,
specialPreviewMode: true,
})

// Add one redirect pointing to the ODB
redirects.push({
route,
target,
Expand Down
11 changes: 8 additions & 3 deletions src/lib/pages/getStaticPropsWithFallback/setup.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,16 @@ const setup = async (functionsPath) => {
const pages = await getPages()

// Create Netlify Function for every page
return pages.map(({ route }) => {
return pages.reduce((jobs, { route }) => {
const relativePath = getFilePathForRoute(route, 'js')
const filePath = slash(join('pages', relativePath))
return { type: 'function', filePath, functionsPath, isISR: true }
})
// Need two different functions - one ODB for normal pages, one standard for preview mode
return [
...jobs,
{ type: 'function', filePath, functionsPath, isODB: true },
{ type: 'function', filePath, functionsPath, forFallbackPreviewMode: true },
]
}, [])
}

module.exports = setup
4 changes: 2 additions & 2 deletions src/lib/templates/getTemplate.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const getTemplate = ({ filePath, isISR }) => {
if (isISR) {
const getTemplate = ({ filePath, isODB }) => {
if (isODB) {
return `// Auto-generated file. DO NOT MODIFY.
const { getHandlerFunction } = require('./getHandlerFunction')
const { builder } = require('@netlify/functions')
Expand Down
34 changes: 22 additions & 12 deletions src/tests/__snapshots__/basePath.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ exports[`Routing creates Netlify redirects 1`] = `
/_next/data/%BUILD_ID%/getStaticProps/2.json /.netlify/functions/next_getStaticProps_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/static.json /.netlify/functions/next_getStaticProps_static 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/with-revalidate.json /.netlify/functions/next_getStaticProps_withrevalidate 200
/_next/data/%BUILD_ID%/getStaticProps/withFallback/3.json /.netlify/functions/next_getStaticProps_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallback/4.json /.netlify/functions/next_getStaticProps_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallback/my/path/1.json /.netlify/functions/next_getStaticProps_withFallback_slug 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallback/my/path/2.json /.netlify/functions/next_getStaticProps_withFallback_slug 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallbackBlocking/3.json /.netlify/functions/next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallbackBlocking/4.json /.netlify/functions/next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallback/3.json /.netlify/functions/preview-next_getStaticProps_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallback/4.json /.netlify/functions/preview-next_getStaticProps_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallback/my/path/1.json /.netlify/functions/preview-next_getStaticProps_withFallback_slug 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallback/my/path/2.json /.netlify/functions/preview-next_getStaticProps_withFallback_slug 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallbackBlocking/3.json /.netlify/functions/preview-next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withFallbackBlocking/4.json /.netlify/functions/preview-next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withRevalidate/1.json /.netlify/functions/next_getStaticProps_withRevalidate_id 200
/_next/data/%BUILD_ID%/getStaticProps/withRevalidate/2.json /.netlify/functions/next_getStaticProps_withRevalidate_id 200
/_next/data/%BUILD_ID%/getStaticProps/withRevalidate/withFallback/1.json /.netlify/functions/preview-next_getStaticProps_withRevalidate_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/_next/data/%BUILD_ID%/getStaticProps/withRevalidate/withFallback/2.json /.netlify/functions/preview-next_getStaticProps_withRevalidate_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/api/hello-background /foo/api/hello-background 301!
/api/static /foo/api/static 301!
/foo /.netlify/functions/next_index 200
Expand All @@ -31,20 +33,24 @@ exports[`Routing creates Netlify redirects 1`] = `
/foo/getStaticProps/static /.netlify/functions/next_getStaticProps_static 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/static /getStaticProps/static 200
/foo/getStaticProps/with-revalidate /.netlify/functions/next_getStaticProps_withrevalidate 200
/foo/getStaticProps/withFallback/3 /.netlify/functions/next_getStaticProps_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/3 /.netlify/functions/preview-next_getStaticProps_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/3 /getStaticProps/withFallback/3 200
/foo/getStaticProps/withFallback/4 /.netlify/functions/next_getStaticProps_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/4 /.netlify/functions/preview-next_getStaticProps_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/4 /getStaticProps/withFallback/4 200
/foo/getStaticProps/withFallback/my/path/1 /.netlify/functions/next_getStaticProps_withFallback_slug 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/my/path/1 /.netlify/functions/preview-next_getStaticProps_withFallback_slug 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/my/path/1 /getStaticProps/withFallback/my/path/1 200
/foo/getStaticProps/withFallback/my/path/2 /.netlify/functions/next_getStaticProps_withFallback_slug 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/my/path/2 /.netlify/functions/preview-next_getStaticProps_withFallback_slug 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/my/path/2 /getStaticProps/withFallback/my/path/2 200
/foo/getStaticProps/withFallbackBlocking/3 /.netlify/functions/next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallbackBlocking/3 /.netlify/functions/preview-next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallbackBlocking/3 /getStaticProps/withFallbackBlocking/3 200
/foo/getStaticProps/withFallbackBlocking/4 /.netlify/functions/next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallbackBlocking/4 /.netlify/functions/preview-next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallbackBlocking/4 /getStaticProps/withFallbackBlocking/4 200
/foo/getStaticProps/withRevalidate/1 /.netlify/functions/next_getStaticProps_withRevalidate_id 200
/foo/getStaticProps/withRevalidate/2 /.netlify/functions/next_getStaticProps_withRevalidate_id 200
/foo/getStaticProps/withRevalidate/withFallback/1 /.netlify/functions/preview-next_getStaticProps_withRevalidate_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withRevalidate/withFallback/1 /getStaticProps/withRevalidate/withFallback/1 200
/foo/getStaticProps/withRevalidate/withFallback/2 /.netlify/functions/preview-next_getStaticProps_withRevalidate_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withRevalidate/withFallback/2 /getStaticProps/withRevalidate/withFallback/2 200
/foo/static /static 200
/getServerSideProps/static /foo/getServerSideProps/static 301!
/getStaticProps/with-revalidate /foo/getStaticProps/with-revalidate 301!
Expand All @@ -65,9 +71,13 @@ exports[`Routing creates Netlify redirects 1`] = `
/foo/getServerSideProps/all /.netlify/functions/next_getServerSideProps_all_slug 200
/foo/getServerSideProps/all/* /.netlify/functions/next_getServerSideProps_all_slug 200
/foo/getServerSideProps/:id /.netlify/functions/next_getServerSideProps_id 200
/foo/getStaticProps/withFallback/:id /.netlify/functions/preview-next_getStaticProps_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/:id /.netlify/functions/next_getStaticProps_withFallback_id 200
/foo/getStaticProps/withFallback/:slug/* /.netlify/functions/preview-next_getStaticProps_withFallback_slug 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallback/:slug/* /.netlify/functions/next_getStaticProps_withFallback_slug 200
/foo/getStaticProps/withFallbackBlocking/:id /.netlify/functions/preview-next_getStaticProps_withFallbackBlocking_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withFallbackBlocking/:id /.netlify/functions/next_getStaticProps_withFallbackBlocking_id 200
/foo/getStaticProps/withRevalidate/withFallback/:id /.netlify/functions/preview-next_getStaticProps_withRevalidate_withFallback_id 200! Cookie=__prerender_bypass,__next_preview_data
/foo/getStaticProps/withRevalidate/withFallback/:id /.netlify/functions/next_getStaticProps_withRevalidate_withFallback_id 200
/foo/shows/:id /.netlify/functions/next_shows_id 200
/foo/shows/:params/* /.netlify/functions/next_shows_params 200
Expand Down
Loading