diff --git a/demos/default/pages/getStaticProps/withRevalidate/withFallbackBlocking/[id].js b/demos/default/pages/getStaticProps/withRevalidate/withFallbackBlocking/[id].js
new file mode 100644
index 0000000000..38058f5d42
--- /dev/null
+++ b/demos/default/pages/getStaticProps/withRevalidate/withFallbackBlocking/[id].js
@@ -0,0 +1,45 @@
+import Link from 'next/link'
+
+const Show = ({ show, time }) => (
+
+
This page uses getStaticProps() to pre-fetch a TV show.
+
Ids 1 and 2 are prerendered
+
+
+
Show #{show.id}
+
{show.name}
+
Rendered at {time}
+
+
+
+
Go back home
+
+
+)
+
+export async function getStaticPaths() {
+ // Set the paths we want to pre-render
+ const paths = [{ params: { id: '1' } }, { params: { id: '2' } }]
+
+ // We'll pre-render only these paths at build time.
+
+ return { paths, fallback: 'blocking' }
+}
+
+export async function getStaticProps({ params }) {
+ // The ID to render
+ const { id } = params
+
+ const res = await fetch(`https://api.tvmaze.com/shows/${id}`)
+ const data = await res.json()
+ const time = new Date().toLocaleTimeString()
+ return {
+ props: {
+ show: data,
+ time,
+ },
+ revalidate: 60,
+ }
+}
+
+export default Show
diff --git a/src/helpers/files.ts b/src/helpers/files.ts
index 90d0eaa79e..757f5abca1 100644
--- a/src/helpers/files.ts
+++ b/src/helpers/files.ts
@@ -15,6 +15,7 @@ import { MINIMUM_REVALIDATE_SECONDS, DIVIDER } from '../constants'
import { NextConfig } from './config'
import { Rewrites, RoutesManifest } from './types'
+import { findModuleFromBase } from './utils'
const TEST_ROUTE = /(|\/)\[[^/]+?](\/|\.html|$)/
@@ -272,47 +273,56 @@ export const moveStaticPages = async ({
}
}
-const patchFile = async ({ file, from, to }: { file: string; from: string; to: string }): Promise => {
+/**
+ * Attempt to patch a source file, preserving a backup
+ */
+const patchFile = async ({ file, from, to }: { file: string; from: string; to: string }): Promise => {
if (!existsSync(file)) {
- return
+ console.warn('File was not found')
+
+ return false
}
const content = await readFile(file, 'utf8')
if (content.includes(to)) {
- return
+ console.log('File already patched')
+ return false
}
const newContent = content.replace(from, to)
+ if (newContent === content) {
+ console.warn('File was not changed')
+ return false
+ }
await writeFile(`${file}.orig`, content)
await writeFile(file, newContent)
+ console.log('Done')
+ return true
}
+/**
+ * The file we need has moved around a bit over the past few versions,
+ * so we iterate through the options until we find it
+ */
const getServerFile = (root) => {
- let serverFile
- try {
- serverFile = require.resolve('next/dist/server/next-server', { paths: [root] })
- } catch {
- // Ignore
- }
- if (!serverFile) {
- try {
- // eslint-disable-next-line node/no-missing-require
- serverFile = require.resolve('next/dist/next-server/server/next-server', { paths: [root] })
- } catch {
- // Ignore
- }
- }
- return serverFile
+ const candidates = [
+ 'next/dist/server/base-server',
+ 'next/dist/server/next-server',
+ 'next/dist/next-server/server/next-server',
+ ]
+
+ return findModuleFromBase({ candidates, paths: [root] })
}
-export const patchNextFiles = async (root: string): Promise => {
+export const patchNextFiles = (root: string): Promise | boolean => {
const serverFile = getServerFile(root)
console.log(`Patching ${serverFile}`)
if (serverFile) {
- await patchFile({
+ return patchFile({
file: serverFile,
from: `let ssgCacheKey = `,
to: `let ssgCacheKey = process.env._BYPASS_SSG || `,
})
}
+ return false
}
export const unpatchNextFiles = async (root: string): Promise => {
diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts
index cb46b75ebe..f4a94ab5df 100644
--- a/src/helpers/utils.ts
+++ b/src/helpers/utils.ts
@@ -157,3 +157,20 @@ export const shouldSkip = (): boolean =>
process.env.NEXT_PLUGIN_FORCE_RUN === '0' ||
process.env.NETLIFY_NEXT_PLUGIN_SKIP === 'true' ||
process.env.NETLIFY_NEXT_PLUGIN_SKIP === '1'
+
+/**
+ * Given an array of base paths and candidate modules, return the first one that exists
+ */
+export const findModuleFromBase = ({ paths, candidates }): string | null => {
+ for (const candidate of candidates) {
+ try {
+ const modulePath = require.resolve(candidate, { paths })
+ if (modulePath) {
+ return modulePath
+ }
+ } catch (error) {
+ console.error(error)
+ }
+ }
+ return null
+}
diff --git a/test/__snapshots__/index.js.snap b/test/__snapshots__/index.js.snap
index 91e49adb40..8f2e0888af 100644
--- a/test/__snapshots__/index.js.snap
+++ b/test/__snapshots__/index.js.snap
@@ -29,6 +29,7 @@ exports.resolvePages = () => {
require.resolve('../../../.next/server/pages/getStaticProps/withFallbackBlocking/[id].js')
require.resolve('../../../.next/server/pages/getStaticProps/withRevalidate/[id].js')
require.resolve('../../../.next/server/pages/getStaticProps/withRevalidate/withFallback/[id].js')
+ require.resolve('../../../.next/server/pages/getStaticProps/withRevalidate/withFallbackBlocking/[id].js')
require.resolve('../../../.next/server/pages/index.js')
require.resolve('../../../.next/server/pages/middle/_middleware.js')
require.resolve('../../../.next/server/pages/previewTest.js')
@@ -67,6 +68,7 @@ exports.resolvePages = () => {
require.resolve('../../../.next/server/pages/getStaticProps/withFallbackBlocking/[id].js')
require.resolve('../../../.next/server/pages/getStaticProps/withRevalidate/[id].js')
require.resolve('../../../.next/server/pages/getStaticProps/withRevalidate/withFallback/[id].js')
+ require.resolve('../../../.next/server/pages/getStaticProps/withRevalidate/withFallbackBlocking/[id].js')
require.resolve('../../../.next/server/pages/index.js')
require.resolve('../../../.next/server/pages/middle/_middleware.js')
require.resolve('../../../.next/server/pages/previewTest.js')
@@ -105,6 +107,7 @@ exports.resolvePages = () => {
require.resolve('../../../web/.next/server/pages/getStaticProps/withFallbackBlocking/[id].js')
require.resolve('../../../web/.next/server/pages/getStaticProps/withRevalidate/[id].js')
require.resolve('../../../web/.next/server/pages/getStaticProps/withRevalidate/withFallback/[id].js')
+ require.resolve('../../../web/.next/server/pages/getStaticProps/withRevalidate/withFallbackBlocking/[id].js')
require.resolve('../../../web/.next/server/pages/index.js')
require.resolve('../../../web/.next/server/pages/middle/_middleware.js')
require.resolve('../../../web/.next/server/pages/previewTest.js')
@@ -143,6 +146,7 @@ exports.resolvePages = () => {
require.resolve('../../../web/.next/server/pages/getStaticProps/withFallbackBlocking/[id].js')
require.resolve('../../../web/.next/server/pages/getStaticProps/withRevalidate/[id].js')
require.resolve('../../../web/.next/server/pages/getStaticProps/withRevalidate/withFallback/[id].js')
+ require.resolve('../../../web/.next/server/pages/getStaticProps/withRevalidate/withFallbackBlocking/[id].js')
require.resolve('../../../web/.next/server/pages/index.js')
require.resolve('../../../web/.next/server/pages/middle/_middleware.js')
require.resolve('../../../web/.next/server/pages/previewTest.js')
@@ -647,6 +651,30 @@ Array [
"status": 200,
"to": "/.netlify/builders/___netlify-odb-handler",
},
+ Object {
+ "force": true,
+ "from": "/_next/data/build-id/en/getStaticProps/withRevalidate/withFallbackBlocking/1.json",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
+ Object {
+ "force": true,
+ "from": "/getStaticProps/withRevalidate/withFallbackBlocking/1",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
+ Object {
+ "force": true,
+ "from": "/_next/data/build-id/en/getStaticProps/withRevalidate/withFallbackBlocking/2.json",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
+ Object {
+ "force": true,
+ "from": "/getStaticProps/withRevalidate/withFallbackBlocking/2",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
Object {
"force": false,
"from": "/_next/data/build-id/en/index.json",
@@ -1367,6 +1395,42 @@ Array [
"status": 200,
"to": "/.netlify/builders/___netlify-odb-handler",
},
+ Object {
+ "force": false,
+ "from": "/_next/data/build-id/en/getStaticProps/withRevalidate/withFallbackBlocking/:id.json",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
+ Object {
+ "force": false,
+ "from": "/getStaticProps/withRevalidate/withFallbackBlocking/:id",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
+ Object {
+ "force": false,
+ "from": "/_next/data/build-id/es/getStaticProps/withRevalidate/withFallbackBlocking/:id.json",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
+ Object {
+ "force": false,
+ "from": "/es/getStaticProps/withRevalidate/withFallbackBlocking/:id",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
+ Object {
+ "force": false,
+ "from": "/_next/data/build-id/fr/getStaticProps/withRevalidate/withFallbackBlocking/:id.json",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
+ Object {
+ "force": false,
+ "from": "/fr/getStaticProps/withRevalidate/withFallbackBlocking/:id",
+ "status": 200,
+ "to": "/.netlify/builders/___netlify-odb-handler",
+ },
Object {
"force": false,
"from": "/_next/data/build-id/en/getStaticProps/withRevalidate/:id.json",
diff --git a/test/index.js b/test/index.js
index 7539a494c3..04f27bf565 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1,4 +1,4 @@
-const { writeJSON, unlink, existsSync, readFileSync, copy, ensureDir, readJson, writeFile } = require('fs-extra')
+const { writeJSON, unlink, existsSync, readFileSync, copy, ensureDir, readJson } = require('fs-extra')
const path = require('path')
const process = require('process')
const os = require('os')
@@ -10,10 +10,16 @@ const plugin = require('../src')
const { HANDLER_FUNCTION_NAME, ODB_FUNCTION_NAME } = require('../src/constants')
const { join } = require('pathe')
-const { matchMiddleware, stripLocale, matchesRedirect, matchesRewrite } = require('../src/helpers/files')
+const {
+ matchMiddleware,
+ stripLocale,
+ matchesRedirect,
+ matchesRewrite,
+ patchNextFiles,
+ unpatchNextFiles,
+} = require('../src/helpers/files')
const { dirname } = require('path')
const { getProblematicUserRewrites } = require('../src/helpers/verification')
-const { outdent } = require('outdent')
const FIXTURES_DIR = `${__dirname}/fixtures`
const SAMPLE_PROJECT_DIR = `${__dirname}/../demos/default`
@@ -659,6 +665,23 @@ describe('utility functions', () => {
expect(matchesRewrite(path, REWRITES)).toBeTruthy()
})
})
+
+ test('patches Next server files', async () => {
+ const root = path.resolve(dirname(__dirname))
+ await copy(join(root, 'package.json'), path.join(process.cwd(), 'package.json'))
+ await ensureDir(path.join(process.cwd(), 'node_modules'))
+ await copy(path.join(root, 'node_modules', 'next'), path.join(process.cwd(), 'node_modules', 'next'))
+
+ expect(await patchNextFiles(process.cwd())).toBeTruthy()
+ const serverFile = path.resolve(process.cwd(), 'node_modules', 'next', 'dist', 'server', 'base-server.js')
+ const patchedData = await readFileSync(serverFile, 'utf8')
+ expect(patchedData.includes('_BYPASS_SSG')).toBeTruthy()
+
+ await unpatchNextFiles(process.cwd())
+
+ const unPatchedData = await readFileSync(serverFile, 'utf8')
+ expect(unPatchedData.includes('_BYPASS_SSG')).toBeFalsy()
+ })
})
describe('function helpers', () => {