@netlify/plugin-nextjs 4.35.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/edge.js +13 -3
- package/lib/helpers/files.js +49 -1
- package/lib/helpers/functions.js +11 -1
- 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 +1 -34
- package/lib/templates/server.js +73 -71
- package/package.json +1 -1
- package/src/templates/edge-shared/rsc-data.test.ts +0 -0
package/lib/helpers/edge.js
CHANGED
|
@@ -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,
|
|
@@ -131,6 +134,7 @@ exports.writeDevEdgeFunction = writeDevEdgeFunction;
|
|
|
131
134
|
* Writes an edge function that routes RSC data requests to the `.rsc` route
|
|
132
135
|
*/
|
|
133
136
|
const generateRscDataEdgeManifest = async ({ prerenderManifest, appPathRoutesManifest, }) => {
|
|
137
|
+
const generator = await (0, functionsMetaData_1.getPluginVersion)();
|
|
134
138
|
if (!prerenderManifest || !appPathRoutesManifest) {
|
|
135
139
|
return [];
|
|
136
140
|
}
|
|
@@ -157,11 +161,13 @@ const generateRscDataEdgeManifest = async ({ prerenderManifest, appPathRoutesMan
|
|
|
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
|
};
|
|
@@ -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: [],
|
|
@@ -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,6 +23,10 @@ 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
32
|
if ((0, analysis_1.isEdgeConfig)(config.runtime)) {
|
|
@@ -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"));
|
|
@@ -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
|
File without changes
|