@netlify/plugin-nextjs 4.35.1-next-server-fix.0 → 4.36.1
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/README.md +1 -1
- package/lib/constants.js +5 -3
- package/lib/helpers/analysis.js +23 -18
- package/lib/helpers/compiler.js +1 -0
- package/lib/helpers/files.js +17 -57
- package/lib/helpers/functions.js +8 -8
- package/lib/helpers/matchers.js +8 -4
- package/lib/helpers/redirects.js +10 -3
- package/lib/helpers/utils.js +12 -0
- package/lib/helpers/verification.js +5 -4
- package/lib/index.js +3 -2
- package/lib/templates/getApiHandler.js +2 -3
- package/lib/templates/getHandler.js +3 -10
- package/lib/templates/getPageResolver.js +1 -1
- package/lib/templates/server.js +1 -1
- package/package.json +3 -3
- package/src/templates/edge/shims.js +27 -3
package/README.md
CHANGED
package/lib/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DIVIDER = exports.LAMBDA_MAX_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;
|
|
3
|
+
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;
|
|
4
4
|
exports.HANDLER_FUNCTION_NAME = '___netlify-handler';
|
|
5
5
|
exports.ODB_FUNCTION_NAME = '___netlify-odb-handler';
|
|
6
6
|
exports.IMAGE_FUNCTION_NAME = '_ipx';
|
|
@@ -29,8 +29,10 @@ exports.CATCH_ALL_REGEX = /\/\[\.{3}(.*)](.json)?$/;
|
|
|
29
29
|
exports.OPTIONAL_CATCH_ALL_REGEX = /\/\[{2}\.{3}(.*)]{2}(.json)?$/;
|
|
30
30
|
exports.DYNAMIC_PARAMETER_REGEX = /\/\[(.*?)]/g;
|
|
31
31
|
exports.MINIMUM_REVALIDATE_SECONDS = 60;
|
|
32
|
-
// 50MB, which is the
|
|
33
|
-
exports.
|
|
32
|
+
// 50MB, which is the warning max
|
|
33
|
+
exports.LAMBDA_WARNING_SIZE = 1024 * 1024 * 50;
|
|
34
|
+
// 250MB, which is the hard max
|
|
35
|
+
exports.LAMBDA_MAX_SIZE = 1024 * 1024 * 250;
|
|
34
36
|
exports.DIVIDER = `
|
|
35
37
|
────────────────────────────────────────────────────────────────
|
|
36
38
|
`;
|
package/lib/helpers/analysis.js
CHANGED
|
@@ -26,6 +26,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
26
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 utils_1 = require("./utils");
|
|
29
30
|
const isEdgeConfig = (config) => ['experimental-edge', 'edge'].includes(config);
|
|
30
31
|
exports.isEdgeConfig = isEdgeConfig;
|
|
31
32
|
const validateConfigValue = (config, apiFilePath) => {
|
|
@@ -61,29 +62,33 @@ let hasWarnedAboutNextVersion = false;
|
|
|
61
62
|
/**
|
|
62
63
|
* Uses Next's swc static analysis to extract the config values from a file.
|
|
63
64
|
*/
|
|
64
|
-
const extractConfigFromFile = async (apiFilePath) => {
|
|
65
|
+
const extractConfigFromFile = async (apiFilePath, appDir) => {
|
|
65
66
|
if (!apiFilePath || !(0, fs_1.existsSync)(apiFilePath)) {
|
|
66
67
|
return {};
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
const extractConstValueModulePath = (0, utils_1.findModuleFromBase)({
|
|
70
|
+
paths: [appDir],
|
|
71
|
+
candidates: ['next/dist/build/analysis/extract-const-value'],
|
|
72
|
+
});
|
|
73
|
+
const parseModulePath = (0, utils_1.findModuleFromBase)({
|
|
74
|
+
paths: [appDir],
|
|
75
|
+
candidates: ['next/dist/build/analysis/parse-module'],
|
|
76
|
+
});
|
|
77
|
+
if (!extractConstValueModulePath || !parseModulePath) {
|
|
78
|
+
if (!hasWarnedAboutNextVersion) {
|
|
79
|
+
console.log("This version of Next.js doesn't support advanced API routes. Skipping...");
|
|
80
|
+
hasWarnedAboutNextVersion = true;
|
|
75
81
|
}
|
|
82
|
+
// Old Next.js version
|
|
83
|
+
return {};
|
|
76
84
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
return {};
|
|
85
|
-
}
|
|
86
|
-
throw error;
|
|
85
|
+
if (!extractConstValue && extractConstValueModulePath) {
|
|
86
|
+
// eslint-disable-next-line import/no-dynamic-require
|
|
87
|
+
extractConstValue = require(extractConstValueModulePath);
|
|
88
|
+
}
|
|
89
|
+
if (!parseModule && parseModulePath) {
|
|
90
|
+
// eslint-disable-next-line prefer-destructuring, @typescript-eslint/no-var-requires, import/no-dynamic-require
|
|
91
|
+
parseModule = require(parseModulePath).parseModule;
|
|
87
92
|
}
|
|
88
93
|
const { extractExportedConstValue, UnsupportedValueError } = extractConstValue;
|
|
89
94
|
const fileContent = await fs_1.default.promises.readFile(apiFilePath, 'utf8');
|
package/lib/helpers/compiler.js
CHANGED
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.
|
|
6
|
+
exports.movePublicFiles = exports.unpatchNextFiles = exports.unpatchFile = exports.patchNextFiles = 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");
|
|
@@ -16,7 +16,6 @@ const constants_1 = require("../constants");
|
|
|
16
16
|
const edge_1 = require("./edge");
|
|
17
17
|
const utils_1 = require("./utils");
|
|
18
18
|
const TEST_ROUTE = /(|\/)\[[^/]+?](\/|\.html|$)/;
|
|
19
|
-
const SOURCE_FILE_EXTENSIONS = ['js', 'jsx', 'ts', 'tsx'];
|
|
20
19
|
const isDynamicRoute = (route) => TEST_ROUTE.test(route);
|
|
21
20
|
exports.isDynamicRoute = isDynamicRoute;
|
|
22
21
|
const stripLocale = (rawPath, locales = []) => {
|
|
@@ -69,7 +68,7 @@ exports.getMiddleware = getMiddleware;
|
|
|
69
68
|
// eslint-disable-next-line max-lines-per-function
|
|
70
69
|
const moveStaticPages = async ({ netlifyConfig, target, i18n, basePath, }) => {
|
|
71
70
|
console.log('Moving static page files to serve from CDN...');
|
|
72
|
-
const outputDir = (0, pathe_1.join)(netlifyConfig.build.publish, target === '
|
|
71
|
+
const outputDir = (0, pathe_1.join)(netlifyConfig.build.publish, target === 'serverless' ? 'serverless' : 'server');
|
|
73
72
|
const buildId = (0, fs_extra_1.readFileSync)((0, pathe_1.join)(netlifyConfig.build.publish, 'BUILD_ID'), 'utf8').trim();
|
|
74
73
|
const dataDir = (0, pathe_1.join)('_next', 'data', buildId);
|
|
75
74
|
await (0, fs_extra_1.ensureDir)((0, pathe_1.join)(netlifyConfig.build.publish, dataDir));
|
|
@@ -278,60 +277,16 @@ const getServerFile = (root, includeBase = true) => {
|
|
|
278
277
|
}
|
|
279
278
|
return (0, utils_1.findModuleFromBase)({ candidates, paths: [root] });
|
|
280
279
|
};
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
const
|
|
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;
|
|
280
|
+
exports.getServerFile = getServerFile;
|
|
281
|
+
// Next.js already defines a default `pageExtensions` array in its `required-server-files.json` file
|
|
282
|
+
// In case it gets `undefined`, this is a fallback
|
|
283
|
+
const SOURCE_FILE_EXTENSIONS = ['js', 'jsx', 'ts', 'tsx'];
|
|
329
284
|
/**
|
|
330
285
|
* Find the source file for a given page route
|
|
331
286
|
*/
|
|
332
|
-
const getSourceFileForPage = (page, roots) => {
|
|
287
|
+
const getSourceFileForPage = (page, roots, pageExtensions = SOURCE_FILE_EXTENSIONS) => {
|
|
333
288
|
for (const root of roots) {
|
|
334
|
-
for (const extension of
|
|
289
|
+
for (const extension of pageExtensions) {
|
|
335
290
|
const file = (0, pathe_1.join)(root, `${page}.${extension}`);
|
|
336
291
|
if ((0, fs_extra_1.existsSync)(file)) {
|
|
337
292
|
return file;
|
|
@@ -359,6 +314,11 @@ const baseServerReplacements = [
|
|
|
359
314
|
`checkIsManualRevalidate(req, this.renderOpts.previewProps)`,
|
|
360
315
|
`checkIsManualRevalidate(process.env._REVALIDATE_SSG ? { headers: { 'x-prerender-revalidate': this.renderOpts.previewProps.previewModeId } } : req, this.renderOpts.previewProps)`,
|
|
361
316
|
],
|
|
317
|
+
// In https://github.com/vercel/next.js/pull/47803 checkIsManualRevalidate was renamed to checkIsOnDemandRevalidate
|
|
318
|
+
[
|
|
319
|
+
`checkIsOnDemandRevalidate(req, this.renderOpts.previewProps)`,
|
|
320
|
+
`checkIsOnDemandRevalidate(process.env._REVALIDATE_SSG ? { headers: { 'x-prerender-revalidate': this.renderOpts.previewProps.previewModeId } } : req, this.renderOpts.previewProps)`,
|
|
321
|
+
],
|
|
362
322
|
// ensure ISR 404 pages send the correct SWR cache headers
|
|
363
323
|
[`private: isPreviewMode || is404Page && cachedData`, `private: isPreviewMode && cachedData`],
|
|
364
324
|
];
|
|
@@ -381,7 +341,7 @@ const nextServerReplacements = [
|
|
|
381
341
|
],
|
|
382
342
|
];
|
|
383
343
|
const patchNextFiles = async (root) => {
|
|
384
|
-
const baseServerFile = getServerFile(root);
|
|
344
|
+
const baseServerFile = (0, exports.getServerFile)(root);
|
|
385
345
|
console.log(`Patching ${baseServerFile}`);
|
|
386
346
|
if (baseServerFile) {
|
|
387
347
|
await patchFile({
|
|
@@ -389,7 +349,7 @@ const patchNextFiles = async (root) => {
|
|
|
389
349
|
replacements: baseServerReplacements,
|
|
390
350
|
});
|
|
391
351
|
}
|
|
392
|
-
const nextServerFile = getServerFile(root, false);
|
|
352
|
+
const nextServerFile = (0, exports.getServerFile)(root, false);
|
|
393
353
|
console.log(`Patching ${nextServerFile}`);
|
|
394
354
|
if (nextServerFile) {
|
|
395
355
|
await patchFile({
|
|
@@ -407,9 +367,9 @@ const unpatchFile = async (file) => {
|
|
|
407
367
|
};
|
|
408
368
|
exports.unpatchFile = unpatchFile;
|
|
409
369
|
const unpatchNextFiles = async (root) => {
|
|
410
|
-
const baseServerFile = getServerFile(root);
|
|
370
|
+
const baseServerFile = (0, exports.getServerFile)(root);
|
|
411
371
|
await (0, exports.unpatchFile)(baseServerFile);
|
|
412
|
-
const nextServerFile = getServerFile(root, false);
|
|
372
|
+
const nextServerFile = (0, exports.getServerFile)(root, false);
|
|
413
373
|
if (nextServerFile !== baseServerFile) {
|
|
414
374
|
await (0, exports.unpatchFile)(nextServerFile);
|
|
415
375
|
}
|
package/lib/helpers/functions.js
CHANGED
|
@@ -23,7 +23,7 @@ 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.
|
|
26
|
+
const nextServerModuleAbsoluteLocation = (0, files_1.getServerFile)(appDir, false);
|
|
27
27
|
const nextServerModuleRelativeLocation = nextServerModuleAbsoluteLocation
|
|
28
28
|
? (0, pathe_1.relative)(functionDir, nextServerModuleAbsoluteLocation)
|
|
29
29
|
: undefined;
|
|
@@ -146,24 +146,24 @@ exports.setupImageFunction = setupImageFunction;
|
|
|
146
146
|
/**
|
|
147
147
|
* Look for API routes, and extract the config from the source file.
|
|
148
148
|
*/
|
|
149
|
-
const getApiRouteConfigs = async (publish,
|
|
149
|
+
const getApiRouteConfigs = async (publish, appDir, pageExtensions) => {
|
|
150
150
|
const pages = await (0, fs_extra_1.readJSON)((0, pathe_1.join)(publish, 'server', 'pages-manifest.json'));
|
|
151
151
|
const apiRoutes = Object.keys(pages).filter((page) => page.startsWith('/api/'));
|
|
152
152
|
// two possible places
|
|
153
153
|
// Ref: https://nextjs.org/docs/advanced-features/src-directory
|
|
154
|
-
const pagesDir = (0, pathe_1.join)(
|
|
155
|
-
const srcPagesDir = (0, pathe_1.join)(
|
|
154
|
+
const pagesDir = (0, pathe_1.join)(appDir, 'pages');
|
|
155
|
+
const srcPagesDir = (0, pathe_1.join)(appDir, 'src', 'pages');
|
|
156
156
|
return await Promise.all(apiRoutes.map(async (apiRoute) => {
|
|
157
|
-
const filePath = (0, files_1.getSourceFileForPage)(apiRoute, [pagesDir, srcPagesDir]);
|
|
158
|
-
return { route: apiRoute, config: await (0, analysis_1.extractConfigFromFile)(filePath), compiled: pages[apiRoute] };
|
|
157
|
+
const filePath = (0, files_1.getSourceFileForPage)(apiRoute, [pagesDir, srcPagesDir], pageExtensions);
|
|
158
|
+
return { route: apiRoute, config: await (0, analysis_1.extractConfigFromFile)(filePath, appDir), compiled: pages[apiRoute] };
|
|
159
159
|
}));
|
|
160
160
|
};
|
|
161
161
|
exports.getApiRouteConfigs = getApiRouteConfigs;
|
|
162
162
|
/**
|
|
163
163
|
* Looks for extended API routes (background and scheduled functions) and extract the config from the source file.
|
|
164
164
|
*/
|
|
165
|
-
const getExtendedApiRouteConfigs = async (publish,
|
|
166
|
-
const settledApiRoutes = await (0, exports.getApiRouteConfigs)(publish,
|
|
165
|
+
const getExtendedApiRouteConfigs = async (publish, appDir, pageExtensions) => {
|
|
166
|
+
const settledApiRoutes = await (0, exports.getApiRouteConfigs)(publish, appDir, pageExtensions);
|
|
167
167
|
// We only want to return the API routes that are background or scheduled functions
|
|
168
168
|
return settledApiRoutes.filter((apiRoute) => apiRoute.config.type !== undefined);
|
|
169
169
|
};
|
package/lib/helpers/matchers.js
CHANGED
|
@@ -10,7 +10,7 @@ const stripLookahead = (regex) => {
|
|
|
10
10
|
}
|
|
11
11
|
try {
|
|
12
12
|
// Parse the regexp into an AST
|
|
13
|
-
const re = (0, regexp_tree_1.transform)(
|
|
13
|
+
const re = (0, regexp_tree_1.transform)(new RegExp(regex), {
|
|
14
14
|
Assertion(path) {
|
|
15
15
|
// Remove the lookahead
|
|
16
16
|
if (path.node.kind === 'Lookahead') {
|
|
@@ -30,8 +30,12 @@ exports.stripLookahead = stripLookahead;
|
|
|
30
30
|
// The Go regexp lib has alternative syntax for named capture groups
|
|
31
31
|
const transformCaptureGroups = (regex) => regex.replace(/\(\?<\w+>/, '(');
|
|
32
32
|
exports.transformCaptureGroups = transformCaptureGroups;
|
|
33
|
-
const
|
|
34
|
-
const
|
|
33
|
+
const LOCALIZED_REGEX_PREFIX_13_1 = '(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))';
|
|
34
|
+
const OPTIONAL_REGEX_PREFIX_13_1 = '(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))?';
|
|
35
|
+
const LOCALIZED_REGEX_PREFIX_13_3 = '(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/((?!_next\\/)[^/.]{1,}))';
|
|
36
|
+
const OPTIONAL_REGEX_PREFIX_13_3 = '(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/((?!_next\\/)[^/.]{1,}))?';
|
|
35
37
|
// Make the locale section of the matcher regex optional
|
|
36
|
-
const makeLocaleOptional = (regex) => regex
|
|
38
|
+
const makeLocaleOptional = (regex) => regex
|
|
39
|
+
.replace(LOCALIZED_REGEX_PREFIX_13_1, OPTIONAL_REGEX_PREFIX_13_1)
|
|
40
|
+
.replace(LOCALIZED_REGEX_PREFIX_13_3, OPTIONAL_REGEX_PREFIX_13_3);
|
|
37
41
|
exports.makeLocaleOptional = makeLocaleOptional;
|
package/lib/helpers/redirects.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.generateRedirects = exports.generateStaticRedirects = void 0;
|
|
6
|
+
exports.generateRedirects = exports.generateDynamicRewrites = exports.generateStaticRedirects = void 0;
|
|
4
7
|
const chalk_1 = require("chalk");
|
|
8
|
+
const destr_1 = __importDefault(require("destr"));
|
|
5
9
|
const fs_extra_1 = require("fs-extra");
|
|
6
10
|
const outdent_1 = require("outdent");
|
|
7
11
|
const pathe_1 = require("pathe");
|
|
@@ -160,7 +164,9 @@ const generateDynamicRewrites = ({ dynamicRoutes, prerenderedDynamicRoutes, midd
|
|
|
160
164
|
withData: true,
|
|
161
165
|
}));
|
|
162
166
|
}
|
|
163
|
-
else if (prerenderedDynamicRoutes[route.page].fallback === false &&
|
|
167
|
+
else if (prerenderedDynamicRoutes[route.page].fallback === false &&
|
|
168
|
+
!is404Isr &&
|
|
169
|
+
!(0, destr_1.default)(process.env.LEGACY_FALLBACK_FALSE)) {
|
|
164
170
|
dynamicRewrites.push(...(0, utils_1.redirectsForNext404Route)({ route: route.page, buildId, basePath, i18n }));
|
|
165
171
|
}
|
|
166
172
|
else {
|
|
@@ -177,6 +183,7 @@ const generateDynamicRewrites = ({ dynamicRoutes, prerenderedDynamicRoutes, midd
|
|
|
177
183
|
dynamicRewrites,
|
|
178
184
|
};
|
|
179
185
|
};
|
|
186
|
+
exports.generateDynamicRewrites = generateDynamicRewrites;
|
|
180
187
|
const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath, trailingSlash, appDir }, buildId, apiRoutes, }) => {
|
|
181
188
|
const { dynamicRoutes: prerenderedDynamicRoutes, routes: prerenderedStaticRoutes } = await (0, fs_extra_1.readJSON)((0, pathe_1.join)(netlifyConfig.build.publish, 'prerender-manifest.json'));
|
|
182
189
|
const { dynamicRoutes, staticRoutes } = await (0, fs_extra_1.readJSON)((0, pathe_1.join)(netlifyConfig.build.publish, 'routes-manifest.json'));
|
|
@@ -215,7 +222,7 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
|
|
|
215
222
|
netlifyConfig.redirects.push(...(0, utils_1.redirectsForNextRoute)({ route: route.page, buildId, basePath, to: constants_1.HANDLER_FUNCTION_PATH, i18n }));
|
|
216
223
|
});
|
|
217
224
|
// Add rewrites for all dynamic routes (both SSR and ISR)
|
|
218
|
-
const { dynamicRewrites, dynamicRoutesThatMatchMiddleware } = generateDynamicRewrites({
|
|
225
|
+
const { dynamicRewrites, dynamicRoutesThatMatchMiddleware } = (0, exports.generateDynamicRewrites)({
|
|
219
226
|
dynamicRoutes,
|
|
220
227
|
prerenderedDynamicRoutes,
|
|
221
228
|
middleware,
|
package/lib/helpers/utils.js
CHANGED
|
@@ -169,6 +169,18 @@ const findModuleFromBase = ({ paths, candidates }) => {
|
|
|
169
169
|
// Ignore the error
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
+
// if we couldn't find a module from paths, let's try to resolve from here
|
|
173
|
+
for (const candidate of candidates) {
|
|
174
|
+
try {
|
|
175
|
+
const modulePath = require.resolve(candidate);
|
|
176
|
+
if (modulePath) {
|
|
177
|
+
return modulePath;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
// Ignore the error
|
|
182
|
+
}
|
|
183
|
+
}
|
|
172
184
|
return null;
|
|
173
185
|
};
|
|
174
186
|
exports.findModuleFromBase = findModuleFromBase;
|
|
@@ -102,7 +102,7 @@ const checkForRootPublish = ({ publish, failBuild, }) => {
|
|
|
102
102
|
}
|
|
103
103
|
};
|
|
104
104
|
exports.checkForRootPublish = checkForRootPublish;
|
|
105
|
-
const checkZipSize = async (file, maxSize = constants_1.LAMBDA_MAX_SIZE) => {
|
|
105
|
+
const checkZipSize = async (file, maxSize = constants_1.LAMBDA_MAX_SIZE, warningSize = constants_1.LAMBDA_WARNING_SIZE) => {
|
|
106
106
|
// Requires contacting the Netlify Support team to fully enable.
|
|
107
107
|
// Enabling this without contacting them can result in failed deploys.
|
|
108
108
|
if ((0, utils_1.isBundleSizeCheckDisabled)()) {
|
|
@@ -114,12 +114,13 @@ const checkZipSize = async (file, maxSize = constants_1.LAMBDA_MAX_SIZE) => {
|
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
116
116
|
const fileSize = await fs_1.promises.stat(file).then(({ size }) => size);
|
|
117
|
-
if (fileSize <
|
|
117
|
+
if (fileSize < warningSize) {
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
// We don't fail the build, because the actual hard max size is larger so it might still succeed
|
|
121
|
-
console.log((0, chalk_1.
|
|
122
|
-
The function zip ${(0, chalk_1.
|
|
121
|
+
console.log((0, chalk_1.yellowBright)((0, outdent_1.outdent) `
|
|
122
|
+
The function zip ${(0, chalk_1.blueBright)((0, path_1.relative)(process.cwd(), file))} size is ${(0, pretty_bytes_1.default)(fileSize)}, which is larger than the recommended maximum size of ${(0, pretty_bytes_1.default)(warningSize)}.
|
|
123
|
+
This will fail the build if the unzipped size is bigger than the maximum size of ${(0, pretty_bytes_1.default)(maxSize)}.
|
|
123
124
|
There are a few reasons this could happen. You may have accidentally bundled a large dependency, or you might have a
|
|
124
125
|
large number of pre-rendered pages included.
|
|
125
126
|
`));
|
package/lib/index.js
CHANGED
|
@@ -37,13 +37,14 @@ const plugin = {
|
|
|
37
37
|
// eslint-disable-next-line unicorn/consistent-destructuring
|
|
38
38
|
netlifyConfig.build.environment.NEXT_PRIVATE_TARGET = 'server';
|
|
39
39
|
},
|
|
40
|
+
// eslint-disable-next-line max-lines-per-function
|
|
40
41
|
async onBuild({ constants, netlifyConfig, utils: { build: { failBuild }, }, }) {
|
|
41
42
|
if ((0, utils_1.shouldSkip)()) {
|
|
42
43
|
return;
|
|
43
44
|
}
|
|
44
45
|
const { publish } = netlifyConfig.build;
|
|
45
46
|
(0, verification_1.checkNextSiteHasBuilt)({ publish, failBuild });
|
|
46
|
-
const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir, experimental, routesManifest } = await (0, config_1.getNextConfig)({
|
|
47
|
+
const { appDir, basePath, i18n, images, target, ignore, trailingSlash, outdir, experimental, routesManifest, pageExtensions, } = await (0, config_1.getNextConfig)({
|
|
47
48
|
publish,
|
|
48
49
|
failBuild,
|
|
49
50
|
});
|
|
@@ -91,7 +92,7 @@ const plugin = {
|
|
|
91
92
|
}
|
|
92
93
|
const buildId = (0, fs_extra_1.readFileSync)((0, path_1.join)(publish, 'BUILD_ID'), 'utf8').trim();
|
|
93
94
|
await (0, config_1.configureHandlerFunctions)({ netlifyConfig, ignore, publish: (0, path_1.relative)(process.cwd(), publish) });
|
|
94
|
-
const apiRoutes = await (0, functions_1.getExtendedApiRouteConfigs)(publish, appDir);
|
|
95
|
+
const apiRoutes = await (0, functions_1.getExtendedApiRouteConfigs)(publish, appDir, pageExtensions);
|
|
95
96
|
await (0, functions_1.generateFunctions)(constants, appDir, apiRoutes);
|
|
96
97
|
await (0, functions_1.generatePagesResolver)(constants);
|
|
97
98
|
await (0, files_1.movePublicFiles)({ appDir, outdir, publish, basePath });
|
|
@@ -11,8 +11,7 @@ const { URLSearchParams, URL } = require('url');
|
|
|
11
11
|
const { Bridge } = require('@vercel/node-bridge/bridge');
|
|
12
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
|
-
|
|
15
|
-
const makeHandler = (conf, app, pageRoot, page, NextServer) => {
|
|
14
|
+
const makeApiHandler = ({ conf, app, pageRoot, page, NextServer }) => {
|
|
16
15
|
var _a;
|
|
17
16
|
// Change working directory into the site root, unless using Nx, which moves the
|
|
18
17
|
// dist directory and handles this itself
|
|
@@ -111,7 +110,7 @@ const getApiHandler = ({ page, config, publishDir = '../../../.next', appDir = '
|
|
|
111
110
|
let staticManifest
|
|
112
111
|
const path = require("path");
|
|
113
112
|
const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server"));
|
|
114
|
-
const handler = (${
|
|
113
|
+
const handler = (${makeApiHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, page:${JSON.stringify(page)}, NextServer})
|
|
115
114
|
exports.handler = ${config.type === "experimental-scheduled" /* ApiRouteType.SCHEDULED */ ? `schedule(${JSON.stringify(config.schedule)}, handler);` : 'handler'}
|
|
116
115
|
`;
|
|
117
116
|
exports.getApiHandler = getApiHandler;
|
|
@@ -14,7 +14,7 @@ const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, n
|
|
|
14
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
16
|
// eslint-disable-next-line max-lines-per-function
|
|
17
|
-
const makeHandler = (conf, app, pageRoot, NextServer, staticManifest = [], mode = 'ssr') => {
|
|
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
|
|
@@ -87,13 +87,6 @@ const makeHandler = (conf, app, pageRoot, NextServer, staticManifest = [], mode
|
|
|
87
87
|
// Next expects to be able to parse the query from the URL
|
|
88
88
|
const query = new URLSearchParams(event.queryStringParameters).toString();
|
|
89
89
|
event.path = query ? `${event.path}?${query}` : event.path;
|
|
90
|
-
const graphToken = event.netlifyGraphToken;
|
|
91
|
-
if (graphToken && requestMode !== 'ssr') {
|
|
92
|
-
// Prefix with underscore to help us determine the origin of the token
|
|
93
|
-
// allows us to write better error messages
|
|
94
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
95
|
-
process.env._NETLIFY_GRAPH_TOKEN = graphToken;
|
|
96
|
-
}
|
|
97
90
|
const { headers, ...result } = await getBridge(event, context).launcher(event, context);
|
|
98
91
|
// Convert all headers to multiValueHeaders
|
|
99
92
|
const multiValueHeaders = getMultiValueHeaders(headers);
|
|
@@ -162,7 +155,7 @@ const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '..
|
|
|
162
155
|
const path = require("path");
|
|
163
156
|
const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server"));
|
|
164
157
|
exports.handler = ${isODB
|
|
165
|
-
? `builder((${makeHandler.toString()})(config, "${appDir}", pageRoot, NextServer, staticManifest, 'odb'));`
|
|
166
|
-
: `(${makeHandler.toString()})(config, "${appDir}", pageRoot, NextServer, staticManifest, 'ssr');`}
|
|
158
|
+
? `builder((${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'odb' }));`
|
|
159
|
+
: `(${makeHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, NextServer, staticManifest, mode: 'ssr' });`}
|
|
167
160
|
`;
|
|
168
161
|
exports.getHandler = getHandler;
|
|
@@ -33,7 +33,7 @@ const getResolverForDependencies = ({ dependencies, functionDir, }) => {
|
|
|
33
33
|
// This file is purely to allow nft to know about these pages.
|
|
34
34
|
exports.resolvePages = () => {
|
|
35
35
|
try {
|
|
36
|
-
${pageFiles.join('\n ')}
|
|
36
|
+
${pageFiles.sort().join('\n ')}
|
|
37
37
|
} catch {}
|
|
38
38
|
}
|
|
39
39
|
`;
|
package/lib/templates/server.js
CHANGED
|
@@ -64,7 +64,7 @@ const getNetlifyNextServer = (NextServer) => {
|
|
|
64
64
|
for (const dynamicRoute in dynamicRoutes) {
|
|
65
65
|
const { dataRoute, routeRegex } = dynamicRoutes[dynamicRoute];
|
|
66
66
|
const matches = unlocalizedRoute.match(routeRegex);
|
|
67
|
-
if (matches
|
|
67
|
+
if ((matches === null || matches === void 0 ? void 0 : matches.length) > 0) {
|
|
68
68
|
// remove the first match, which is the full route
|
|
69
69
|
matches.shift();
|
|
70
70
|
// replace the dynamic segments with the actual values
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/plugin-nextjs",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.36.1",
|
|
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.11.1",
|
|
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.3.0",
|
|
46
46
|
"npm-run-all": "^4.1.5",
|
|
47
47
|
"typescript": "^4.6.3"
|
|
48
48
|
},
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
// deno-lint-ignore-file
|
|
2
|
+
// deno-lint-ignore-file prefer-const no-unused-vars
|
|
3
3
|
import { decode as _base64Decode } from 'https://deno.land/std@0.175.0/encoding/base64.ts'
|
|
4
|
-
import
|
|
4
|
+
import BufferCompat from 'https://deno.land/std@0.175.0/node/buffer.ts'
|
|
5
|
+
import EventsCompat from 'https://deno.land/std@0.175.0/node/events.ts'
|
|
6
|
+
import AsyncHooksCompat from 'https://deno.land/std@0.175.0/node/async_hooks.ts'
|
|
7
|
+
import AssertCompat from 'https://deno.land/std@0.175.0/node/assert.ts'
|
|
8
|
+
import UtilCompat from 'https://deno.land/std@0.175.0/node/util.ts'
|
|
5
9
|
|
|
6
10
|
/**
|
|
7
11
|
* These are the shims, polyfills and other kludges to make Next.js work in standards-compliant runtime.
|
|
@@ -18,7 +22,7 @@ globalThis.EdgeRuntime = 'netlify-edge'
|
|
|
18
22
|
let _ENTRIES = {}
|
|
19
23
|
|
|
20
24
|
// Next.js expects this as a global
|
|
21
|
-
globalThis.AsyncLocalStorage =
|
|
25
|
+
globalThis.AsyncLocalStorage = AsyncHooksCompat.AsyncLocalStorage
|
|
22
26
|
|
|
23
27
|
// Next.js uses this extension to the Headers API implemented by Cloudflare workerd
|
|
24
28
|
if (!('getAll' in Headers.prototype)) {
|
|
@@ -48,6 +52,26 @@ const fetch /* type {typeof globalThis.fetch} */ = async (url, init) => {
|
|
|
48
52
|
}
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
// Shim native modules that Vercel makes available
|
|
56
|
+
if (typeof require === 'undefined') {
|
|
57
|
+
globalThis.require = (name) => {
|
|
58
|
+
switch (name.replace(/^node:/, '')) {
|
|
59
|
+
case 'buffer':
|
|
60
|
+
return BufferCompat
|
|
61
|
+
case 'events':
|
|
62
|
+
return EventsCompat
|
|
63
|
+
case 'async_hooks':
|
|
64
|
+
return AsyncHooksCompat
|
|
65
|
+
case 'assert':
|
|
66
|
+
return AssertCompat
|
|
67
|
+
case 'util':
|
|
68
|
+
return UtilCompat
|
|
69
|
+
default:
|
|
70
|
+
throw new ReferenceError(`Native module not found: ${name}`)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
51
75
|
// Next edge runtime uses "self" as a function-scoped global-like object, but some of the older polyfills expect it to equal globalThis
|
|
52
76
|
// See https://nextjs.org/docs/basic-features/supported-browsers-features#polyfills
|
|
53
77
|
const self = { ...globalThis, fetch }
|