Skip to content

Commit 6817cc9

Browse files
fix: new ISR cache handling to resolve regression in 13.4 (#2165)
Co-authored-by: Nick Taylor <[email protected]>
1 parent ffcae54 commit 6817cc9

File tree

6 files changed

+17
-164
lines changed

6 files changed

+17
-164
lines changed

packages/runtime/src/helpers/dev.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@ import type { OnPreBuild } from '@netlify/build'
44
import execa from 'execa'
55

66
import { writeDevEdgeFunction } from './edge'
7-
import { patchNextFiles } from './files'
87

98
// The types haven't been updated yet
109
export const onPreDev: OnPreBuild = async ({ constants, netlifyConfig }) => {
1110
const base = netlifyConfig.build.base ?? process.cwd()
1211

13-
// Need to patch the files, because build might not have been run
14-
await patchNextFiles(base)
15-
1612
await writeDevEdgeFunction(constants)
1713
// Don't await this or it will never finish
1814
execa.node(

packages/runtime/src/helpers/files.ts

Lines changed: 1 addition & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,7 @@ import { cpus } from 'os'
22

33
import type { NetlifyConfig } from '@netlify/build'
44
import { yellowBright } from 'chalk'
5-
import {
6-
existsSync,
7-
readJson,
8-
move,
9-
copy,
10-
writeJson,
11-
readFile,
12-
writeFile,
13-
ensureDir,
14-
readFileSync,
15-
remove,
16-
} from 'fs-extra'
5+
import { existsSync, readJson, move, copy, writeJson, ensureDir, readFileSync, remove } from 'fs-extra'
176
import globby from 'globby'
187
import { PrerenderManifest } from 'next/dist/build'
198
import { outdent } from 'outdent'
@@ -297,45 +286,6 @@ export const moveStaticPages = async ({
297286
}
298287
}
299288

300-
const PATCH_WARNING = `/* File patched by Netlify */`
301-
302-
/**
303-
* Attempt to patch a source file, preserving a backup
304-
*/
305-
const patchFile = async ({
306-
file,
307-
replacements,
308-
}: {
309-
file: string
310-
replacements: Array<[from: string, to: string]>
311-
}): Promise<boolean> => {
312-
if (!existsSync(file)) {
313-
console.warn('File was not found')
314-
return false
315-
}
316-
let content = await readFile(file, 'utf8')
317-
318-
// If the file has already been patched, patch the backed-up original instead
319-
if (content.includes(PATCH_WARNING) && existsSync(`${file}.orig`)) {
320-
content = await readFile(`${file}.orig`, 'utf8')
321-
}
322-
323-
const newContent = replacements.reduce((acc, [from, to]) => {
324-
if (acc.includes(to)) {
325-
console.log('Already patched. Skipping.')
326-
return acc
327-
}
328-
return acc.replace(from, to)
329-
}, content)
330-
if (newContent === content) {
331-
console.warn('File was not changed')
332-
return false
333-
}
334-
await writeFile(`${file}.orig`, content)
335-
await writeFile(file, `${newContent}\n${PATCH_WARNING}`)
336-
console.log('Done')
337-
return true
338-
}
339289
/**
340290
* The file we need has moved around a bit over the past few versions,
341291
* so we iterate through the options until we find it
@@ -386,82 +336,6 @@ export const getDependenciesOfFile = async (file: string) => {
386336
return dependencies.files.map((dep) => resolve(dirname(file), dep))
387337
}
388338

389-
const baseServerReplacements: Array<[string, string]> = [
390-
// force manual revalidate during cache fetches
391-
// for more info https://github.com/netlify/next-runtime/pull/1541
392-
[
393-
`checkIsManualRevalidate(req, this.renderOpts.previewProps)`,
394-
`checkIsManualRevalidate(process.env._REVALIDATE_SSG ? { headers: { 'x-prerender-revalidate': this.renderOpts.previewProps.previewModeId } } : req, this.renderOpts.previewProps)`,
395-
],
396-
// In https://github.com/vercel/next.js/pull/47803 checkIsManualRevalidate was renamed to checkIsOnDemandRevalidate
397-
[
398-
`checkIsOnDemandRevalidate(req, this.renderOpts.previewProps)`,
399-
`checkIsOnDemandRevalidate(process.env._REVALIDATE_SSG ? { headers: { 'x-prerender-revalidate': this.renderOpts.previewProps.previewModeId } } : req, this.renderOpts.previewProps)`,
400-
],
401-
// format of checkIsOnDemandRevalidate changed in 13.3.4
402-
[
403-
'checkIsOnDemandRevalidate)(req, this.renderOpts.previewProps)',
404-
'checkIsOnDemandRevalidate)(process.env._REVALIDATE_SSG ? { headers: { "x-prerender-revalidate": this.renderOpts.previewProps.previewModeId } } : req, this.renderOpts.previewProps)',
405-
],
406-
// ensure ISR 404 pages send the correct SWR cache headers
407-
[`private: isPreviewMode || is404Page && cachedData`, `private: isPreviewMode && cachedData`],
408-
]
409-
410-
const nextServerReplacements: Array<[string, string]> = [
411-
[
412-
`getMiddlewareManifest() {\n if (this.minimalMode) return null;`,
413-
`getMiddlewareManifest() {\n if (this.minimalMode || (!process.env.NETLIFY_DEV && process.env.NEXT_DISABLE_NETLIFY_EDGE !== 'true' && process.env.NEXT_DISABLE_NETLIFY_EDGE !== '1')) return null;`,
414-
],
415-
[
416-
`generateCatchAllMiddlewareRoute(devReady) {\n if (this.minimalMode) return []`,
417-
`generateCatchAllMiddlewareRoute(devReady) {\n if (this.minimalMode || (!process.env.NETLIFY_DEV && process.env.NEXT_DISABLE_NETLIFY_EDGE !== 'true' && process.env.NEXT_DISABLE_NETLIFY_EDGE !== '1')) return [];`,
418-
],
419-
[
420-
`generateCatchAllMiddlewareRoute() {\n if (this.minimalMode) return undefined;`,
421-
`generateCatchAllMiddlewareRoute() {\n if (this.minimalMode || (!process.env.NETLIFY_DEV && process.env.NEXT_DISABLE_NETLIFY_EDGE !== 'true' && process.env.NEXT_DISABLE_NETLIFY_EDGE !== '1')) return undefined;`,
422-
],
423-
[
424-
`getMiddlewareManifest() {\n if (this.minimalMode) {`,
425-
`getMiddlewareManifest() {\n if (!this.minimalMode && (!process.env.NETLIFY_DEV && process.env.NEXT_DISABLE_NETLIFY_EDGE === 'true' || process.env.NEXT_DISABLE_NETLIFY_EDGE === '1')) {`,
426-
],
427-
]
428-
429-
export const patchNextFiles = async (root: string): Promise<void> => {
430-
const baseServerFile = getServerFile(root)
431-
console.log(`Patching ${baseServerFile}`)
432-
if (baseServerFile) {
433-
await patchFile({
434-
file: baseServerFile,
435-
replacements: baseServerReplacements,
436-
})
437-
}
438-
439-
const nextServerFile = getServerFile(root, false)
440-
console.log(`Patching ${nextServerFile}`)
441-
if (nextServerFile) {
442-
await patchFile({
443-
file: nextServerFile,
444-
replacements: nextServerReplacements,
445-
})
446-
}
447-
}
448-
449-
export const unpatchFile = async (file: string): Promise<void> => {
450-
const origFile = `${file}.orig`
451-
if (existsSync(origFile)) {
452-
await move(origFile, file, { overwrite: true })
453-
}
454-
}
455-
456-
export const unpatchNextFiles = async (root: string): Promise<void> => {
457-
const baseServerFile = getServerFile(root)
458-
await unpatchFile(baseServerFile)
459-
const nextServerFile = getServerFile(root, false)
460-
if (nextServerFile !== baseServerFile) {
461-
await unpatchFile(nextServerFile)
462-
}
463-
}
464-
465339
export const movePublicFiles = async ({
466340
appDir,
467341
outdir,

packages/runtime/src/index.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from './helpers/config'
1818
import { onPreDev } from './helpers/dev'
1919
import { writeEdgeFunctions, loadMiddlewareManifest, cleanupEdgeFunctions } from './helpers/edge'
20-
import { moveStaticPages, movePublicFiles, patchNextFiles, removeMetadataFiles } from './helpers/files'
20+
import { moveStaticPages, movePublicFiles, removeMetadataFiles } from './helpers/files'
2121
import { splitApiRoutes } from './helpers/flags'
2222
import {
2323
generateFunctions,
@@ -184,8 +184,6 @@ const plugin: NetlifyPlugin = {
184184

185185
await movePublicFiles({ appDir, outdir, publish, basePath })
186186

187-
await patchNextFiles(appDir)
188-
189187
if (!destr(process.env.SERVE_STATIC_FILES_FROM_ORIGIN)) {
190188
await moveStaticPages({ target, netlifyConfig, i18n, basePath })
191189
}

packages/runtime/src/templates/getHandler.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@ const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mod
6969

7070
// We don't want to write ISR files to disk in the lambda environment
7171
conf.experimental.isrFlushToDisk = false
72-
// This is our flag that we use when patching the source
73-
// eslint-disable-next-line no-underscore-dangle
74-
process.env._REVALIDATE_SSG = 'true'
7572
for (const [key, value] of Object.entries(conf.env)) {
7673
process.env[key] = String(value)
7774
}

packages/runtime/src/templates/server.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { PrerenderManifest } from 'next/dist/build'
22
import type { BaseNextResponse } from 'next/dist/server/base-http'
3-
import { NodeRequestHandler, Options } from 'next/dist/server/next-server'
3+
import type { NodeRequestHandler, Options } from 'next/dist/server/next-server'
44

55
import {
66
netlifyApiFetch,
@@ -42,6 +42,7 @@ const getNetlifyNextServer = (NextServer: NextServerType) => {
4242
// conditionally use the prebundled React module
4343
this.netlifyPrebundleReact(url)
4444

45+
// intercept on-demand revalidation requests and handle with the Netlify API
4546
if (headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) {
4647
// handle on-demand revalidation by purging the ODB cache
4748
await this.netlifyRevalidate(url)
@@ -50,9 +51,20 @@ const getNetlifyNextServer = (NextServer: NextServerType) => {
5051
res.statusCode = 200
5152
res.setHeader('x-nextjs-cache', 'REVALIDATED')
5253
res.send()
53-
} else {
54-
return handler(req, res, parsedUrl)
54+
return
5555
}
56+
57+
// force Next to revalidate all requests so that we always have fresh content
58+
// for our ODBs and middleware is disabled at the origin
59+
// but ignore in preview mode (prerender_bypass is set to true in preview mode)
60+
// because otherwise revalidate will override preview mode
61+
if (!headers.cookie?.includes('__prerender_bypass')) {
62+
// this header controls whether Next.js will revalidate the page
63+
// and needs to be set to the preview mode id to enable it
64+
headers['x-prerender-revalidate'] = this.renderOpts.previewProps.previewModeId
65+
}
66+
67+
return handler(req, res, parsedUrl)
5668
}
5769
}
5870

test/helpers/files.spec.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import {
88
stripLocale,
99
matchesRedirect,
1010
matchesRewrite,
11-
patchNextFiles,
12-
unpatchNextFiles,
1311
getDependenciesOfFile,
1412
getSourceFileForPage,
1513
} from '../../packages/runtime/src/helpers/files'
@@ -189,28 +187,6 @@ describe('files utility functions', () => {
189187
})
190188
})
191189

192-
describeCwdTmpDir('file patching', () => {
193-
it('patches Next server files', async () => {
194-
// Testing to make sure that the patching functionality works within base-server.js and next-server.js files
195-
const root = path.resolve(dirname(resolve(__dirname, '..')))
196-
await copy(join(root, 'package.json'), path.join(process.cwd(), 'package.json'))
197-
await ensureDir(path.join(process.cwd(), 'node_modules'))
198-
await copy(path.join(root, 'node_modules', 'next'), path.join(process.cwd(), 'node_modules', 'next'))
199-
200-
await patchNextFiles(process.cwd())
201-
const serverFile = path.resolve(process.cwd(), 'node_modules', 'next', 'dist', 'server', 'base-server.js')
202-
const patchedData = await readFileSync(serverFile, 'utf8')
203-
expect(patchedData.includes('_REVALIDATE_SSG')).toBeTruthy()
204-
expect(patchedData.includes('private: isPreviewMode && cachedData')).toBeTruthy()
205-
206-
await unpatchNextFiles(process.cwd())
207-
208-
const unPatchedData = await readFileSync(serverFile, 'utf8')
209-
expect(unPatchedData.includes('_REVALIDATE_SSG')).toBeFalsy()
210-
expect(unPatchedData.includes('private: isPreviewMode && cachedData')).toBeFalsy()
211-
})
212-
})
213-
214190
describe('dependency tracing', () => {
215191
it('generates dependency list from a source file', async () => {
216192
const dependencies = await getDependenciesOfFile(resolve(__dirname, '../fixtures/analysis/background.js'))

0 commit comments

Comments
 (0)