@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.
- package/lib/helpers/analysis.js +5 -3
- package/lib/helpers/edge.js +19 -9
- package/lib/helpers/files.js +49 -1
- package/lib/helpers/functions.js +12 -2
- package/lib/helpers/functionsMetaData.js +14 -9
- package/lib/templates/getApiHandler.js +11 -6
- package/lib/templates/getHandler.js +15 -9
- package/lib/templates/handlerUtils.js +2 -35
- package/lib/templates/server.js +73 -71
- package/package.json +2 -2
- package/src/templates/edge-shared/rsc-data.ts +1 -1
- package/src/templates/edge-shared/rsc-data.test.ts +0 -0
package/lib/helpers/analysis.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
}
|
package/lib/helpers/edge.js
CHANGED
|
@@ -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.
|
|
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
|
|
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.
|
|
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.
|
|
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',
|
package/lib/helpers/files.js
CHANGED
|
@@ -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
|
*/
|
package/lib/helpers/functions.js
CHANGED
|
@@ -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
|
|
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)({
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 {
|
|
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-
|
|
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,
|
|
148
|
-
const {
|
|
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.
|
|
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
|
|
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
|
*/
|
package/lib/templates/server.js
CHANGED
|
@@ -1,81 +1,83 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getNetlifyNextServer = void 0;
|
|
4
4
|
const handlerUtils_1 = require("./handlerUtils");
|
|
5
|
-
const
|
|
6
|
-
class NetlifyNextServer extends NextServer {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
47
|
+
.filter(({ dataRoute }) => dataRoute?.endsWith('.rsc'))
|
|
48
48
|
.map(({ routeRegex }) => new RegExp(routeRegex))
|
|
49
49
|
|
|
50
50
|
const matchesDynamicRscDataRoute = (pathname: string) => {
|
|
File without changes
|