@netlify/plugin-nextjs 4.37.4 → 4.38.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/constants.js +3 -1
- package/lib/helpers/config.js +6 -1
- package/lib/helpers/dev.js +0 -3
- package/lib/helpers/edge.js +7 -2
- package/lib/helpers/files.js +1 -98
- package/lib/helpers/functions.js +41 -10
- package/lib/index.js +1 -2
- package/lib/templates/getHandler.js +6 -5
- package/lib/templates/handlerUtils.js +4 -1
- package/lib/templates/requireHooks.js +96 -0
- package/lib/templates/server.js +34 -2
- package/package.json +3 -3
package/lib/constants.js
CHANGED
|
@@ -3,15 +3,17 @@ 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.DIVIDER = exports.LAMBDA_MAX_SIZE = exports.LAMBDA_WARNING_SIZE = exports.MINIMUM_REVALIDATE_SECONDS = exports.DYNAMIC_PARAMETER_REGEX = exports.OPTIONAL_CATCH_ALL_REGEX = exports.CATCH_ALL_REGEX = exports.DEFAULT_FUNCTIONS_SRC = exports.HANDLER_FUNCTION_PATH = exports.ODB_FUNCTION_PATH = exports.HIDDEN_PATHS = exports.IMAGE_FUNCTION_TITLE = exports.ODB_FUNCTION_TITLE = exports.HANDLER_FUNCTION_TITLE = exports.NEXT_PLUGIN = exports.NEXT_PLUGIN_NAME = exports.IMAGE_FUNCTION_NAME = exports.ODB_FUNCTION_NAME = exports.HANDLER_FUNCTION_NAME = void 0;
|
|
6
|
+
exports.DIVIDER = exports.LAMBDA_MAX_SIZE = exports.LAMBDA_WARNING_SIZE = exports.MINIMUM_REVALIDATE_SECONDS = exports.DYNAMIC_PARAMETER_REGEX = exports.OPTIONAL_CATCH_ALL_REGEX = exports.CATCH_ALL_REGEX = exports.DEFAULT_FUNCTIONS_SRC = exports.HANDLER_FUNCTION_PATH = exports.ODB_FUNCTION_PATH = exports.HIDDEN_PATHS = exports.IMAGE_FUNCTION_TITLE = exports.API_FUNCTION_TITLE = exports.ODB_FUNCTION_TITLE = exports.HANDLER_FUNCTION_TITLE = exports.NEXT_PLUGIN = exports.NEXT_PLUGIN_NAME = exports.IMAGE_FUNCTION_NAME = exports.API_FUNCTION_NAME = exports.ODB_FUNCTION_NAME = exports.HANDLER_FUNCTION_NAME = void 0;
|
|
7
7
|
const destr_1 = __importDefault(require("destr"));
|
|
8
8
|
exports.HANDLER_FUNCTION_NAME = '___netlify-handler';
|
|
9
9
|
exports.ODB_FUNCTION_NAME = '___netlify-odb-handler';
|
|
10
|
+
exports.API_FUNCTION_NAME = '___netlify-api-handler';
|
|
10
11
|
exports.IMAGE_FUNCTION_NAME = '_ipx';
|
|
11
12
|
exports.NEXT_PLUGIN_NAME = '@netlify/next-runtime';
|
|
12
13
|
exports.NEXT_PLUGIN = '@netlify/plugin-nextjs';
|
|
13
14
|
exports.HANDLER_FUNCTION_TITLE = 'Next.js SSR handler';
|
|
14
15
|
exports.ODB_FUNCTION_TITLE = 'Next.js ISR handler';
|
|
16
|
+
exports.API_FUNCTION_TITLE = 'Next.js API handler';
|
|
15
17
|
exports.IMAGE_FUNCTION_TITLE = 'next/image handler';
|
|
16
18
|
// These are paths in .next that shouldn't be publicly accessible
|
|
17
19
|
exports.HIDDEN_PATHS = (0, destr_1.default)(process.env.NEXT_KEEP_METADATA_FILES)
|
package/lib/helpers/config.js
CHANGED
|
@@ -64,6 +64,11 @@ const hasManuallyAddedModule = ({ netlifyConfig, moduleName, }) =>
|
|
|
64
64
|
Object.values(netlifyConfig.functions).some(({ included_files = [] }) => included_files.some((inc) => inc.includes(`node_modules/${moduleName}`)));
|
|
65
65
|
exports.hasManuallyAddedModule = hasManuallyAddedModule;
|
|
66
66
|
/* eslint-enable camelcase */
|
|
67
|
+
/**
|
|
68
|
+
* Transforms `/api/shows/[id].js` into `/api/shows/*id*.js`,
|
|
69
|
+
* so that the file `[id].js` is matched correctly.
|
|
70
|
+
*/
|
|
71
|
+
const escapeGlob = (path) => path.replace(/\[/g, '*').replace(/]/g, '*');
|
|
67
72
|
const configureHandlerFunctions = async ({ netlifyConfig, publish, ignore = [], apiLambdas, splitApiRoutes, }) => {
|
|
68
73
|
var _a, _b, _c;
|
|
69
74
|
const config = await (0, exports.getRequiredServerFiles)(publish);
|
|
@@ -100,7 +105,7 @@ const configureHandlerFunctions = async ({ netlifyConfig, publish, ignore = [],
|
|
|
100
105
|
(_b = netlifyConfig.functions)[functionName] || (_b[functionName] = { included_files: [] });
|
|
101
106
|
netlifyConfig.functions[functionName].node_bundler = 'none';
|
|
102
107
|
(_c = netlifyConfig.functions[functionName]).included_files || (_c.included_files = []);
|
|
103
|
-
netlifyConfig.functions[functionName].included_files.push(...includedFiles);
|
|
108
|
+
netlifyConfig.functions[functionName].included_files.push(...includedFiles.map(escapeGlob));
|
|
104
109
|
}
|
|
105
110
|
}
|
|
106
111
|
else {
|
package/lib/helpers/dev.js
CHANGED
|
@@ -7,13 +7,10 @@ exports.onPreDev = void 0;
|
|
|
7
7
|
const path_1 = require("path");
|
|
8
8
|
const execa_1 = __importDefault(require("execa"));
|
|
9
9
|
const edge_1 = require("./edge");
|
|
10
|
-
const files_1 = require("./files");
|
|
11
10
|
// The types haven't been updated yet
|
|
12
11
|
const onPreDev = async ({ constants, netlifyConfig }) => {
|
|
13
12
|
var _a;
|
|
14
13
|
const base = (_a = netlifyConfig.build.base) !== null && _a !== void 0 ? _a : process.cwd();
|
|
15
|
-
// Need to patch the files, because build might not have been run
|
|
16
|
-
await (0, files_1.patchNextFiles)(base);
|
|
17
14
|
await (0, edge_1.writeDevEdgeFunction)(constants);
|
|
18
15
|
// Don't await this or it will never finish
|
|
19
16
|
execa_1.default.node((0, path_1.resolve)(__dirname, '..', '..', 'lib', 'helpers', 'middlewareWatcher.js'), [base, process.env.NODE_ENV === 'test' ? '--once' : ''], {
|
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.generateRscDataEdgeManifest = 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.sanitizeEdgePath = 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");
|
|
@@ -31,6 +31,11 @@ exports.loadPrerenderManifest = loadPrerenderManifest;
|
|
|
31
31
|
* Convert the Next middleware name into a valid Edge Function name
|
|
32
32
|
*/
|
|
33
33
|
const sanitizeName = (name) => `next_${name.replace(/\W/g, '_')}`;
|
|
34
|
+
/**
|
|
35
|
+
* Convert the images path to strip the origin (until domain-level Edge functions are supported)
|
|
36
|
+
*/
|
|
37
|
+
const sanitizeEdgePath = (imagesPath) => new URL(imagesPath, process.env.URL || 'http://n').pathname;
|
|
38
|
+
exports.sanitizeEdgePath = sanitizeEdgePath;
|
|
34
39
|
// Slightly different spacing in different versions!
|
|
35
40
|
const IMPORT_UNSUPPORTED = [
|
|
36
41
|
`Object.defineProperty(globalThis,"__import_unsupported"`,
|
|
@@ -301,7 +306,7 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
|
|
|
301
306
|
manifest.functions.push({
|
|
302
307
|
function: 'ipx',
|
|
303
308
|
name: 'next/image handler',
|
|
304
|
-
path: nextConfig.images.path
|
|
309
|
+
path: nextConfig.images.path ? (0, exports.sanitizeEdgePath)(nextConfig.images.path) : '/_next/image',
|
|
305
310
|
generator,
|
|
306
311
|
});
|
|
307
312
|
manifest.layers.push({
|
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.removeMetadataFiles = exports.movePublicFiles = exports.
|
|
6
|
+
exports.removeMetadataFiles = exports.movePublicFiles = exports.getDependenciesOfFile = exports.getSourceFileForPage = exports.getServerFile = 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");
|
|
@@ -236,36 +236,6 @@ const moveStaticPages = async ({ netlifyConfig, target, i18n, basePath, }) => {
|
|
|
236
236
|
}
|
|
237
237
|
};
|
|
238
238
|
exports.moveStaticPages = moveStaticPages;
|
|
239
|
-
const PATCH_WARNING = `/* File patched by Netlify */`;
|
|
240
|
-
/**
|
|
241
|
-
* Attempt to patch a source file, preserving a backup
|
|
242
|
-
*/
|
|
243
|
-
const patchFile = async ({ file, replacements, }) => {
|
|
244
|
-
if (!(0, fs_extra_1.existsSync)(file)) {
|
|
245
|
-
console.warn('File was not found');
|
|
246
|
-
return false;
|
|
247
|
-
}
|
|
248
|
-
let content = await (0, fs_extra_1.readFile)(file, 'utf8');
|
|
249
|
-
// If the file has already been patched, patch the backed-up original instead
|
|
250
|
-
if (content.includes(PATCH_WARNING) && (0, fs_extra_1.existsSync)(`${file}.orig`)) {
|
|
251
|
-
content = await (0, fs_extra_1.readFile)(`${file}.orig`, 'utf8');
|
|
252
|
-
}
|
|
253
|
-
const newContent = replacements.reduce((acc, [from, to]) => {
|
|
254
|
-
if (acc.includes(to)) {
|
|
255
|
-
console.log('Already patched. Skipping.');
|
|
256
|
-
return acc;
|
|
257
|
-
}
|
|
258
|
-
return acc.replace(from, to);
|
|
259
|
-
}, content);
|
|
260
|
-
if (newContent === content) {
|
|
261
|
-
console.warn('File was not changed');
|
|
262
|
-
return false;
|
|
263
|
-
}
|
|
264
|
-
await (0, fs_extra_1.writeFile)(`${file}.orig`, content);
|
|
265
|
-
await (0, fs_extra_1.writeFile)(file, `${newContent}\n${PATCH_WARNING}`);
|
|
266
|
-
console.log('Done');
|
|
267
|
-
return true;
|
|
268
|
-
};
|
|
269
239
|
/**
|
|
270
240
|
* The file we need has moved around a bit over the past few versions,
|
|
271
241
|
* so we iterate through the options until we find it
|
|
@@ -312,73 +282,6 @@ const getDependenciesOfFile = async (file) => {
|
|
|
312
282
|
return dependencies.files.map((dep) => (0, pathe_1.resolve)((0, pathe_1.dirname)(file), dep));
|
|
313
283
|
};
|
|
314
284
|
exports.getDependenciesOfFile = getDependenciesOfFile;
|
|
315
|
-
const baseServerReplacements = [
|
|
316
|
-
// force manual revalidate during cache fetches
|
|
317
|
-
[
|
|
318
|
-
`checkIsManualRevalidate(req, this.renderOpts.previewProps)`,
|
|
319
|
-
`checkIsManualRevalidate(process.env._REVALIDATE_SSG ? { headers: { 'x-prerender-revalidate': this.renderOpts.previewProps.previewModeId } } : req, this.renderOpts.previewProps)`,
|
|
320
|
-
],
|
|
321
|
-
// In https://github.com/vercel/next.js/pull/47803 checkIsManualRevalidate was renamed to checkIsOnDemandRevalidate
|
|
322
|
-
[
|
|
323
|
-
`checkIsOnDemandRevalidate(req, this.renderOpts.previewProps)`,
|
|
324
|
-
`checkIsOnDemandRevalidate(process.env._REVALIDATE_SSG ? { headers: { 'x-prerender-revalidate': this.renderOpts.previewProps.previewModeId } } : req, this.renderOpts.previewProps)`,
|
|
325
|
-
],
|
|
326
|
-
// ensure ISR 404 pages send the correct SWR cache headers
|
|
327
|
-
[`private: isPreviewMode || is404Page && cachedData`, `private: isPreviewMode && cachedData`],
|
|
328
|
-
];
|
|
329
|
-
const nextServerReplacements = [
|
|
330
|
-
[
|
|
331
|
-
`getMiddlewareManifest() {\n if (this.minimalMode) return null;`,
|
|
332
|
-
`getMiddlewareManifest() {\n if (this.minimalMode || (process.env.NEXT_DISABLE_NETLIFY_EDGE !== 'true' && process.env.NEXT_DISABLE_NETLIFY_EDGE !== '1')) return null;`,
|
|
333
|
-
],
|
|
334
|
-
[
|
|
335
|
-
`generateCatchAllMiddlewareRoute(devReady) {\n if (this.minimalMode) return []`,
|
|
336
|
-
`generateCatchAllMiddlewareRoute(devReady) {\n if (this.minimalMode || (process.env.NEXT_DISABLE_NETLIFY_EDGE !== 'true' && process.env.NEXT_DISABLE_NETLIFY_EDGE !== '1')) return [];`,
|
|
337
|
-
],
|
|
338
|
-
[
|
|
339
|
-
`generateCatchAllMiddlewareRoute() {\n if (this.minimalMode) return undefined;`,
|
|
340
|
-
`generateCatchAllMiddlewareRoute() {\n if (this.minimalMode || (process.env.NEXT_DISABLE_NETLIFY_EDGE !== 'true' && process.env.NEXT_DISABLE_NETLIFY_EDGE !== '1')) return undefined;`,
|
|
341
|
-
],
|
|
342
|
-
[
|
|
343
|
-
`getMiddlewareManifest() {\n if (this.minimalMode) {`,
|
|
344
|
-
`getMiddlewareManifest() {\n if (!this.minimalMode && (process.env.NEXT_DISABLE_NETLIFY_EDGE === 'true' || process.env.NEXT_DISABLE_NETLIFY_EDGE === '1')) {`,
|
|
345
|
-
],
|
|
346
|
-
];
|
|
347
|
-
const patchNextFiles = async (root) => {
|
|
348
|
-
const baseServerFile = (0, exports.getServerFile)(root);
|
|
349
|
-
console.log(`Patching ${baseServerFile}`);
|
|
350
|
-
if (baseServerFile) {
|
|
351
|
-
await patchFile({
|
|
352
|
-
file: baseServerFile,
|
|
353
|
-
replacements: baseServerReplacements,
|
|
354
|
-
});
|
|
355
|
-
}
|
|
356
|
-
const nextServerFile = (0, exports.getServerFile)(root, false);
|
|
357
|
-
console.log(`Patching ${nextServerFile}`);
|
|
358
|
-
if (nextServerFile) {
|
|
359
|
-
await patchFile({
|
|
360
|
-
file: nextServerFile,
|
|
361
|
-
replacements: nextServerReplacements,
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
};
|
|
365
|
-
exports.patchNextFiles = patchNextFiles;
|
|
366
|
-
const unpatchFile = async (file) => {
|
|
367
|
-
const origFile = `${file}.orig`;
|
|
368
|
-
if ((0, fs_extra_1.existsSync)(origFile)) {
|
|
369
|
-
await (0, fs_extra_1.move)(origFile, file, { overwrite: true });
|
|
370
|
-
}
|
|
371
|
-
};
|
|
372
|
-
exports.unpatchFile = unpatchFile;
|
|
373
|
-
const unpatchNextFiles = async (root) => {
|
|
374
|
-
const baseServerFile = (0, exports.getServerFile)(root);
|
|
375
|
-
await (0, exports.unpatchFile)(baseServerFile);
|
|
376
|
-
const nextServerFile = (0, exports.getServerFile)(root, false);
|
|
377
|
-
if (nextServerFile !== baseServerFile) {
|
|
378
|
-
await (0, exports.unpatchFile)(nextServerFile);
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
exports.unpatchNextFiles = unpatchNextFiles;
|
|
382
285
|
const movePublicFiles = async ({ appDir, outdir, publish, basePath, }) => {
|
|
383
286
|
// `outdir` is a config property added when using Next.js with Nx. It's typically
|
|
384
287
|
// a relative path outside of the appDir, e.g. '../../dist/apps/<app-name>', and
|
package/lib/helpers/functions.js
CHANGED
|
@@ -31,7 +31,7 @@ const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS
|
|
|
31
31
|
? (0, pathe_1.relative)(functionDir, nextServerModuleAbsoluteLocation)
|
|
32
32
|
: undefined;
|
|
33
33
|
for (const apiLambda of apiLambdas) {
|
|
34
|
-
const { functionName, routes, type, includedFiles } = apiLambda;
|
|
34
|
+
const { functionName, functionTitle, routes, type, includedFiles } = apiLambda;
|
|
35
35
|
const apiHandlerSource = (0, getApiHandler_1.getApiHandler)({
|
|
36
36
|
// most api lambdas serve multiple routes, but scheduled functions need to be in separate lambdas.
|
|
37
37
|
// so routes[0] is safe to access.
|
|
@@ -46,6 +46,7 @@ const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS
|
|
|
46
46
|
// copy handler dependencies (VercelNodeBridge, NetlifyNextServer, etc.)
|
|
47
47
|
await (0, fs_extra_1.copyFile)(node_bridge_1.default, (0, pathe_1.join)(functionsDir, functionName, 'bridge.js'));
|
|
48
48
|
await (0, fs_extra_1.copyFile)((0, pathe_1.join)(__dirname, '..', '..', 'lib', 'templates', 'server.js'), (0, pathe_1.join)(functionsDir, functionName, 'server.js'));
|
|
49
|
+
await (0, fs_extra_1.copyFile)((0, pathe_1.join)(__dirname, '..', '..', 'lib', 'templates', 'requireHooks.js'), (0, pathe_1.join)(functionsDir, functionName, 'requireHooks.js'));
|
|
49
50
|
await (0, fs_extra_1.copyFile)((0, pathe_1.join)(__dirname, '..', '..', 'lib', 'templates', 'handlerUtils.js'), (0, pathe_1.join)(functionsDir, functionName, 'handlerUtils.js'));
|
|
50
51
|
const resolveSourceFile = (file) => (0, pathe_1.join)(publish, 'server', file);
|
|
51
52
|
// TODO: this should be unneeded once we use the `none` bundler everywhere
|
|
@@ -60,6 +61,7 @@ const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS
|
|
|
60
61
|
].map(resolveSourceFile),
|
|
61
62
|
});
|
|
62
63
|
await (0, fs_extra_1.writeFile)((0, pathe_1.join)(functionsDir, functionName, 'pages.js'), resolverSource);
|
|
64
|
+
await (0, functionsMetaData_1.writeFunctionConfiguration)({ functionName, functionTitle, functionsDir });
|
|
63
65
|
const nfInternalFiles = await (0, tiny_glob_1.default)((0, pathe_1.join)(functionsDir, functionName, '**'));
|
|
64
66
|
includedFiles.push(...nfInternalFiles);
|
|
65
67
|
}
|
|
@@ -76,8 +78,9 @@ const generateFunctions = async ({ FUNCTIONS_SRC = constants_1.DEFAULT_FUNCTIONS
|
|
|
76
78
|
// copy handler dependencies (VercelNodeBridge, NetlifyNextServer, etc.)
|
|
77
79
|
await (0, fs_extra_1.copyFile)(node_bridge_1.default, (0, pathe_1.join)(functionsDir, functionName, 'bridge.js'));
|
|
78
80
|
await (0, fs_extra_1.copyFile)((0, pathe_1.join)(__dirname, '..', '..', 'lib', 'templates', 'server.js'), (0, pathe_1.join)(functionsDir, functionName, 'server.js'));
|
|
81
|
+
await (0, fs_extra_1.copyFile)((0, pathe_1.join)(__dirname, '..', '..', 'lib', 'templates', 'requireHooks.js'), (0, pathe_1.join)(functionsDir, functionName, 'requireHooks.js'));
|
|
79
82
|
await (0, fs_extra_1.copyFile)((0, pathe_1.join)(__dirname, '..', '..', 'lib', 'templates', 'handlerUtils.js'), (0, pathe_1.join)(functionsDir, functionName, 'handlerUtils.js'));
|
|
80
|
-
(0, functionsMetaData_1.writeFunctionConfiguration)({ functionName, functionTitle, functionsDir });
|
|
83
|
+
await (0, functionsMetaData_1.writeFunctionConfiguration)({ functionName, functionTitle, functionsDir });
|
|
81
84
|
};
|
|
82
85
|
await writeHandler(constants_1.HANDLER_FUNCTION_NAME, constants_1.HANDLER_FUNCTION_TITLE, false);
|
|
83
86
|
await writeHandler(constants_1.ODB_FUNCTION_NAME, constants_1.ODB_FUNCTION_TITLE, true);
|
|
@@ -221,20 +224,45 @@ const getBundleWeight = async (patterns) => {
|
|
|
221
224
|
}));
|
|
222
225
|
return sum(sizes.flat(1));
|
|
223
226
|
};
|
|
224
|
-
const MB = 1024 * 1024;
|
|
225
227
|
const getAPILambdas = async (publish, baseDir, pageExtensions) => {
|
|
226
228
|
const commonDependencies = await (0, exports.getAPIPRouteCommonDependencies)(publish);
|
|
227
|
-
const threshold =
|
|
229
|
+
const threshold = constants_1.LAMBDA_WARNING_SIZE - (await getBundleWeight(commonDependencies));
|
|
228
230
|
const apiRoutes = await (0, exports.getApiRouteConfigs)(publish, baseDir, pageExtensions);
|
|
229
231
|
const packFunctions = async (routes, type) => {
|
|
230
232
|
const weighedRoutes = await Promise.all(routes.map(async (route) => ({ value: route, weight: await getBundleWeight(route.includedFiles) })));
|
|
231
233
|
const bins = (0, pack_1.pack)(weighedRoutes, threshold);
|
|
232
|
-
return bins.map((bin
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
234
|
+
return bins.map((bin) => {
|
|
235
|
+
if (bin.length === 1) {
|
|
236
|
+
const [func] = bin;
|
|
237
|
+
const { functionName, functionTitle, config, includedFiles } = func;
|
|
238
|
+
return {
|
|
239
|
+
functionName,
|
|
240
|
+
functionTitle,
|
|
241
|
+
routes: [func],
|
|
242
|
+
includedFiles: [...commonDependencies, ...includedFiles],
|
|
243
|
+
type: config.type,
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
const includedFiles = [...commonDependencies, ...bin.flatMap((route) => route.includedFiles)];
|
|
247
|
+
const nonSingletonBins = bins.filter((b) => b.length > 1);
|
|
248
|
+
if (nonSingletonBins.length === 1) {
|
|
249
|
+
return {
|
|
250
|
+
functionName: constants_1.API_FUNCTION_NAME,
|
|
251
|
+
functionTitle: constants_1.API_FUNCTION_TITLE,
|
|
252
|
+
includedFiles,
|
|
253
|
+
routes: bin,
|
|
254
|
+
type,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
const indexInNonSingletonBins = nonSingletonBins.indexOf(bin);
|
|
258
|
+
return {
|
|
259
|
+
functionName: `${constants_1.API_FUNCTION_NAME}-${indexInNonSingletonBins + 1}`,
|
|
260
|
+
functionTitle: `${constants_1.API_FUNCTION_TITLE} ${indexInNonSingletonBins + 1}/${nonSingletonBins.length}`,
|
|
261
|
+
includedFiles,
|
|
262
|
+
routes: bin,
|
|
263
|
+
type,
|
|
264
|
+
};
|
|
265
|
+
});
|
|
238
266
|
};
|
|
239
267
|
const standardFunctions = apiRoutes.filter((route) => !(0, analysis_1.isEdgeConfig)(route.config.runtime) &&
|
|
240
268
|
route.config.type !== "experimental-background" /* ApiRouteType.BACKGROUND */ &&
|
|
@@ -263,12 +291,14 @@ const getApiRouteConfigs = async (publish, appDir, pageExtensions) => {
|
|
|
263
291
|
const filePath = (0, files_1.getSourceFileForPage)(apiRoute, [pagesDir, srcPagesDir], pageExtensions);
|
|
264
292
|
const config = await (0, analysis_1.extractConfigFromFile)(filePath, appDir);
|
|
265
293
|
const functionName = (0, utils_1.getFunctionNameForPage)(apiRoute, config.type === "experimental-background" /* ApiRouteType.BACKGROUND */);
|
|
294
|
+
const functionTitle = `${constants_1.API_FUNCTION_TITLE} ${apiRoute}`;
|
|
266
295
|
const compiled = pages[apiRoute];
|
|
267
296
|
const compiledPath = (0, pathe_1.join)(publish, 'server', compiled);
|
|
268
297
|
const routeDependencies = await (0, files_1.getDependenciesOfFile)(compiledPath);
|
|
269
298
|
const includedFiles = [compiledPath, ...routeDependencies];
|
|
270
299
|
return {
|
|
271
300
|
functionName,
|
|
301
|
+
functionTitle,
|
|
272
302
|
route: apiRoute,
|
|
273
303
|
config,
|
|
274
304
|
compiled,
|
|
@@ -288,6 +318,7 @@ const getExtendedApiRouteConfigs = async (publish, appDir, pageExtensions) => {
|
|
|
288
318
|
exports.getExtendedApiRouteConfigs = getExtendedApiRouteConfigs;
|
|
289
319
|
const packSingleFunction = (func) => ({
|
|
290
320
|
functionName: func.functionName,
|
|
321
|
+
functionTitle: func.functionTitle,
|
|
291
322
|
includedFiles: func.includedFiles,
|
|
292
323
|
routes: [func],
|
|
293
324
|
type: func.config.type,
|
package/lib/index.js
CHANGED
|
@@ -51,6 +51,7 @@ const plugin = {
|
|
|
51
51
|
});
|
|
52
52
|
await (0, edge_1.cleanupEdgeFunctions)(constants);
|
|
53
53
|
const middlewareManifest = await (0, edge_1.loadMiddlewareManifest)(netlifyConfig);
|
|
54
|
+
const config = await (0, config_1.getRequiredServerFiles)(publish);
|
|
54
55
|
if ((middlewareManifest === null || middlewareManifest === void 0 ? void 0 : middlewareManifest.functions) &&
|
|
55
56
|
Object.keys(middlewareManifest.functions).length !== 0 &&
|
|
56
57
|
(0, destr_1.default)(process.env.NEXT_DISABLE_NETLIFY_EDGE)) {
|
|
@@ -70,7 +71,6 @@ const plugin = {
|
|
|
70
71
|
`));
|
|
71
72
|
}
|
|
72
73
|
if ((0, utils_1.isNextAuthInstalled)()) {
|
|
73
|
-
const config = await (0, config_1.getRequiredServerFiles)(publish);
|
|
74
74
|
const userDefinedNextAuthUrl = config.config.env.NEXTAUTH_URL;
|
|
75
75
|
if (userDefinedNextAuthUrl) {
|
|
76
76
|
console.log(`NextAuth package detected, NEXTAUTH_URL environment variable set by user in next.config.js to ${userDefinedNextAuthUrl}`);
|
|
@@ -105,7 +105,6 @@ const plugin = {
|
|
|
105
105
|
splitApiRoutes: (0, flags_1.splitApiRoutes)(featureFlags, publish),
|
|
106
106
|
});
|
|
107
107
|
await (0, files_1.movePublicFiles)({ appDir, outdir, publish, basePath });
|
|
108
|
-
await (0, files_1.patchNextFiles)(appDir);
|
|
109
108
|
if (!(0, destr_1.default)(process.env.SERVE_STATIC_FILES_FROM_ORIGIN)) {
|
|
110
109
|
await (0, files_1.moveStaticPages)({ target, netlifyConfig, i18n, basePath });
|
|
111
110
|
}
|
|
@@ -11,6 +11,7 @@ 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 { overrideRequireHooks, applyRequireHooks } = require('./requireHooks');
|
|
14
15
|
const { getNetlifyNextServer } = require('./server');
|
|
15
16
|
// We return a function and then call `toString()` on it to serialise it as the launcher function
|
|
16
17
|
// eslint-disable-next-line max-lines-per-function
|
|
@@ -28,14 +29,14 @@ const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mod
|
|
|
28
29
|
require.resolve('./pages.js');
|
|
29
30
|
}
|
|
30
31
|
catch { }
|
|
32
|
+
// Next 13.4 conditionally uses different React versions and we need to make sure we use the same one
|
|
33
|
+
overrideRequireHooks(conf);
|
|
31
34
|
const NetlifyNextServer = getNetlifyNextServer(NextServer);
|
|
35
|
+
applyRequireHooks();
|
|
32
36
|
const ONE_YEAR_IN_SECONDS = 31536000;
|
|
33
37
|
(_a = process.env).NODE_ENV || (_a.NODE_ENV = 'production');
|
|
34
38
|
// We don't want to write ISR files to disk in the lambda environment
|
|
35
39
|
conf.experimental.isrFlushToDisk = false;
|
|
36
|
-
// This is our flag that we use when patching the source
|
|
37
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
38
|
-
process.env._REVALIDATE_SSG = 'true';
|
|
39
40
|
for (const [key, value] of Object.entries(conf.env)) {
|
|
40
41
|
process.env[key] = String(value);
|
|
41
42
|
}
|
|
@@ -152,9 +153,9 @@ const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '..
|
|
|
152
153
|
// We copy the file here rather than requiring from the node module
|
|
153
154
|
const { Bridge } = require("./bridge");
|
|
154
155
|
const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath } = require('./handlerUtils')
|
|
155
|
-
const {
|
|
156
|
+
const { overrideRequireHooks, applyRequireHooks } = require("./requireHooks")
|
|
157
|
+
const { getNetlifyNextServer } = require("./server")
|
|
156
158
|
const NextServer = require(${JSON.stringify(nextServerModuleRelativeLocation)}).default
|
|
157
|
-
|
|
158
159
|
${isODB ? `const { builder } = require("@netlify/functions")` : ''}
|
|
159
160
|
const { config } = require("${publishDir}/required-server-files.json")
|
|
160
161
|
let staticManifest
|
|
@@ -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.augmentFsModule = exports.getMultiValueHeaders = exports.getMaxAge = exports.downloadFile = void 0;
|
|
6
|
+
exports.localizeDataRoute = exports.localizeRoute = exports.unlocalizeRoute = exports.joinPaths = 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"));
|
|
@@ -203,6 +203,9 @@ exports.netlifyApiFetch = netlifyApiFetch;
|
|
|
203
203
|
// Remove trailing slash from a route (except for the root route)
|
|
204
204
|
const normalizeRoute = (route) => (route.endsWith('/') ? route.slice(0, -1) || '/' : route);
|
|
205
205
|
exports.normalizeRoute = normalizeRoute;
|
|
206
|
+
// Join multiple paths together, ensuring that there is only one slash between them
|
|
207
|
+
const joinPaths = (...paths) => paths.reduce((a, b) => (a.endsWith('/') ? `${a}${b}` : `${a}/${b}`));
|
|
208
|
+
exports.joinPaths = joinPaths;
|
|
206
209
|
// Check if a route has a locale prefix (including the root route)
|
|
207
210
|
const isLocalized = (route, i18n) => i18n.locales.some((locale) => route === `/${locale}` || route.startsWith(`/${locale}/`));
|
|
208
211
|
// Remove the locale prefix from a route (if any)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint-disable n/no-extraneous-require, no-underscore-dangle, @typescript-eslint/no-explicit-any */
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.applyRequireHooks = exports.overrideRequireHooks = void 0;
|
|
8
|
+
// This is a modified version of the require hooks from Next.js
|
|
9
|
+
// https://github.com/vercel/next.js/blob/b04c70573ac199a9bb3ea42201e0865e610d5b67/packages/next/src/server/require-hook.ts
|
|
10
|
+
const module_1 = __importDefault(require("module"));
|
|
11
|
+
const resolveFilename = module_1.default._resolveFilename;
|
|
12
|
+
const requireHooks = new Map();
|
|
13
|
+
const overrideRequireHooks = (config) => {
|
|
14
|
+
// we may have changed the working directory in the handler
|
|
15
|
+
const opts = {
|
|
16
|
+
paths: [process.cwd()],
|
|
17
|
+
};
|
|
18
|
+
requireHooks.set('default', new Map([
|
|
19
|
+
['react', require.resolve(`react`, opts)],
|
|
20
|
+
['react/jsx-runtime', require.resolve(`react/jsx-runtime`, opts)],
|
|
21
|
+
]));
|
|
22
|
+
if (config.experimental.appDir) {
|
|
23
|
+
requireHooks.set('next', new Map([
|
|
24
|
+
['react', require.resolve(`next/dist/compiled/react`, opts)],
|
|
25
|
+
['react/jsx-runtime', require.resolve(`next/dist/compiled/react/jsx-runtime`, opts)],
|
|
26
|
+
['react/jsx-dev-runtime', require.resolve(`next/dist/compiled/react/jsx-dev-runtime`, opts)],
|
|
27
|
+
['react-dom', require.resolve(`next/dist/compiled/react-dom/server-rendering-stub`, opts)],
|
|
28
|
+
['react-dom/client', require.resolve(`next/dist/compiled/react-dom/client`, opts)],
|
|
29
|
+
['react-dom/server', require.resolve(`next/dist/compiled/react-dom/server`, opts)],
|
|
30
|
+
['react-dom/server.browser', require.resolve(`next/dist/compiled/react-dom/server.browser`, opts)],
|
|
31
|
+
['react-dom/server.edge', require.resolve(`next/dist/compiled/react-dom/server.edge`, opts)],
|
|
32
|
+
[
|
|
33
|
+
'react-server-dom-webpack/client',
|
|
34
|
+
require.resolve(`next/dist/compiled/react-server-dom-webpack/client`, opts),
|
|
35
|
+
],
|
|
36
|
+
[
|
|
37
|
+
'react-server-dom-webpack/client.edge',
|
|
38
|
+
require.resolve(`next/dist/compiled/react-server-dom-webpack/client.edge`, opts),
|
|
39
|
+
],
|
|
40
|
+
[
|
|
41
|
+
'react-server-dom-webpack/server.edge',
|
|
42
|
+
require.resolve(`next/dist/compiled/react-server-dom-webpack/server.edge`, opts),
|
|
43
|
+
],
|
|
44
|
+
[
|
|
45
|
+
'react-server-dom-webpack/server.node',
|
|
46
|
+
require.resolve(`next/dist/compiled/react-server-dom-webpack/server.node`, opts),
|
|
47
|
+
],
|
|
48
|
+
['styled-jsx', require.resolve('styled-jsx', opts)],
|
|
49
|
+
['styled-jsx/style', require.resolve('styled-jsx/style', opts)],
|
|
50
|
+
]));
|
|
51
|
+
}
|
|
52
|
+
if (config.experimental.serverActions) {
|
|
53
|
+
requireHooks.set('experimental', new Map([
|
|
54
|
+
['react', require.resolve(`next/dist/compiled/react-experimental`, opts)],
|
|
55
|
+
['react/jsx-runtime', require.resolve(`next/dist/compiled/react-experimental/jsx-runtime`, opts)],
|
|
56
|
+
['react/jsx-dev-runtime', require.resolve(`next/dist/compiled/react-experimental/jsx-dev-runtime`, opts)],
|
|
57
|
+
['react-dom', require.resolve(`next/dist/compiled/react-dom-experimental/server-rendering-stub`, opts)],
|
|
58
|
+
['react-dom/client', require.resolve(`next/dist/compiled/react-dom-experimental/client`, opts)],
|
|
59
|
+
['react-dom/server', require.resolve(`next/dist/compiled/react-dom-experimental/server`, opts)],
|
|
60
|
+
['react-dom/server.browser', require.resolve(`next/dist/compiled/react-dom-experimental/server.browser`, opts)],
|
|
61
|
+
['react-dom/server.edge', require.resolve(`next/dist/compiled/react-dom-experimental/server.edge`, opts)],
|
|
62
|
+
[
|
|
63
|
+
'react-server-dom-webpack/client',
|
|
64
|
+
require.resolve(`next/dist/compiled/react-server-dom-webpack-experimental/client`, opts),
|
|
65
|
+
],
|
|
66
|
+
[
|
|
67
|
+
'react-server-dom-webpack/client.edge',
|
|
68
|
+
require.resolve(`next/dist/compiled/react-server-dom-webpack-experimental/client.edge`, opts),
|
|
69
|
+
],
|
|
70
|
+
[
|
|
71
|
+
'react-server-dom-webpack/server.edge',
|
|
72
|
+
require.resolve(`next/dist/compiled/react-server-dom-webpack-experimental/server.edge`, opts),
|
|
73
|
+
],
|
|
74
|
+
[
|
|
75
|
+
'react-server-dom-webpack/server.node',
|
|
76
|
+
require.resolve(`next/dist/compiled/react-server-dom-webpack-experimental/server.node`, opts),
|
|
77
|
+
],
|
|
78
|
+
['styled-jsx', require.resolve('styled-jsx', opts)],
|
|
79
|
+
['styled-jsx/style', require.resolve('styled-jsx/style', opts)],
|
|
80
|
+
]));
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
exports.overrideRequireHooks = overrideRequireHooks;
|
|
84
|
+
const applyRequireHooks = () => {
|
|
85
|
+
// eslint-disable-next-line max-params, func-names
|
|
86
|
+
;
|
|
87
|
+
module_1.default._resolveFilename = function (originalResolveFilename, hooks, request, parent, isMain, options) {
|
|
88
|
+
var _a, _b, _c;
|
|
89
|
+
const reactMode = (_a = process.env.__NEXT_PRIVATE_PREBUNDLED_REACT) !== null && _a !== void 0 ? _a : 'default';
|
|
90
|
+
const resolvedRequest = (_c = (_b = hooks.get(reactMode)) === null || _b === void 0 ? void 0 : _b.get(request)) !== null && _c !== void 0 ? _c : request;
|
|
91
|
+
return originalResolveFilename.call(module_1.default, resolvedRequest, parent, isMain, options);
|
|
92
|
+
// We use `bind` here to avoid referencing outside variables to create potential memory leaks.
|
|
93
|
+
}.bind(null, resolveFilename, requireHooks);
|
|
94
|
+
};
|
|
95
|
+
exports.applyRequireHooks = applyRequireHooks;
|
|
96
|
+
/* eslint-enable n/no-extraneous-require, no-underscore-dangle, @typescript-eslint/no-explicit-any */
|
package/lib/templates/server.js
CHANGED
|
@@ -18,8 +18,12 @@ const getNetlifyNextServer = (NextServer) => {
|
|
|
18
18
|
getRequestHandler() {
|
|
19
19
|
const handler = super.getRequestHandler();
|
|
20
20
|
return async (req, res, parsedUrl) => {
|
|
21
|
+
var _a;
|
|
21
22
|
// preserve the URL before Next.js mutates it for i18n
|
|
22
23
|
const { url, headers } = req;
|
|
24
|
+
// conditionally use the prebundled React module
|
|
25
|
+
this.netlifyPrebundleReact(url);
|
|
26
|
+
// intercept on-demand revalidation requests and handle with the Netlify API
|
|
23
27
|
if (headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) {
|
|
24
28
|
// handle on-demand revalidation by purging the ODB cache
|
|
25
29
|
await this.netlifyRevalidate(url);
|
|
@@ -27,12 +31,40 @@ const getNetlifyNextServer = (NextServer) => {
|
|
|
27
31
|
res.statusCode = 200;
|
|
28
32
|
res.setHeader('x-nextjs-cache', 'REVALIDATED');
|
|
29
33
|
res.send();
|
|
34
|
+
return;
|
|
30
35
|
}
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
// force Next to revalidate all requests so that we always have fresh content
|
|
37
|
+
// for our ODBs and middleware is disabled at the origin
|
|
38
|
+
// but ignore in preview mode (prerender_bypass is set to true in preview mode)
|
|
39
|
+
// because otherwise revalidate will override preview mode
|
|
40
|
+
if (!((_a = headers.cookie) === null || _a === void 0 ? void 0 : _a.includes('__prerender_bypass'))) {
|
|
41
|
+
// this header controls whether Next.js will revalidate the page
|
|
42
|
+
// and needs to be set to the preview mode id to enable it
|
|
43
|
+
headers['x-prerender-revalidate'] = this.renderOpts.previewProps.previewModeId;
|
|
33
44
|
}
|
|
45
|
+
return handler(req, res, parsedUrl);
|
|
34
46
|
};
|
|
35
47
|
}
|
|
48
|
+
// doing what they do in https://github.com/vercel/vercel/blob/1663db7ca34d3dd99b57994f801fb30b72fbd2f3/packages/next/src/server-build.ts#L576-L580
|
|
49
|
+
netlifyPrebundleReact(path) {
|
|
50
|
+
var _a;
|
|
51
|
+
const routesManifest = this.getRoutesManifest();
|
|
52
|
+
const appPathsManifest = this.getAppPathsManifest();
|
|
53
|
+
const routes = [...routesManifest.staticRoutes, ...routesManifest.dynamicRoutes];
|
|
54
|
+
const matchedRoute = routes.find((route) => new RegExp(route.regex).test(path));
|
|
55
|
+
const isAppRoute = appPathsManifest && matchedRoute ? appPathsManifest[(0, handlerUtils_1.joinPaths)(matchedRoute.page, 'page')] : false;
|
|
56
|
+
if (isAppRoute) {
|
|
57
|
+
// app routes should use prebundled React
|
|
58
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
59
|
+
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = ((_a = this.nextConfig.experimental) === null || _a === void 0 ? void 0 : _a.serverActions)
|
|
60
|
+
? 'experimental'
|
|
61
|
+
: 'next';
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
// pages routes should use use node_modules React
|
|
65
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
66
|
+
process.env.__NEXT_PRIVATE_PREBUNDLED_REACT = '';
|
|
67
|
+
}
|
|
36
68
|
async netlifyRevalidate(route) {
|
|
37
69
|
try {
|
|
38
70
|
// call netlify API to revalidate the path
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/plugin-nextjs",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.38.0",
|
|
4
4
|
"description": "Run Next.js seamlessly on Netlify",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@delucis/if-env": "^1.1.2",
|
|
40
|
-
"@netlify/build": "^29.
|
|
40
|
+
"@netlify/build": "^29.12.7",
|
|
41
41
|
"@types/fs-extra": "^9.0.13",
|
|
42
42
|
"@types/jest": "^27.4.1",
|
|
43
43
|
"@types/merge-stream": "^1.1.2",
|
|
44
44
|
"@types/node": "^17.0.25",
|
|
45
|
-
"next": "^13.
|
|
45
|
+
"next": "^13.4.1",
|
|
46
46
|
"npm-run-all": "^4.1.5",
|
|
47
47
|
"typescript": "^4.6.3"
|
|
48
48
|
},
|