@netlify/plugin-nextjs 4.34.0 → 4.35.1-next-server-fix.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -23,16 +23,18 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.extractConfigFromFile = exports.validateConfigValue = void 0;
26
+ exports.extractConfigFromFile = exports.validateConfigValue = exports.isEdgeConfig = void 0;
27
27
  const fs_1 = __importStar(require("fs"));
28
28
  const pathe_1 = require("pathe");
29
+ const isEdgeConfig = (config) => ['experimental-edge', 'edge'].includes(config);
30
+ exports.isEdgeConfig = isEdgeConfig;
29
31
  const validateConfigValue = (config, apiFilePath) => {
30
32
  if (config.type === "experimental-scheduled" /* ApiRouteType.SCHEDULED */) {
31
33
  if (!config.schedule) {
32
34
  console.error(`Invalid config value in ${(0, pathe_1.relative)(process.cwd(), apiFilePath)}: schedule is required when type is "${"experimental-scheduled" /* ApiRouteType.SCHEDULED */}"`);
33
35
  return false;
34
36
  }
35
- if (config.runtime === 'experimental-edge') {
37
+ if ((0, exports.isEdgeConfig)(config.runtime)) {
36
38
  console.error(`Invalid config value in ${(0, pathe_1.relative)(process.cwd(), apiFilePath)}: edge runtime is not supported for scheduled functions`);
37
39
  return false;
38
40
  }
@@ -43,7 +45,7 @@ const validateConfigValue = (config, apiFilePath) => {
43
45
  console.error(`Invalid config value in ${(0, pathe_1.relative)(process.cwd(), apiFilePath)}: schedule is not allowed unless type is "${"experimental-scheduled" /* ApiRouteType.SCHEDULED */}"`);
44
46
  return false;
45
47
  }
46
- if (config.type && config.runtime === 'experimental-edge') {
48
+ if (config.type && (0, exports.isEdgeConfig)(config.runtime)) {
47
49
  console.error(`Invalid config value in ${(0, pathe_1.relative)(process.cwd(), apiFilePath)}: edge runtime is not supported for background functions`);
48
50
  return false;
49
51
  }
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.writeEdgeFunctions = exports.getEdgeFunctionPatternForPage = exports.writeRscDataEdgeFunction = exports.writeDevEdgeFunction = exports.cleanupEdgeFunctions = exports.loadPrerenderManifest = exports.loadAppPathRoutesManifest = exports.loadMiddlewareManifest = exports.isAppDirRoute = void 0;
6
+ exports.writeEdgeFunctions = exports.getEdgeFunctionPatternForPage = exports.generateRscDataEdgeManifest = exports.writeDevEdgeFunction = exports.cleanupEdgeFunctions = exports.loadPrerenderManifest = exports.loadAppPathRoutesManifest = exports.loadMiddlewareManifest = exports.isAppDirRoute = void 0;
7
7
  const fs_1 = require("fs");
8
8
  const path_1 = require("path");
9
9
  const chalk_1 = require("chalk");
@@ -12,6 +12,7 @@ const fs_extra_1 = require("fs-extra");
12
12
  const outdent_1 = require("outdent");
13
13
  const constants_1 = require("../constants");
14
14
  const config_1 = require("./config");
15
+ const functionsMetaData_1 = require("./functionsMetaData");
15
16
  const matchers_1 = require("./matchers");
16
17
  const maybeLoadJson = (path) => {
17
18
  if ((0, fs_1.existsSync)(path)) {
@@ -101,19 +102,21 @@ const generateEdgeFunctionMiddlewareMatchers = ({ edgeFunctionDefinition, nextCo
101
102
  }
102
103
  return edgeFunctionDefinition.matchers;
103
104
  };
104
- const middlewareMatcherToEdgeFunctionDefinition = (matcher, name, cache) => {
105
+ const middlewareMatcherToEdgeFunctionDefinition = (matcher, name, generator, cache) => {
105
106
  const pattern = (0, matchers_1.transformCaptureGroups)((0, matchers_1.stripLookahead)(matcher.regexp));
106
- return { function: name, pattern, name, cache };
107
+ return { function: name, pattern, name, cache, generator };
107
108
  };
108
109
  const cleanupEdgeFunctions = ({ INTERNAL_EDGE_FUNCTIONS_SRC = '.netlify/edge-functions', }) => (0, fs_extra_1.emptyDir)(INTERNAL_EDGE_FUNCTIONS_SRC);
109
110
  exports.cleanupEdgeFunctions = cleanupEdgeFunctions;
110
111
  const writeDevEdgeFunction = async ({ INTERNAL_EDGE_FUNCTIONS_SRC = '.netlify/edge-functions', }) => {
112
+ const generator = await (0, functionsMetaData_1.getPluginVersion)();
111
113
  const manifest = {
112
114
  functions: [
113
115
  {
114
116
  function: 'next-dev',
115
117
  name: 'netlify dev handler',
116
118
  path: '/*',
119
+ generator,
117
120
  },
118
121
  ],
119
122
  version: 1,
@@ -130,19 +133,20 @@ exports.writeDevEdgeFunction = writeDevEdgeFunction;
130
133
  /**
131
134
  * Writes an edge function that routes RSC data requests to the `.rsc` route
132
135
  */
133
- const writeRscDataEdgeFunction = async ({ prerenderManifest, appPathRoutesManifest, }) => {
136
+ const generateRscDataEdgeManifest = async ({ prerenderManifest, appPathRoutesManifest, }) => {
137
+ const generator = await (0, functionsMetaData_1.getPluginVersion)();
134
138
  if (!prerenderManifest || !appPathRoutesManifest) {
135
139
  return [];
136
140
  }
137
141
  const staticAppdirRoutes = [];
138
142
  for (const [path, route] of Object.entries(prerenderManifest.routes)) {
139
- if ((0, exports.isAppDirRoute)(route.srcRoute, appPathRoutesManifest)) {
143
+ if ((0, exports.isAppDirRoute)(route.srcRoute, appPathRoutesManifest) && route.dataRoute) {
140
144
  staticAppdirRoutes.push(path, route.dataRoute);
141
145
  }
142
146
  }
143
147
  const dynamicAppDirRoutes = [];
144
148
  for (const [path, route] of Object.entries(prerenderManifest.dynamicRoutes)) {
145
- if ((0, exports.isAppDirRoute)(path, appPathRoutesManifest)) {
149
+ if ((0, exports.isAppDirRoute)(path, appPathRoutesManifest) && route.dataRouteRegex) {
146
150
  dynamicAppDirRoutes.push(route.routeRegex, route.dataRouteRegex);
147
151
  }
148
152
  }
@@ -157,15 +161,17 @@ const writeRscDataEdgeFunction = async ({ prerenderManifest, appPathRoutesManife
157
161
  function: 'rsc-data',
158
162
  name: 'RSC data routing',
159
163
  path,
164
+ generator,
160
165
  })),
161
166
  ...dynamicAppDirRoutes.map((pattern) => ({
162
167
  function: 'rsc-data',
163
168
  name: 'RSC data routing',
164
169
  pattern,
170
+ generator,
165
171
  })),
166
172
  ];
167
173
  };
168
- exports.writeRscDataEdgeFunction = writeRscDataEdgeFunction;
174
+ exports.generateRscDataEdgeManifest = generateRscDataEdgeManifest;
169
175
  const getEdgeFunctionPatternForPage = ({ edgeFunctionDefinition, pageRegexMap, appPathRoutesManifest, }) => {
170
176
  // We don't just use the matcher from the edge function definition, because it doesn't handle trailing slashes
171
177
  var _a, _b;
@@ -187,6 +193,7 @@ exports.getEdgeFunctionPatternForPage = getEdgeFunctionPatternForPage;
187
193
  // eslint-disable-next-line max-lines-per-function
188
194
  const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
189
195
  var _a;
196
+ const generator = await (0, functionsMetaData_1.getPluginVersion)();
190
197
  const manifest = {
191
198
  functions: [],
192
199
  layers: [],
@@ -206,7 +213,7 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
206
213
  console.log('Environment variable NEXT_DISABLE_NETLIFY_EDGE has been set, skipping Netlify Edge Function creation.');
207
214
  return;
208
215
  }
209
- const rscFunctions = await (0, exports.writeRscDataEdgeFunction)({
216
+ const rscFunctions = await (0, exports.generateRscDataEdgeManifest)({
210
217
  prerenderManifest: await (0, exports.loadPrerenderManifest)(netlifyConfig),
211
218
  appPathRoutesManifest: await (0, exports.loadAppPathRoutesManifest)(netlifyConfig),
212
219
  });
@@ -234,7 +241,7 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
234
241
  matchers,
235
242
  middleware: true,
236
243
  });
237
- manifest.functions.push(...matchers.map((matcher) => middlewareMatcherToEdgeFunctionDefinition(matcher, functionName)));
244
+ manifest.functions.push(...matchers.map((matcher) => middlewareMatcherToEdgeFunctionDefinition(matcher, functionName, generator)));
238
245
  }
239
246
  // Functions (i.e. not middleware, but edge SSR and API routes)
240
247
  if (typeof middlewareManifest.functions === 'object') {
@@ -267,6 +274,7 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
267
274
  pattern,
268
275
  // cache: "manual" is currently experimental, so we restrict it to sites that use experimental appDir
269
276
  cache: usesAppDir ? 'manual' : undefined,
277
+ generator,
270
278
  });
271
279
  // pages-dir page routes also have a data route. If there's a match, add an entry mapping that to the function too
272
280
  const dataRoute = dataRoutesMap.get(edgeFunctionDefinition.page);
@@ -276,6 +284,7 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
276
284
  name: edgeFunctionDefinition.name,
277
285
  pattern: dataRoute,
278
286
  cache: usesAppDir ? 'manual' : undefined,
287
+ generator,
279
288
  });
280
289
  }
281
290
  }
@@ -293,6 +302,7 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
293
302
  function: 'ipx',
294
303
  name: 'next/image handler',
295
304
  path: nextConfig.images.path || '/_next/image',
305
+ generator,
296
306
  });
297
307
  manifest.layers.push({
298
308
  name: 'https://ipx-edge-function-layer.netlify.app/mod.ts',
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.movePublicFiles = exports.unpatchNextFiles = exports.unpatchFile = exports.patchNextFiles = exports.getDependenciesOfFile = exports.getSourceFileForPage = exports.moveStaticPages = exports.getMiddleware = exports.matchesRewrite = exports.matchesRedirect = exports.matchMiddleware = exports.stripLocale = exports.isDynamicRoute = void 0;
6
+ exports.movePublicFiles = exports.unpatchNextFiles = exports.unpatchFile = exports.patchNextFiles = exports.getDependenciesOfFile = exports.getSourceFileForPage = exports.getNextServerModulePath = exports.moveStaticPages = exports.getMiddleware = exports.matchesRewrite = exports.matchesRedirect = exports.matchMiddleware = exports.stripLocale = exports.isDynamicRoute = void 0;
7
7
  const os_1 = require("os");
8
8
  const chalk_1 = require("chalk");
9
9
  const fs_extra_1 = require("fs-extra");
@@ -278,6 +278,54 @@ const getServerFile = (root, includeBase = true) => {
278
278
  }
279
279
  return (0, utils_1.findModuleFromBase)({ candidates, paths: [root] });
280
280
  };
281
+ /**
282
+ * Try to find next-server module in few locations (to support different next versions) and in few context (try to resolve from app location and from this module)
283
+ */
284
+ const getNextServerModulePath = (root) => {
285
+ // first let's try to use app location directory to find next-server
286
+ try {
287
+ const nextServerModuleLocation = getServerFile(root, false);
288
+ if (nextServerModuleLocation) {
289
+ return nextServerModuleLocation;
290
+ }
291
+ }
292
+ catch (error) {
293
+ if (!error.message.includes('Cannot find module')) {
294
+ // A different error, so rethrow it
295
+ throw error;
296
+ }
297
+ }
298
+ // if we didn't find it, let's try to resolve "next" package from this module
299
+ try {
300
+ // next >= 11.0.1. Yay breaking changes in patch releases!
301
+ const nextServerModuleLocation = require.resolve('next/dist/server/next-server');
302
+ if (nextServerModuleLocation) {
303
+ return nextServerModuleLocation;
304
+ }
305
+ }
306
+ catch (error) {
307
+ if (!error.message.includes("Cannot find module 'next/dist/server/next-server'")) {
308
+ // A different error, so rethrow it
309
+ throw error;
310
+ }
311
+ // Probably an old version of next, so fall through and find it elsewhere.
312
+ }
313
+ try {
314
+ // next < 11.0.1
315
+ // eslint-disable-next-line n/no-missing-require
316
+ const nextServerModuleLocation = require.resolve('next/dist/next-server/server/next-server');
317
+ if (nextServerModuleLocation) {
318
+ return nextServerModuleLocation;
319
+ }
320
+ }
321
+ catch (error) {
322
+ if (!error.message.includes("Cannot find module 'next/dist/next-server/server/next-server'")) {
323
+ throw error;
324
+ }
325
+ }
326
+ return null;
327
+ };
328
+ exports.getNextServerModulePath = getNextServerModulePath;
281
329
  /**
282
330
  * Find the source file for a given page route
283
331
  */
@@ -23,9 +23,13 @@ const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS
23
23
  const functionsDir = (0, pathe_1.resolve)(INTERNAL_FUNCTIONS_SRC || FUNCTIONS_SRC);
24
24
  const functionDir = (0, pathe_1.join)(functionsDir, constants_1.HANDLER_FUNCTION_NAME);
25
25
  const publishDir = (0, pathe_1.relative)(functionDir, publish);
26
+ const nextServerModuleAbsoluteLocation = (0, files_1.getNextServerModulePath)(appDir);
27
+ const nextServerModuleRelativeLocation = nextServerModuleAbsoluteLocation
28
+ ? (0, pathe_1.relative)(functionDir, nextServerModuleAbsoluteLocation)
29
+ : undefined;
26
30
  for (const { route, config, compiled } of apiRoutes) {
27
31
  // Don't write a lambda if the runtime is edge
28
- if (config.runtime === 'experimental-edge') {
32
+ if ((0, analysis_1.isEdgeConfig)(config.runtime)) {
29
33
  continue;
30
34
  }
31
35
  const apiHandlerSource = await (0, getApiHandler_1.getApiHandler)({
@@ -33,6 +37,7 @@ const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS
33
37
  config,
34
38
  publishDir,
35
39
  appDir: (0, pathe_1.relative)(functionDir, appDir),
40
+ nextServerModuleRelativeLocation,
36
41
  });
37
42
  const functionName = (0, utils_1.getFunctionNameForPage)(route, config.type === "experimental-background" /* ApiRouteType.BACKGROUND */);
38
43
  await (0, fs_extra_1.ensureDir)((0, pathe_1.join)(functionsDir, functionName));
@@ -51,7 +56,12 @@ const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS
51
56
  await (0, fs_extra_1.writeFile)((0, pathe_1.join)(functionsDir, functionName, 'pages.js'), resolverSource);
52
57
  }
53
58
  const writeHandler = async (functionName, functionTitle, isODB) => {
54
- const handlerSource = await (0, getHandler_1.getHandler)({ isODB, publishDir, appDir: (0, pathe_1.relative)(functionDir, appDir) });
59
+ const handlerSource = await (0, getHandler_1.getHandler)({
60
+ isODB,
61
+ publishDir,
62
+ appDir: (0, pathe_1.relative)(functionDir, appDir),
63
+ nextServerModuleRelativeLocation,
64
+ });
55
65
  await (0, fs_extra_1.ensureDir)((0, pathe_1.join)(functionsDir, functionName));
56
66
  // write main handler file (standard or ODB)
57
67
  await (0, fs_extra_1.writeFile)((0, pathe_1.join)(functionsDir, functionName, `${functionName}.js`), handlerSource);
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writeFunctionConfiguration = void 0;
3
+ exports.writeFunctionConfiguration = exports.getPluginVersion = void 0;
4
4
  const fs_extra_1 = require("fs-extra");
5
5
  const pathe_1 = require("pathe");
6
6
  const constants_1 = require("../constants");
@@ -12,6 +12,17 @@ const getNextRuntimeVersion = async (packageJsonPath, useNodeModulesPath) => {
12
12
  const packagePlugin = await (0, fs_extra_1.readJSON)(packageJsonPath);
13
13
  return useNodeModulesPath ? packagePlugin.version : packagePlugin.dependencies[constants_1.NEXT_PLUGIN];
14
14
  };
15
+ const PLUGIN_PACKAGE_PATH = '.netlify/plugins/package.json';
16
+ const nextPluginVersion = async () => {
17
+ const moduleRoot = (0, config_1.resolveModuleRoot)(constants_1.NEXT_PLUGIN);
18
+ const nodeModulesPath = moduleRoot ? (0, pathe_1.join)(moduleRoot, 'package.json') : null;
19
+ return ((await getNextRuntimeVersion(nodeModulesPath, true)) ||
20
+ (await getNextRuntimeVersion(PLUGIN_PACKAGE_PATH, false)) ||
21
+ // The runtime version should always be available, but if it's not, return 'unknown'
22
+ 'unknown');
23
+ };
24
+ const getPluginVersion = async () => `${constants_1.NEXT_PLUGIN_NAME}@${await nextPluginVersion()}`;
25
+ exports.getPluginVersion = getPluginVersion;
15
26
  /**
16
27
  * Creates a function configuration file for the given function.
17
28
  *
@@ -19,17 +30,11 @@ const getNextRuntimeVersion = async (packageJsonPath, useNodeModulesPath) => {
19
30
  */
20
31
  const writeFunctionConfiguration = async (functionInfo) => {
21
32
  const { functionName, functionTitle, functionsDir } = functionInfo;
22
- const pluginPackagePath = '.netlify/plugins/package.json';
23
- const moduleRoot = (0, config_1.resolveModuleRoot)(constants_1.NEXT_PLUGIN);
24
- const nodeModulesPath = moduleRoot ? (0, pathe_1.join)(moduleRoot, 'package.json') : null;
25
- const nextPluginVersion = (await getNextRuntimeVersion(nodeModulesPath, true)) ||
26
- (await getNextRuntimeVersion(pluginPackagePath, false)) ||
27
- // The runtime version should always be available, but if it's not, return 'unknown'
28
- 'unknown';
33
+ const generator = await (0, exports.getPluginVersion)();
29
34
  const metadata = {
30
35
  config: {
31
36
  name: functionTitle,
32
- generator: `${constants_1.NEXT_PLUGIN_NAME}@${nextPluginVersion}`,
37
+ generator,
33
38
  },
34
39
  version: 1,
35
40
  };
@@ -9,9 +9,10 @@ const path = require('path');
9
9
  // eslint-disable-next-line n/prefer-global/url, n/prefer-global/url-search-params
10
10
  const { URLSearchParams, URL } = require('url');
11
11
  const { Bridge } = require('@vercel/node-bridge/bridge');
12
- const { getMultiValueHeaders, getNextServer } = require('./handlerUtils');
12
+ const { getMultiValueHeaders } = require('./handlerUtils');
13
13
  // We return a function and then call `toString()` on it to serialise it as the launcher function
14
- const makeHandler = (conf, app, pageRoot, page) => {
14
+ // eslint-disable-next-line max-params
15
+ const makeHandler = (conf, app, pageRoot, page, NextServer) => {
15
16
  var _a;
16
17
  // Change working directory into the site root, unless using Nx, which moves the
17
18
  // dist directory and handles this itself
@@ -46,7 +47,6 @@ const makeHandler = (conf, app, pageRoot, page) => {
46
47
  // Scheduled functions don't have a URL, but we need to give one so Next knows the route to serve
47
48
  const url = event.rawUrl ? new URL(event.rawUrl) : new URL(path, process.env.URL || 'http://n');
48
49
  const port = Number.parseInt(url.port) || 80;
49
- const NextServer = getNextServer();
50
50
  const nextServer = new NextServer({
51
51
  conf,
52
52
  dir,
@@ -91,13 +91,18 @@ const makeHandler = (conf, app, pageRoot, page) => {
91
91
  /**
92
92
  * Handlers for API routes are simpler than page routes, but they each have a separate one
93
93
  */
94
- const getApiHandler = ({ page, config, publishDir = '../../../.next', appDir = '../../..', }) =>
94
+ const getApiHandler = ({ page, config, publishDir = '../../../.next', appDir = '../../..', nextServerModuleRelativeLocation, }) =>
95
95
  // This is a string, but if you have the right editor plugin it should format as js
96
96
  (0, outdent_1.outdent /* javascript */) `
97
+ if (!${JSON.stringify(nextServerModuleRelativeLocation)}) {
98
+ throw new Error('Could not find Next.js server')
99
+ }
100
+
97
101
  const { Server } = require("http");
98
102
  // We copy the file here rather than requiring from the node module
99
103
  const { Bridge } = require("./bridge");
100
- const { getMultiValueHeaders, getNextServer } = require('./handlerUtils')
104
+ const { getMultiValueHeaders } = require('./handlerUtils')
105
+ const NextServer = require(${JSON.stringify(nextServerModuleRelativeLocation)}).default
101
106
 
102
107
  ${config.type === "experimental-scheduled" /* ApiRouteType.SCHEDULED */ ? `const { schedule } = require("@netlify/functions")` : ''}
103
108
 
@@ -106,7 +111,7 @@ const getApiHandler = ({ page, config, publishDir = '../../../.next', appDir = '
106
111
  let staticManifest
107
112
  const path = require("path");
108
113
  const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server"));
109
- const handler = (${makeHandler.toString()})(config, "${appDir}", pageRoot, ${JSON.stringify(page)})
114
+ const handler = (${makeHandler.toString()})(config, "${appDir}", pageRoot, ${JSON.stringify(page)}, NextServer)
110
115
  exports.handler = ${config.type === "experimental-scheduled" /* ApiRouteType.SCHEDULED */ ? `schedule(${JSON.stringify(config.schedule)}, handler);` : 'handler'}
111
116
  `;
112
117
  exports.getApiHandler = getApiHandler;
@@ -11,10 +11,10 @@ const path = require('path');
11
11
  const { URLSearchParams, URL } = require('url');
12
12
  const { Bridge } = require('@vercel/node-bridge/bridge');
13
13
  const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath, } = require('./handlerUtils');
14
- const { NetlifyNextServer } = require('./server');
14
+ const { getNetlifyNextServer } = require('./server');
15
15
  // We return a function and then call `toString()` on it to serialise it as the launcher function
16
- // eslint-disable-next-line max-params, max-lines-per-function
17
- const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') => {
16
+ // eslint-disable-next-line max-lines-per-function
17
+ const makeHandler = (conf, app, pageRoot, NextServer, staticManifest = [], mode = 'ssr') => {
18
18
  var _a;
19
19
  // Change working directory into the site root, unless using Nx, which moves the
20
20
  // dist directory and handles this itself
@@ -28,6 +28,7 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
28
28
  require.resolve('./pages.js');
29
29
  }
30
30
  catch { }
31
+ const NetlifyNextServer = getNetlifyNextServer(NextServer);
31
32
  const ONE_YEAR_IN_SECONDS = 31536000;
32
33
  (_a = process.env).NODE_ENV || (_a.NODE_ENV = 'production');
33
34
  // We don't want to write ISR files to disk in the lambda environment
@@ -59,7 +60,7 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
59
60
  hostname: url.hostname,
60
61
  port,
61
62
  }, {
62
- revalidateToken: customContext.odb_refresh_hooks,
63
+ revalidateToken: customContext === null || customContext === void 0 ? void 0 : customContext.odb_refresh_hooks,
63
64
  });
64
65
  const requestHandler = nextServer.getRequestHandler();
65
66
  const server = new Server(async (req, res) => {
@@ -137,15 +138,20 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
137
138
  };
138
139
  };
139
140
  };
140
- const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '../../..' }) =>
141
+ const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '../../..', nextServerModuleRelativeLocation, }) =>
141
142
  // This is a string, but if you have the right editor plugin it should format as js
142
143
  (0, outdent_1.outdent /* javascript */) `
144
+ if (!${JSON.stringify(nextServerModuleRelativeLocation)}) {
145
+ throw new Error('Could not find Next.js server')
146
+ }
147
+
143
148
  const { Server } = require("http");
144
149
  const { promises } = require("fs");
145
150
  // We copy the file here rather than requiring from the node module
146
151
  const { Bridge } = require("./bridge");
147
- const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, getNextServer, normalizePath } = require('./handlerUtils')
148
- const { NetlifyNextServer } = require('./server')
152
+ const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath } = require('./handlerUtils')
153
+ const { getNetlifyNextServer } = require('./server')
154
+ const NextServer = require(${JSON.stringify(nextServerModuleRelativeLocation)}).default
149
155
 
150
156
  ${isODB ? `const { builder } = require("@netlify/functions")` : ''}
151
157
  const { config } = require("${publishDir}/required-server-files.json")
@@ -156,7 +162,7 @@ const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '..
156
162
  const path = require("path");
157
163
  const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server"));
158
164
  exports.handler = ${isODB
159
- ? `builder((${makeHandler.toString()})(config, "${appDir}", pageRoot, staticManifest, 'odb'));`
160
- : `(${makeHandler.toString()})(config, "${appDir}", pageRoot, staticManifest, 'ssr');`}
165
+ ? `builder((${makeHandler.toString()})(config, "${appDir}", pageRoot, NextServer, staticManifest, 'odb'));`
166
+ : `(${makeHandler.toString()})(config, "${appDir}", pageRoot, NextServer, staticManifest, 'ssr');`}
161
167
  `;
162
168
  exports.getHandler = getHandler;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.localizeDataRoute = exports.localizeRoute = exports.unlocalizeRoute = exports.normalizeRoute = exports.netlifyApiFetch = exports.normalizePath = exports.getPrefetchResponse = exports.getNextServer = exports.augmentFsModule = exports.getMultiValueHeaders = exports.getMaxAge = exports.downloadFile = void 0;
6
+ exports.localizeDataRoute = exports.localizeRoute = exports.unlocalizeRoute = exports.normalizeRoute = exports.netlifyApiFetch = exports.normalizePath = exports.getPrefetchResponse = exports.augmentFsModule = exports.getMultiValueHeaders = exports.getMaxAge = exports.downloadFile = void 0;
7
7
  const fs_1 = require("fs");
8
8
  const os_1 = require("os");
9
9
  const path_1 = __importDefault(require("path"));
@@ -91,7 +91,7 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
91
91
  // Grab the real fs.promises.readFile...
92
92
  const readfileOrig = promises.readFile;
93
93
  const statsOrig = promises.stat;
94
- // ...then money-patch it to see if it's requesting a CDN file
94
+ // ...then monkey-patch it to see if it's requesting a CDN file
95
95
  promises.readFile = (async (file, options) => {
96
96
  const baseUrl = getBase();
97
97
  // We only care about page files
@@ -139,39 +139,6 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
139
139
  });
140
140
  };
141
141
  exports.augmentFsModule = augmentFsModule;
142
- /**
143
- * Next.js has an annoying habit of needing deep imports, but then moving those in patch releases. This is our abstraction.
144
- */
145
- const getNextServer = () => {
146
- let NextServer;
147
- try {
148
- // next >= 11.0.1. Yay breaking changes in patch releases!
149
- // eslint-disable-next-line @typescript-eslint/no-var-requires
150
- NextServer = require('next/dist/server/next-server').default;
151
- }
152
- catch (error) {
153
- if (!error.message.includes("Cannot find module 'next/dist/server/next-server'")) {
154
- // A different error, so rethrow it
155
- throw error;
156
- }
157
- // Probably an old version of next, so fall through and find it elsewhere.
158
- }
159
- if (!NextServer) {
160
- try {
161
- // next < 11.0.1
162
- // eslint-disable-next-line n/no-missing-require, import/no-unresolved, @typescript-eslint/no-var-requires
163
- NextServer = require('next/dist/next-server/server/next-server').default;
164
- }
165
- catch (error) {
166
- if (!error.message.includes("Cannot find module 'next/dist/next-server/server/next-server'")) {
167
- throw error;
168
- }
169
- throw new Error('Could not find Next.js server');
170
- }
171
- }
172
- return NextServer;
173
- };
174
- exports.getNextServer = getNextServer;
175
142
  /**
176
143
  * Prefetch requests are used to check for middleware redirects, and shouldn't trigger SSR.
177
144
  */
@@ -1,81 +1,83 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NetlifyNextServer = void 0;
3
+ exports.getNetlifyNextServer = void 0;
4
4
  const handlerUtils_1 = require("./handlerUtils");
5
- const NextServer = (0, handlerUtils_1.getNextServer)();
6
- class NetlifyNextServer extends NextServer {
7
- constructor(options, netlifyConfig) {
8
- super(options);
9
- this.netlifyConfig = netlifyConfig;
10
- // copy the prerender manifest so it doesn't get mutated by Next.js
11
- const manifest = this.getPrerenderManifest();
12
- this.netlifyPrerenderManifest = {
13
- ...manifest,
14
- routes: { ...manifest.routes },
15
- dynamicRoutes: { ...manifest.dynamicRoutes },
16
- };
17
- }
18
- getRequestHandler() {
19
- const handler = super.getRequestHandler();
20
- return async (req, res, parsedUrl) => {
21
- // preserve the URL before Next.js mutates it for i18n
22
- const { url, headers } = req;
23
- // handle the original res.revalidate() request
24
- await handler(req, res, parsedUrl);
25
- // handle on-demand revalidation by purging the ODB cache
26
- if (res.statusCode === 200 && headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) {
27
- await this.netlifyRevalidate(url);
28
- }
29
- };
30
- }
31
- async netlifyRevalidate(route) {
32
- try {
33
- // call netlify API to revalidate the path
34
- const result = await (0, handlerUtils_1.netlifyApiFetch)({
35
- endpoint: `sites/${process.env.SITE_ID}/refresh_on_demand_builders`,
36
- payload: {
37
- paths: this.getNetlifyPathsForRoute(route),
38
- domain: this.hostname,
39
- },
40
- token: this.netlifyConfig.revalidateToken,
41
- method: 'POST',
42
- });
43
- if (!result.ok) {
44
- throw new Error(result.message);
45
- }
5
+ const getNetlifyNextServer = (NextServer) => {
6
+ class NetlifyNextServer extends NextServer {
7
+ constructor(options, netlifyConfig) {
8
+ super(options);
9
+ this.netlifyConfig = netlifyConfig;
10
+ // copy the prerender manifest so it doesn't get mutated by Next.js
11
+ const manifest = this.getPrerenderManifest();
12
+ this.netlifyPrerenderManifest = {
13
+ ...manifest,
14
+ routes: { ...manifest.routes },
15
+ dynamicRoutes: { ...manifest.dynamicRoutes },
16
+ };
46
17
  }
47
- catch (error) {
48
- console.log(`Error revalidating ${route}:`, error.message);
49
- throw error;
18
+ getRequestHandler() {
19
+ const handler = super.getRequestHandler();
20
+ return async (req, res, parsedUrl) => {
21
+ // preserve the URL before Next.js mutates it for i18n
22
+ const { url, headers } = req;
23
+ // handle the original res.revalidate() request
24
+ await handler(req, res, parsedUrl);
25
+ // handle on-demand revalidation by purging the ODB cache
26
+ if (res.statusCode === 200 && headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) {
27
+ await this.netlifyRevalidate(url);
28
+ }
29
+ };
50
30
  }
51
- }
52
- getNetlifyPathsForRoute(route) {
53
- const { i18n } = this.nextConfig;
54
- const { routes, dynamicRoutes } = this.netlifyPrerenderManifest;
55
- // matches static routes
56
- const normalizedRoute = (0, handlerUtils_1.normalizeRoute)(i18n ? (0, handlerUtils_1.localizeRoute)(route, i18n) : route);
57
- if (normalizedRoute in routes) {
58
- const { dataRoute } = routes[normalizedRoute];
59
- const normalizedDataRoute = i18n ? (0, handlerUtils_1.localizeDataRoute)(dataRoute, normalizedRoute) : dataRoute;
60
- return [route, normalizedDataRoute];
31
+ async netlifyRevalidate(route) {
32
+ try {
33
+ // call netlify API to revalidate the path
34
+ const result = await (0, handlerUtils_1.netlifyApiFetch)({
35
+ endpoint: `sites/${process.env.SITE_ID}/refresh_on_demand_builders`,
36
+ payload: {
37
+ paths: this.getNetlifyPathsForRoute(route),
38
+ domain: this.hostname,
39
+ },
40
+ token: this.netlifyConfig.revalidateToken,
41
+ method: 'POST',
42
+ });
43
+ if (!result.ok) {
44
+ throw new Error(result.message);
45
+ }
46
+ }
47
+ catch (error) {
48
+ console.log(`Error revalidating ${route}:`, error.message);
49
+ throw error;
50
+ }
61
51
  }
62
- // matches dynamic routes
63
- const unlocalizedRoute = i18n ? (0, handlerUtils_1.unlocalizeRoute)(normalizedRoute, i18n) : normalizedRoute;
64
- for (const dynamicRoute in dynamicRoutes) {
65
- const { dataRoute, routeRegex } = dynamicRoutes[dynamicRoute];
66
- const matches = unlocalizedRoute.match(routeRegex);
67
- if (matches && matches.length !== 0) {
68
- // remove the first match, which is the full route
69
- matches.shift();
70
- // replace the dynamic segments with the actual values
71
- const interpolatedDataRoute = dataRoute.replace(/\[(.*?)]/g, () => matches.shift());
72
- const normalizedDataRoute = i18n
73
- ? (0, handlerUtils_1.localizeDataRoute)(interpolatedDataRoute, normalizedRoute)
74
- : interpolatedDataRoute;
52
+ getNetlifyPathsForRoute(route) {
53
+ const { i18n } = this.nextConfig;
54
+ const { routes, dynamicRoutes } = this.netlifyPrerenderManifest;
55
+ // matches static routes
56
+ const normalizedRoute = (0, handlerUtils_1.normalizeRoute)(i18n ? (0, handlerUtils_1.localizeRoute)(route, i18n) : route);
57
+ if (normalizedRoute in routes) {
58
+ const { dataRoute } = routes[normalizedRoute];
59
+ const normalizedDataRoute = i18n ? (0, handlerUtils_1.localizeDataRoute)(dataRoute, normalizedRoute) : dataRoute;
75
60
  return [route, normalizedDataRoute];
76
61
  }
62
+ // matches dynamic routes
63
+ const unlocalizedRoute = i18n ? (0, handlerUtils_1.unlocalizeRoute)(normalizedRoute, i18n) : normalizedRoute;
64
+ for (const dynamicRoute in dynamicRoutes) {
65
+ const { dataRoute, routeRegex } = dynamicRoutes[dynamicRoute];
66
+ const matches = unlocalizedRoute.match(routeRegex);
67
+ if (matches && matches.length !== 0) {
68
+ // remove the first match, which is the full route
69
+ matches.shift();
70
+ // replace the dynamic segments with the actual values
71
+ const interpolatedDataRoute = dataRoute.replace(/\[(.*?)]/g, () => matches.shift());
72
+ const normalizedDataRoute = i18n
73
+ ? (0, handlerUtils_1.localizeDataRoute)(interpolatedDataRoute, normalizedRoute)
74
+ : interpolatedDataRoute;
75
+ return [route, normalizedDataRoute];
76
+ }
77
+ }
78
+ throw new Error(`not an ISR route`);
77
79
  }
78
- throw new Error(`not an ISR route`);
79
80
  }
80
- }
81
- exports.NetlifyNextServer = NetlifyNextServer;
81
+ return NetlifyNextServer;
82
+ };
83
+ exports.getNetlifyNextServer = getNetlifyNextServer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "4.34.0",
3
+ "version": "4.35.1-next-server-fix.0",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@delucis/if-env": "^1.1.2",
40
- "@netlify/build": "^29.9.0",
40
+ "@netlify/build": "^29.9.2",
41
41
  "@types/fs-extra": "^9.0.13",
42
42
  "@types/jest": "^27.4.1",
43
43
  "@types/merge-stream": "^1.1.2",
@@ -44,7 +44,7 @@ export const getRscDataRouter = ({ routes: staticRoutes, dynamicRoutes }: Preren
44
44
  )
45
45
 
46
46
  const dynamicRouteMatcher = Object.values(dynamicRoutes)
47
- .filter(({ dataRoute }) => dataRoute.endsWith('.rsc'))
47
+ .filter(({ dataRoute }) => dataRoute?.endsWith('.rsc'))
48
48
  .map(({ routeRegex }) => new RegExp(routeRegex))
49
49
 
50
50
  const matchesDynamicRscDataRoute = (pathname: string) => {
File without changes