@netlify/plugin-nextjs 4.36.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 CHANGED
@@ -1,4 +1,4 @@
1
- ![Next.js Runtime](https://github.com/netlify/next-runtime/raw/main/next-on-netlify.png)
1
+ ![Next.js Runtime](https://github.com/netlify/next-runtime/raw/main/next-js-runtime.png)
2
2
 
3
3
  # `@netlify/plugin-nextjs`
4
4
 
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 documented max, though the hard max seems to be higher
33
- exports.LAMBDA_MAX_SIZE = 1024 * 1024 * 50;
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
  `;
@@ -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
- try {
69
- if (!extractConstValue) {
70
- extractConstValue = require('next/dist/build/analysis/extract-const-value');
71
- }
72
- if (!parseModule) {
73
- // eslint-disable-next-line prefer-destructuring, @typescript-eslint/no-var-requires
74
- parseModule = require('next/dist/build/analysis/parse-module').parseModule;
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
- catch (error) {
78
- if (error.code === 'MODULE_NOT_FOUND') {
79
- if (!hasWarnedAboutNextVersion) {
80
- console.log("This version of Next.js doesn't support advanced API routes. Skipping...");
81
- hasWarnedAboutNextVersion = true;
82
- }
83
- // Old Next.js version
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');
@@ -27,6 +27,7 @@ const buildMiddlewareFile = async (entryPoints, base) => {
27
27
  format: 'esm',
28
28
  target: 'esnext',
29
29
  absWorkingDir: base,
30
+ external: ['next/dist/compiled/@vercel/og'],
30
31
  });
31
32
  }
32
33
  catch (error) {
@@ -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.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 = []) => {
@@ -107,7 +106,6 @@ const moveStaticPages = async ({ netlifyConfig, target, i18n, basePath, }) => {
107
106
  filesManifest[file] = targetPath;
108
107
  const dest = (0, pathe_1.join)(netlifyConfig.build.publish, targetPath);
109
108
  try {
110
- console.log(`Moving ${source} to ${dest}`);
111
109
  await (0, fs_extra_1.move)(source, dest);
112
110
  }
113
111
  catch (error) {
@@ -279,12 +277,16 @@ const getServerFile = (root, includeBase = true) => {
279
277
  }
280
278
  return (0, utils_1.findModuleFromBase)({ candidates, paths: [root] });
281
279
  };
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'];
282
284
  /**
283
285
  * Find the source file for a given page route
284
286
  */
285
- const getSourceFileForPage = (page, roots) => {
287
+ const getSourceFileForPage = (page, roots, pageExtensions = SOURCE_FILE_EXTENSIONS) => {
286
288
  for (const root of roots) {
287
- for (const extension of SOURCE_FILE_EXTENSIONS) {
289
+ for (const extension of pageExtensions) {
288
290
  const file = (0, pathe_1.join)(root, `${page}.${extension}`);
289
291
  if ((0, fs_extra_1.existsSync)(file)) {
290
292
  return file;
@@ -312,6 +314,11 @@ const baseServerReplacements = [
312
314
  `checkIsManualRevalidate(req, this.renderOpts.previewProps)`,
313
315
  `checkIsManualRevalidate(process.env._REVALIDATE_SSG ? { headers: { 'x-prerender-revalidate': this.renderOpts.previewProps.previewModeId } } : req, this.renderOpts.previewProps)`,
314
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
+ ],
315
322
  // ensure ISR 404 pages send the correct SWR cache headers
316
323
  [`private: isPreviewMode || is404Page && cachedData`, `private: isPreviewMode && cachedData`],
317
324
  ];
@@ -334,7 +341,7 @@ const nextServerReplacements = [
334
341
  ],
335
342
  ];
336
343
  const patchNextFiles = async (root) => {
337
- const baseServerFile = getServerFile(root);
344
+ const baseServerFile = (0, exports.getServerFile)(root);
338
345
  console.log(`Patching ${baseServerFile}`);
339
346
  if (baseServerFile) {
340
347
  await patchFile({
@@ -342,7 +349,7 @@ const patchNextFiles = async (root) => {
342
349
  replacements: baseServerReplacements,
343
350
  });
344
351
  }
345
- const nextServerFile = getServerFile(root, false);
352
+ const nextServerFile = (0, exports.getServerFile)(root, false);
346
353
  console.log(`Patching ${nextServerFile}`);
347
354
  if (nextServerFile) {
348
355
  await patchFile({
@@ -360,9 +367,9 @@ const unpatchFile = async (file) => {
360
367
  };
361
368
  exports.unpatchFile = unpatchFile;
362
369
  const unpatchNextFiles = async (root) => {
363
- const baseServerFile = getServerFile(root);
370
+ const baseServerFile = (0, exports.getServerFile)(root);
364
371
  await (0, exports.unpatchFile)(baseServerFile);
365
- const nextServerFile = getServerFile(root, false);
372
+ const nextServerFile = (0, exports.getServerFile)(root, false);
366
373
  if (nextServerFile !== baseServerFile) {
367
374
  await (0, exports.unpatchFile)(nextServerFile);
368
375
  }
@@ -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.getServerFile)(appDir, false);
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)({ isODB, publishDir, appDir: (0, pathe_1.relative)(functionDir, appDir) });
59
+ const handlerSource = await (0, getHandler_1.getHandler)({
60
+ isODB,
61
+ publishDir,
62
+ appDir: (0, pathe_1.relative)(functionDir, appDir),
63
+ nextServerModuleRelativeLocation,
64
+ });
55
65
  await (0, fs_extra_1.ensureDir)((0, pathe_1.join)(functionsDir, functionName));
56
66
  // write main handler file (standard or ODB)
57
67
  await (0, fs_extra_1.writeFile)((0, pathe_1.join)(functionsDir, functionName, `${functionName}.js`), handlerSource);
@@ -136,24 +146,24 @@ exports.setupImageFunction = setupImageFunction;
136
146
  /**
137
147
  * Look for API routes, and extract the config from the source file.
138
148
  */
139
- const getApiRouteConfigs = async (publish, baseDir) => {
149
+ const getApiRouteConfigs = async (publish, appDir, pageExtensions) => {
140
150
  const pages = await (0, fs_extra_1.readJSON)((0, pathe_1.join)(publish, 'server', 'pages-manifest.json'));
141
151
  const apiRoutes = Object.keys(pages).filter((page) => page.startsWith('/api/'));
142
152
  // two possible places
143
153
  // Ref: https://nextjs.org/docs/advanced-features/src-directory
144
- const pagesDir = (0, pathe_1.join)(baseDir, 'pages');
145
- const srcPagesDir = (0, pathe_1.join)(baseDir, 'src', 'pages');
154
+ const pagesDir = (0, pathe_1.join)(appDir, 'pages');
155
+ const srcPagesDir = (0, pathe_1.join)(appDir, 'src', 'pages');
146
156
  return await Promise.all(apiRoutes.map(async (apiRoute) => {
147
- const filePath = (0, files_1.getSourceFileForPage)(apiRoute, [pagesDir, srcPagesDir]);
148
- 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] };
149
159
  }));
150
160
  };
151
161
  exports.getApiRouteConfigs = getApiRouteConfigs;
152
162
  /**
153
163
  * Looks for extended API routes (background and scheduled functions) and extract the config from the source file.
154
164
  */
155
- const getExtendedApiRouteConfigs = async (publish, baseDir) => {
156
- const settledApiRoutes = await (0, exports.getApiRouteConfigs)(publish, baseDir);
165
+ const getExtendedApiRouteConfigs = async (publish, appDir, pageExtensions) => {
166
+ const settledApiRoutes = await (0, exports.getApiRouteConfigs)(publish, appDir, pageExtensions);
157
167
  // We only want to return the API routes that are background or scheduled functions
158
168
  return settledApiRoutes.filter((apiRoute) => apiRoute.config.type !== undefined);
159
169
  };
@@ -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)(`/${regex}/`, {
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 LOCALIZED_REGEX_PREFIX = '(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))';
34
- const OPTIONAL_REGEX_PREFIX = '(?:\\/(_next\\/data\\/[^/]{1,}))?(?:\\/([^/.]{1,}))?';
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.replace(LOCALIZED_REGEX_PREFIX, OPTIONAL_REGEX_PREFIX);
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;
@@ -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 && !is404Isr) {
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,
@@ -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 < maxSize) {
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.redBright)((0, outdent_1.outdent) `
122
- The function zip ${(0, chalk_1.yellowBright)((0, path_1.relative)(process.cwd(), file))} size is ${(0, pretty_bytes_1.default)(fileSize)}, which is larger than the maximum supported size of ${(0, pretty_bytes_1.default)(maxSize)}.
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 });
@@ -9,9 +9,9 @@ const path = require('path');
9
9
  // eslint-disable-next-line n/prefer-global/url, n/prefer-global/url-search-params
10
10
  const { URLSearchParams, URL } = require('url');
11
11
  const { Bridge } = require('@vercel/node-bridge/bridge');
12
- const { getMultiValueHeaders, getNextServer } = require('./handlerUtils');
12
+ const { getMultiValueHeaders } = require('./handlerUtils');
13
13
  // We return a function and then call `toString()` on it to serialise it as the launcher function
14
- const makeHandler = (conf, app, pageRoot, page) => {
14
+ const makeApiHandler = ({ conf, app, pageRoot, page, NextServer }) => {
15
15
  var _a;
16
16
  // Change working directory into the site root, unless using Nx, which moves the
17
17
  // dist directory and handles this itself
@@ -46,7 +46,6 @@ const makeHandler = (conf, app, pageRoot, page) => {
46
46
  // Scheduled functions don't have a URL, but we need to give one so Next knows the route to serve
47
47
  const url = event.rawUrl ? new URL(event.rawUrl) : new URL(path, process.env.URL || 'http://n');
48
48
  const port = Number.parseInt(url.port) || 80;
49
- const NextServer = getNextServer();
50
49
  const nextServer = new NextServer({
51
50
  conf,
52
51
  dir,
@@ -91,13 +90,18 @@ const makeHandler = (conf, app, pageRoot, page) => {
91
90
  /**
92
91
  * Handlers for API routes are simpler than page routes, but they each have a separate one
93
92
  */
94
- const getApiHandler = ({ page, config, publishDir = '../../../.next', appDir = '../../..', }) =>
93
+ const getApiHandler = ({ page, config, publishDir = '../../../.next', appDir = '../../..', nextServerModuleRelativeLocation, }) =>
95
94
  // This is a string, but if you have the right editor plugin it should format as js
96
95
  (0, outdent_1.outdent /* javascript */) `
96
+ if (!${JSON.stringify(nextServerModuleRelativeLocation)}) {
97
+ throw new Error('Could not find Next.js server')
98
+ }
99
+
97
100
  const { Server } = require("http");
98
101
  // We copy the file here rather than requiring from the node module
99
102
  const { Bridge } = require("./bridge");
100
- const { getMultiValueHeaders, getNextServer } = require('./handlerUtils')
103
+ const { getMultiValueHeaders } = require('./handlerUtils')
104
+ const NextServer = require(${JSON.stringify(nextServerModuleRelativeLocation)}).default
101
105
 
102
106
  ${config.type === "experimental-scheduled" /* ApiRouteType.SCHEDULED */ ? `const { schedule } = require("@netlify/functions")` : ''}
103
107
 
@@ -106,7 +110,7 @@ const getApiHandler = ({ page, config, publishDir = '../../../.next', appDir = '
106
110
  let staticManifest
107
111
  const path = require("path");
108
112
  const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server"));
109
- const handler = (${makeHandler.toString()})(config, "${appDir}", pageRoot, ${JSON.stringify(page)})
113
+ const handler = (${makeApiHandler.toString()})({ conf: config, app: "${appDir}", pageRoot, page:${JSON.stringify(page)}, NextServer})
110
114
  exports.handler = ${config.type === "experimental-scheduled" /* ApiRouteType.SCHEDULED */ ? `schedule(${JSON.stringify(config.schedule)}, handler);` : 'handler'}
111
115
  `;
112
116
  exports.getApiHandler = getApiHandler;
@@ -11,10 +11,10 @@ const path = require('path');
11
11
  const { URLSearchParams, URL } = require('url');
12
12
  const { Bridge } = require('@vercel/node-bridge/bridge');
13
13
  const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath, } = require('./handlerUtils');
14
- const { NetlifyNextServer } = require('./server');
14
+ const { getNetlifyNextServer } = require('./server');
15
15
  // We return a function and then call `toString()` on it to serialise it as the launcher function
16
- // eslint-disable-next-line max-params, max-lines-per-function
17
- const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') => {
16
+ // eslint-disable-next-line max-lines-per-function
17
+ const makeHandler = ({ conf, app, pageRoot, NextServer, staticManifest = [], mode = 'ssr' }) => {
18
18
  var _a;
19
19
  // Change working directory into the site root, unless using Nx, which moves the
20
20
  // dist directory and handles this itself
@@ -28,6 +28,7 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
28
28
  require.resolve('./pages.js');
29
29
  }
30
30
  catch { }
31
+ const NetlifyNextServer = getNetlifyNextServer(NextServer);
31
32
  const ONE_YEAR_IN_SECONDS = 31536000;
32
33
  (_a = process.env).NODE_ENV || (_a.NODE_ENV = 'production');
33
34
  // We don't want to write ISR files to disk in the lambda environment
@@ -59,7 +60,7 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
59
60
  hostname: url.hostname,
60
61
  port,
61
62
  }, {
62
- revalidateToken: customContext.odb_refresh_hooks,
63
+ revalidateToken: customContext === null || customContext === void 0 ? void 0 : customContext.odb_refresh_hooks,
63
64
  });
64
65
  const requestHandler = nextServer.getRequestHandler();
65
66
  const server = new Server(async (req, res) => {
@@ -86,13 +87,6 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
86
87
  // Next expects to be able to parse the query from the URL
87
88
  const query = new URLSearchParams(event.queryStringParameters).toString();
88
89
  event.path = query ? `${event.path}?${query}` : event.path;
89
- const graphToken = event.netlifyGraphToken;
90
- if (graphToken && requestMode !== 'ssr') {
91
- // Prefix with underscore to help us determine the origin of the token
92
- // allows us to write better error messages
93
- // eslint-disable-next-line no-underscore-dangle
94
- process.env._NETLIFY_GRAPH_TOKEN = graphToken;
95
- }
96
90
  const { headers, ...result } = await getBridge(event, context).launcher(event, context);
97
91
  // Convert all headers to multiValueHeaders
98
92
  const multiValueHeaders = getMultiValueHeaders(headers);
@@ -137,15 +131,20 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
137
131
  };
138
132
  };
139
133
  };
140
- const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '../../..' }) =>
134
+ const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '../../..', nextServerModuleRelativeLocation, }) =>
141
135
  // This is a string, but if you have the right editor plugin it should format as js
142
136
  (0, outdent_1.outdent /* javascript */) `
137
+ if (!${JSON.stringify(nextServerModuleRelativeLocation)}) {
138
+ throw new Error('Could not find Next.js server')
139
+ }
140
+
143
141
  const { Server } = require("http");
144
142
  const { promises } = require("fs");
145
143
  // We copy the file here rather than requiring from the node module
146
144
  const { Bridge } = require("./bridge");
147
- const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, getNextServer, normalizePath } = require('./handlerUtils')
148
- const { NetlifyNextServer } = require('./server')
145
+ const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, normalizePath } = require('./handlerUtils')
146
+ const { getNetlifyNextServer } = require('./server')
147
+ const NextServer = require(${JSON.stringify(nextServerModuleRelativeLocation)}).default
149
148
 
150
149
  ${isODB ? `const { builder } = require("@netlify/functions")` : ''}
151
150
  const { config } = require("${publishDir}/required-server-files.json")
@@ -156,7 +155,7 @@ const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '..
156
155
  const path = require("path");
157
156
  const pageRoot = path.resolve(path.join(__dirname, "${publishDir}", "server"));
158
157
  exports.handler = ${isODB
159
- ? `builder((${makeHandler.toString()})(config, "${appDir}", pageRoot, staticManifest, 'odb'));`
160
- : `(${makeHandler.toString()})(config, "${appDir}", pageRoot, 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' });`}
161
160
  `;
162
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
  `;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.localizeDataRoute = exports.localizeRoute = exports.unlocalizeRoute = exports.normalizeRoute = exports.netlifyApiFetch = exports.normalizePath = exports.getPrefetchResponse = exports.getNextServer = exports.augmentFsModule = exports.getMultiValueHeaders = exports.getMaxAge = exports.downloadFile = void 0;
6
+ exports.localizeDataRoute = exports.localizeRoute = exports.unlocalizeRoute = exports.normalizeRoute = exports.netlifyApiFetch = exports.normalizePath = exports.getPrefetchResponse = exports.augmentFsModule = exports.getMultiValueHeaders = exports.getMaxAge = exports.downloadFile = void 0;
7
7
  const fs_1 = require("fs");
8
8
  const os_1 = require("os");
9
9
  const path_1 = __importDefault(require("path"));
@@ -139,39 +139,6 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
139
139
  });
140
140
  };
141
141
  exports.augmentFsModule = augmentFsModule;
142
- /**
143
- * Next.js has an annoying habit of needing deep imports, but then moving those in patch releases. This is our abstraction.
144
- */
145
- const getNextServer = () => {
146
- let NextServer;
147
- try {
148
- // next >= 11.0.1. Yay breaking changes in patch releases!
149
- // eslint-disable-next-line @typescript-eslint/no-var-requires
150
- NextServer = require('next/dist/server/next-server').default;
151
- }
152
- catch (error) {
153
- if (!error.message.includes("Cannot find module 'next/dist/server/next-server'")) {
154
- // A different error, so rethrow it
155
- throw error;
156
- }
157
- // Probably an old version of next, so fall through and find it elsewhere.
158
- }
159
- if (!NextServer) {
160
- try {
161
- // next < 11.0.1
162
- // eslint-disable-next-line n/no-missing-require, import/no-unresolved, @typescript-eslint/no-var-requires
163
- NextServer = require('next/dist/next-server/server/next-server').default;
164
- }
165
- catch (error) {
166
- if (!error.message.includes("Cannot find module 'next/dist/next-server/server/next-server'")) {
167
- throw error;
168
- }
169
- throw new Error('Could not find Next.js server');
170
- }
171
- }
172
- return NextServer;
173
- };
174
- exports.getNextServer = getNextServer;
175
142
  /**
176
143
  * Prefetch requests are used to check for middleware redirects, and shouldn't trigger SSR.
177
144
  */
@@ -1,81 +1,83 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NetlifyNextServer = void 0;
3
+ exports.getNetlifyNextServer = void 0;
4
4
  const handlerUtils_1 = require("./handlerUtils");
5
- const NextServer = (0, handlerUtils_1.getNextServer)();
6
- class NetlifyNextServer extends NextServer {
7
- constructor(options, netlifyConfig) {
8
- super(options);
9
- this.netlifyConfig = netlifyConfig;
10
- // copy the prerender manifest so it doesn't get mutated by Next.js
11
- const manifest = this.getPrerenderManifest();
12
- this.netlifyPrerenderManifest = {
13
- ...manifest,
14
- routes: { ...manifest.routes },
15
- dynamicRoutes: { ...manifest.dynamicRoutes },
16
- };
17
- }
18
- getRequestHandler() {
19
- const handler = super.getRequestHandler();
20
- return async (req, res, parsedUrl) => {
21
- // preserve the URL before Next.js mutates it for i18n
22
- const { url, headers } = req;
23
- // handle the original res.revalidate() request
24
- await handler(req, res, parsedUrl);
25
- // handle on-demand revalidation by purging the ODB cache
26
- if (res.statusCode === 200 && headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) {
27
- await this.netlifyRevalidate(url);
28
- }
29
- };
30
- }
31
- async netlifyRevalidate(route) {
32
- try {
33
- // call netlify API to revalidate the path
34
- const result = await (0, handlerUtils_1.netlifyApiFetch)({
35
- endpoint: `sites/${process.env.SITE_ID}/refresh_on_demand_builders`,
36
- payload: {
37
- paths: this.getNetlifyPathsForRoute(route),
38
- domain: this.hostname,
39
- },
40
- token: this.netlifyConfig.revalidateToken,
41
- method: 'POST',
42
- });
43
- if (!result.ok) {
44
- throw new Error(result.message);
45
- }
5
+ const getNetlifyNextServer = (NextServer) => {
6
+ class NetlifyNextServer extends NextServer {
7
+ constructor(options, netlifyConfig) {
8
+ super(options);
9
+ this.netlifyConfig = netlifyConfig;
10
+ // copy the prerender manifest so it doesn't get mutated by Next.js
11
+ const manifest = this.getPrerenderManifest();
12
+ this.netlifyPrerenderManifest = {
13
+ ...manifest,
14
+ routes: { ...manifest.routes },
15
+ dynamicRoutes: { ...manifest.dynamicRoutes },
16
+ };
46
17
  }
47
- catch (error) {
48
- console.log(`Error revalidating ${route}:`, error.message);
49
- throw error;
18
+ getRequestHandler() {
19
+ const handler = super.getRequestHandler();
20
+ return async (req, res, parsedUrl) => {
21
+ // preserve the URL before Next.js mutates it for i18n
22
+ const { url, headers } = req;
23
+ // handle the original res.revalidate() request
24
+ await handler(req, res, parsedUrl);
25
+ // handle on-demand revalidation by purging the ODB cache
26
+ if (res.statusCode === 200 && headers['x-prerender-revalidate'] && this.netlifyConfig.revalidateToken) {
27
+ await this.netlifyRevalidate(url);
28
+ }
29
+ };
50
30
  }
51
- }
52
- getNetlifyPathsForRoute(route) {
53
- const { i18n } = this.nextConfig;
54
- const { routes, dynamicRoutes } = this.netlifyPrerenderManifest;
55
- // matches static routes
56
- const normalizedRoute = (0, handlerUtils_1.normalizeRoute)(i18n ? (0, handlerUtils_1.localizeRoute)(route, i18n) : route);
57
- if (normalizedRoute in routes) {
58
- const { dataRoute } = routes[normalizedRoute];
59
- const normalizedDataRoute = i18n ? (0, handlerUtils_1.localizeDataRoute)(dataRoute, normalizedRoute) : dataRoute;
60
- return [route, normalizedDataRoute];
31
+ async netlifyRevalidate(route) {
32
+ try {
33
+ // call netlify API to revalidate the path
34
+ const result = await (0, handlerUtils_1.netlifyApiFetch)({
35
+ endpoint: `sites/${process.env.SITE_ID}/refresh_on_demand_builders`,
36
+ payload: {
37
+ paths: this.getNetlifyPathsForRoute(route),
38
+ domain: this.hostname,
39
+ },
40
+ token: this.netlifyConfig.revalidateToken,
41
+ method: 'POST',
42
+ });
43
+ if (!result.ok) {
44
+ throw new Error(result.message);
45
+ }
46
+ }
47
+ catch (error) {
48
+ console.log(`Error revalidating ${route}:`, error.message);
49
+ throw error;
50
+ }
61
51
  }
62
- // matches dynamic routes
63
- const unlocalizedRoute = i18n ? (0, handlerUtils_1.unlocalizeRoute)(normalizedRoute, i18n) : normalizedRoute;
64
- for (const dynamicRoute in dynamicRoutes) {
65
- const { dataRoute, routeRegex } = dynamicRoutes[dynamicRoute];
66
- const matches = unlocalizedRoute.match(routeRegex);
67
- if (matches && matches.length !== 0) {
68
- // remove the first match, which is the full route
69
- matches.shift();
70
- // replace the dynamic segments with the actual values
71
- const interpolatedDataRoute = dataRoute.replace(/\[(.*?)]/g, () => matches.shift());
72
- const normalizedDataRoute = i18n
73
- ? (0, handlerUtils_1.localizeDataRoute)(interpolatedDataRoute, normalizedRoute)
74
- : interpolatedDataRoute;
52
+ getNetlifyPathsForRoute(route) {
53
+ const { i18n } = this.nextConfig;
54
+ const { routes, dynamicRoutes } = this.netlifyPrerenderManifest;
55
+ // matches static routes
56
+ const normalizedRoute = (0, handlerUtils_1.normalizeRoute)(i18n ? (0, handlerUtils_1.localizeRoute)(route, i18n) : route);
57
+ if (normalizedRoute in routes) {
58
+ const { dataRoute } = routes[normalizedRoute];
59
+ const normalizedDataRoute = i18n ? (0, handlerUtils_1.localizeDataRoute)(dataRoute, normalizedRoute) : dataRoute;
75
60
  return [route, normalizedDataRoute];
76
61
  }
62
+ // matches dynamic routes
63
+ const unlocalizedRoute = i18n ? (0, handlerUtils_1.unlocalizeRoute)(normalizedRoute, i18n) : normalizedRoute;
64
+ for (const dynamicRoute in dynamicRoutes) {
65
+ const { dataRoute, routeRegex } = dynamicRoutes[dynamicRoute];
66
+ const matches = unlocalizedRoute.match(routeRegex);
67
+ if ((matches === null || matches === void 0 ? void 0 : matches.length) > 0) {
68
+ // remove the first match, which is the full route
69
+ matches.shift();
70
+ // replace the dynamic segments with the actual values
71
+ const interpolatedDataRoute = dataRoute.replace(/\[(.*?)]/g, () => matches.shift());
72
+ const normalizedDataRoute = i18n
73
+ ? (0, handlerUtils_1.localizeDataRoute)(interpolatedDataRoute, normalizedRoute)
74
+ : interpolatedDataRoute;
75
+ return [route, normalizedDataRoute];
76
+ }
77
+ }
78
+ throw new Error(`not an ISR route`);
77
79
  }
78
- throw new Error(`not an ISR route`);
79
80
  }
80
- }
81
- exports.NetlifyNextServer = NetlifyNextServer;
81
+ return NetlifyNextServer;
82
+ };
83
+ exports.getNetlifyNextServer = getNetlifyNextServer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "4.36.0",
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.9.2",
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.1.6",
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 no-var prefer-const no-unused-vars no-explicit-any
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 { AsyncLocalStorage as ALSCompat } from 'https://deno.land/std@0.175.0/node/async_hooks.ts'
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 = ALSCompat
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 }