@netlify/plugin-nextjs 4.29.3-appdir.0 → 4.29.4

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.
@@ -154,4 +154,3 @@ const generateCustomHeaders = (nextConfig, netlifyHeaders = []) => {
154
154
  }
155
155
  };
156
156
  exports.generateCustomHeaders = generateCustomHeaders;
157
- /* eslint-enable max-lines */
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.writeEdgeFunctions = exports.writeDevEdgeFunction = exports.cleanupEdgeFunctions = exports.loadAppPathRoutesManifest = exports.loadMiddlewareManifest = void 0;
7
- /* eslint-disable max-lines */
8
7
  const fs_1 = require("fs");
9
8
  const path_1 = require("path");
10
9
  const chalk_1 = require("chalk");
@@ -37,8 +36,6 @@ import {
37
36
  delete globalThis.window
38
37
  globalThis.process = { env: {...Deno.env.toObject(), NEXT_RUNTIME: 'edge', 'NEXT_PRIVATE_MINIMAL_MODE': '1' } }
39
38
  globalThis.EdgeRuntime = "netlify-edge"
40
- // Next uses "self" as a function-scoped global-like object
41
- const self = {}
42
39
  let _ENTRIES = {}
43
40
 
44
41
  // Next.js uses this extension to the Headers API implemented by Cloudflare workerd
@@ -70,6 +67,10 @@ const fetch = async (url, init) => {
70
67
  }
71
68
  }
72
69
 
70
+ // Next edge runtime uses "self" as a function-scoped global-like object, but some of the older polyfills expect it to equal globalThis
71
+ // See https://nextjs.org/docs/basic-features/supported-browsers-features#polyfills
72
+ const self = { ...globalThis, fetch }
73
+
73
74
  `;
74
75
  // Slightly different spacing in different versions!
75
76
  const IMPORT_UNSUPPORTED = [
@@ -178,6 +179,7 @@ exports.writeDevEdgeFunction = writeDevEdgeFunction;
178
179
  * Writes Edge Functions for the Next middleware
179
180
  */
180
181
  const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
182
+ var _a;
181
183
  const manifest = {
182
184
  functions: [],
183
185
  version: 1,
@@ -187,6 +189,7 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
187
189
  const { publish } = netlifyConfig.build;
188
190
  const nextConfigFile = await (0, config_1.getRequiredServerFiles)(publish);
189
191
  const nextConfig = nextConfigFile.config;
192
+ const usesAppDir = (_a = nextConfig.experimental) === null || _a === void 0 ? void 0 : _a.appDir;
190
193
  await (0, fs_extra_1.copy)(getEdgeTemplatePath('../edge-shared'), (0, path_1.join)(edgeFunctionRoot, 'edge-shared'));
191
194
  await (0, fs_extra_1.writeJSON)((0, path_1.join)(edgeFunctionRoot, 'edge-shared', 'nextConfig.json'), nextConfig);
192
195
  if (!(0, destr_1.default)(process.env.NEXT_DISABLE_EDGE_IMAGES) &&
@@ -239,7 +242,8 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
239
242
  pageRegexMap,
240
243
  appPathRoutesManifest,
241
244
  nextConfig,
242
- cache: 'manual',
245
+ // cache: "manual" is currently experimental, so we restrict it to sites that use experimental appDir
246
+ cache: usesAppDir ? 'manual' : undefined,
243
247
  });
244
248
  manifest.functions.push(...functionDefinitions);
245
249
  }
@@ -254,4 +258,3 @@ const writeEdgeFunctions = async ({ netlifyConfig, routesManifest, }) => {
254
258
  await (0, fs_extra_1.writeJson)((0, path_1.join)(edgeFunctionRoot, 'manifest.json'), manifest);
255
259
  };
256
260
  exports.writeEdgeFunctions = writeEdgeFunctions;
257
- /* eslint-enable max-lines */
@@ -4,7 +4,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
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;
7
- /* eslint-disable max-lines */
8
7
  const os_1 = require("os");
9
8
  const chalk_1 = require("chalk");
10
9
  const fs_extra_1 = require("fs-extra");
@@ -123,29 +122,32 @@ const moveStaticPages = async ({ netlifyConfig, target, i18n, basePath, }) => {
123
122
  // Limit concurrent file moves to number of cpus or 2 if there is only 1
124
123
  const limit = (0, p_limit_1.default)(Math.max(2, (0, os_1.cpus)().length));
125
124
  const promises = pages.map((rawPath) => {
125
+ // Convert to POSIX path
126
126
  const filePath = (0, slash_1.default)(rawPath);
127
+ // Remove the initial 'app' or 'pages' directory from the output path
128
+ const pagePath = filePath.split('/').slice(1).join('/');
127
129
  // Don't move ISR files, as they're used for the first request
128
- if (isrFiles.has(filePath)) {
130
+ if (isrFiles.has(pagePath)) {
129
131
  return;
130
132
  }
131
- if ((0, exports.isDynamicRoute)(filePath)) {
133
+ if ((0, exports.isDynamicRoute)(pagePath)) {
132
134
  return;
133
135
  }
134
- if ((0, exports.matchesRedirect)(filePath, redirects)) {
135
- matchedRedirects.add(filePath);
136
+ if ((0, exports.matchesRedirect)(pagePath, redirects)) {
137
+ matchedRedirects.add(pagePath);
136
138
  return;
137
139
  }
138
- if ((0, exports.matchesRewrite)(filePath, rewrites)) {
139
- matchedRewrites.add(filePath);
140
+ if ((0, exports.matchesRewrite)(pagePath, rewrites)) {
141
+ matchedRewrites.add(pagePath);
140
142
  return;
141
143
  }
142
144
  // Middleware matches against the unlocalised path
143
- const unlocalizedPath = (0, exports.stripLocale)(rawPath, i18n === null || i18n === void 0 ? void 0 : i18n.locales);
145
+ const unlocalizedPath = (0, exports.stripLocale)(pagePath, i18n === null || i18n === void 0 ? void 0 : i18n.locales);
144
146
  const middlewarePath = (0, exports.matchMiddleware)(middleware, unlocalizedPath);
145
147
  // If a file matches middleware it can't be offloaded to the CDN, and needs to stay at the origin to be served by next/server
146
148
  if (middlewarePath) {
147
149
  matchingMiddleware.add(middlewarePath);
148
- matchedPages.add(rawPath);
150
+ matchedPages.add(filePath);
149
151
  return;
150
152
  }
151
153
  return limit(moveFile, filePath);
@@ -276,11 +278,13 @@ const getServerFile = (root, includeBase = true) => {
276
278
  /**
277
279
  * Find the source file for a given page route
278
280
  */
279
- const getSourceFileForPage = (page, root) => {
280
- for (const extension of SOURCE_FILE_EXTENSIONS) {
281
- const file = (0, pathe_1.join)(root, `${page}.${extension}`);
282
- if ((0, fs_extra_1.existsSync)(file)) {
283
- return file;
281
+ const getSourceFileForPage = (page, roots) => {
282
+ for (const root of roots) {
283
+ for (const extension of SOURCE_FILE_EXTENSIONS) {
284
+ const file = (0, pathe_1.join)(root, `${page}.${extension}`);
285
+ if ((0, fs_extra_1.existsSync)(file)) {
286
+ return file;
287
+ }
284
288
  }
285
289
  }
286
290
  console.log('Could not find source file for page', page);
@@ -373,4 +377,3 @@ const movePublicFiles = async ({ appDir, outdir, publish, }) => {
373
377
  }
374
378
  };
375
379
  exports.movePublicFiles = movePublicFiles;
376
- /* eslint-enable max-lines */
@@ -126,9 +126,12 @@ exports.setupImageFunction = setupImageFunction;
126
126
  const getApiRouteConfigs = async (publish, baseDir) => {
127
127
  const pages = await (0, fs_extra_1.readJSON)((0, pathe_1.join)(publish, 'server', 'pages-manifest.json'));
128
128
  const apiRoutes = Object.keys(pages).filter((page) => page.startsWith('/api/'));
129
+ // two possible places
130
+ // Ref: https://nextjs.org/docs/advanced-features/src-directory
129
131
  const pagesDir = (0, pathe_1.join)(baseDir, 'pages');
132
+ const srcPagesDir = (0, pathe_1.join)(baseDir, 'src', 'pages');
130
133
  return await Promise.all(apiRoutes.map(async (apiRoute) => {
131
- const filePath = (0, files_1.getSourceFileForPage)(apiRoute, pagesDir);
134
+ const filePath = (0, files_1.getSourceFileForPage)(apiRoute, [pagesDir, srcPagesDir]);
132
135
  return { route: apiRoute, config: await (0, analysis_1.extractConfigFromFile)(filePath), compiled: pages[apiRoute] };
133
136
  }));
134
137
  };
@@ -167,4 +170,3 @@ const warnOnApiRoutes = async ({ FUNCTIONS_DIST, }) => {
167
170
  }
168
171
  };
169
172
  exports.warnOnApiRoutes = warnOnApiRoutes;
170
- /* eslint-enable max-lines */
@@ -127,7 +127,7 @@ const generateStaticIsrRewrites = ({ staticRouteEntries, basePath, i18n, buildId
127
127
  /**
128
128
  * Generate rewrites for all dynamic routes
129
129
  */
130
- const generateDynamicRewrites = ({ dynamicRoutes, prerenderedDynamicRoutes, middleware, basePath, buildId, i18n, }) => {
130
+ const generateDynamicRewrites = ({ dynamicRoutes, prerenderedDynamicRoutes, middleware, basePath, buildId, i18n, is404Isr, }) => {
131
131
  const dynamicRewrites = [];
132
132
  const dynamicRoutesThatMatchMiddleware = [];
133
133
  dynamicRoutes.forEach((route) => {
@@ -138,8 +138,11 @@ const generateDynamicRewrites = ({ dynamicRoutes, prerenderedDynamicRoutes, midd
138
138
  if (matchesMiddleware(middleware, route.page)) {
139
139
  dynamicRoutesThatMatchMiddleware.push(route.page);
140
140
  }
141
+ else if (prerenderedDynamicRoutes[route.page].fallback === false && !is404Isr) {
142
+ dynamicRewrites.push(...(0, utils_1.redirectsForNext404Route)({ route: route.page, buildId, basePath, i18n }));
143
+ }
141
144
  else {
142
- dynamicRewrites.push(...(0, utils_1.redirectsForNextRoute)({ buildId, route: route.page, basePath, to: constants_1.ODB_FUNCTION_PATH, status: 200, i18n }));
145
+ dynamicRewrites.push(...(0, utils_1.redirectsForNextRoute)({ route: route.page, buildId, basePath, to: constants_1.ODB_FUNCTION_PATH, i18n }));
143
146
  }
144
147
  }
145
148
  else {
@@ -168,6 +171,7 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
168
171
  const middleware = await (0, files_1.getMiddleware)(netlifyConfig.build.publish);
169
172
  netlifyConfig.redirects.push(...generateMiddlewareRewrites({ basePath, i18n, middleware, buildId }));
170
173
  const staticRouteEntries = Object.entries(prerenderedStaticRoutes);
174
+ const is404Isr = staticRouteEntries.some(([route, { initialRevalidateSeconds }]) => (0, utils_1.is404Route)(route, i18n) && initialRevalidateSeconds !== false);
171
175
  const routesThatMatchMiddleware = [];
172
176
  const { staticRoutePaths, staticIsrRewrites, staticIsrRoutesThatMatchMiddleware } = generateStaticIsrRewrites({
173
177
  staticRouteEntries,
@@ -194,6 +198,7 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
194
198
  basePath,
195
199
  buildId,
196
200
  i18n,
201
+ is404Isr,
197
202
  });
198
203
  netlifyConfig.redirects.push(...dynamicRewrites);
199
204
  routesThatMatchMiddleware.push(...dynamicRoutesThatMatchMiddleware);
@@ -214,4 +219,3 @@ const generateRedirects = async ({ netlifyConfig, nextConfig: { i18n, basePath,
214
219
  }
215
220
  };
216
221
  exports.generateRedirects = generateRedirects;
217
- /* eslint-enable max-lines */
@@ -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.getRemotePatterns = exports.isBundleSizeCheckDisabled = exports.getCustomImageResponseHeaders = exports.isNextAuthInstalled = exports.findModuleFromBase = exports.shouldSkip = exports.getPreviewRewrites = exports.getApiRewrites = exports.redirectsForNextRouteWithData = exports.redirectsForNextRoute = exports.is404Route = exports.isApiRoute = exports.routeToDataRoute = exports.netlifyRoutesForNextRouteWithData = exports.toNetlifyRoute = exports.getFunctionNameForPage = void 0;
6
+ exports.getRemotePatterns = exports.isBundleSizeCheckDisabled = exports.getCustomImageResponseHeaders = exports.isNextAuthInstalled = exports.findModuleFromBase = exports.shouldSkip = exports.getPreviewRewrites = exports.getApiRewrites = exports.redirectsForNextRouteWithData = exports.redirectsForNext404Route = exports.redirectsForNextRoute = exports.is404Route = exports.isApiRoute = exports.routeToDataRoute = exports.netlifyRoutesForNextRouteWithData = exports.toNetlifyRoute = exports.getFunctionNameForPage = void 0;
7
7
  const globby_1 = __importDefault(require("globby"));
8
8
  const pathe_1 = require("pathe");
9
9
  const constants_1 = require("../constants");
@@ -57,7 +57,10 @@ exports.routeToDataRoute = routeToDataRoute;
57
57
  const netlifyRoutesForNextRoute = (route, buildId, i18n) => {
58
58
  var _a;
59
59
  if (!((_a = i18n === null || i18n === void 0 ? void 0 : i18n.locales) === null || _a === void 0 ? void 0 : _a.length)) {
60
- return (0, exports.netlifyRoutesForNextRouteWithData)({ route, dataRoute: (0, exports.routeToDataRoute)(route, buildId) });
60
+ return (0, exports.netlifyRoutesForNextRouteWithData)({ route, dataRoute: (0, exports.routeToDataRoute)(route, buildId) }).map((redirect) => ({
61
+ redirect,
62
+ locale: false,
63
+ }));
61
64
  }
62
65
  const { locales, defaultLocale } = i18n;
63
66
  const routes = [];
@@ -69,7 +72,10 @@ const netlifyRoutesForNextRoute = (route, buildId, i18n) => {
69
72
  ...(0, exports.netlifyRoutesForNextRouteWithData)({
70
73
  route: locale === defaultLocale ? route : `/${locale}${route}`,
71
74
  dataRoute,
72
- }));
75
+ }).map((redirect) => ({
76
+ redirect,
77
+ locale,
78
+ })));
73
79
  });
74
80
  return routes;
75
81
  };
@@ -77,13 +83,20 @@ const isApiRoute = (route) => route.startsWith('/api/') || route === '/api';
77
83
  exports.isApiRoute = isApiRoute;
78
84
  const is404Route = (route, i18n) => i18n ? i18n.locales.some((locale) => route === `/${locale}/404`) : route === '/404';
79
85
  exports.is404Route = is404Route;
80
- const redirectsForNextRoute = ({ route, buildId, basePath, to, i18n, status = 200, force = false, }) => netlifyRoutesForNextRoute(route, buildId, i18n).map((redirect) => ({
86
+ const redirectsForNextRoute = ({ route, buildId, basePath, to, i18n, status = 200, force = false, }) => netlifyRoutesForNextRoute(route, buildId, i18n).map(({ redirect }) => ({
81
87
  from: `${basePath}${redirect}`,
82
88
  to,
83
89
  status,
84
90
  force,
85
91
  }));
86
92
  exports.redirectsForNextRoute = redirectsForNextRoute;
93
+ const redirectsForNext404Route = ({ route, buildId, basePath, i18n, force = false, }) => netlifyRoutesForNextRoute(route, buildId, i18n).map(({ redirect, locale }) => ({
94
+ from: `${basePath}${redirect}`,
95
+ to: locale ? `${basePath}/server/pages/${locale}/404.html` : `${basePath}/server/pages/404.html`,
96
+ status: 404,
97
+ force,
98
+ }));
99
+ exports.redirectsForNext404Route = redirectsForNext404Route;
87
100
  const redirectsForNextRouteWithData = ({ route, dataRoute, basePath, to, status = 200, force = false, }) => (0, exports.netlifyRoutesForNextRouteWithData)({ route, dataRoute }).map((redirect) => ({
88
101
  from: `${basePath}${redirect}`,
89
102
  to,
@@ -193,4 +206,3 @@ const getRemotePatterns = (experimental, images) => {
193
206
  return [];
194
207
  };
195
208
  exports.getRemotePatterns = getRemotePatterns;
196
- /* eslint-enable max-lines */
@@ -27,7 +27,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.warnForRootRedirects = exports.warnForProblematicUserRewrites = exports.getProblematicUserRewrites = exports.checkZipSize = exports.checkForRootPublish = exports.checkNextSiteHasBuilt = exports.checkForOldFunctions = exports.verifyNetlifyBuildVersion = void 0;
30
- /* eslint-disable max-lines */
31
30
  const fs_1 = require("fs");
32
31
  const path_1 = __importStar(require("path"));
33
32
  const chalk_1 = require("chalk");
@@ -180,4 +179,3 @@ const warnForRootRedirects = ({ appDir }) => {
180
179
  }
181
180
  };
182
181
  exports.warnForRootRedirects = warnForRootRedirects;
183
- /* eslint-enable max-lines */
package/lib/index.js CHANGED
@@ -3,7 +3,6 @@ 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
- /* eslint-disable max-lines */
7
6
  const path_1 = require("path");
8
7
  const chalk_1 = require("chalk");
9
8
  const destr_1 = __importDefault(require("destr"));
@@ -164,4 +163,3 @@ const nextRuntime = (_inputs, meta = {}) => {
164
163
  };
165
164
  };
166
165
  module.exports = nextRuntime;
167
- /* eslint-enable max-lines */
@@ -10,7 +10,7 @@ const path = require('path');
10
10
  // eslint-disable-next-line n/prefer-global/url, n/prefer-global/url-search-params
11
11
  const { URLSearchParams, URL } = require('url');
12
12
  const { Bridge } = require('@vercel/node-bridge/bridge');
13
- const { augmentFsModule, getMaxAge, getMultiValueHeaders, getNextServer } = require('./handlerUtils');
13
+ const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, getNextServer, } = require('./handlerUtils');
14
14
  // We return a function and then call `toString()` on it to serialise it as the launcher function
15
15
  // eslint-disable-next-line max-params
16
16
  const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') => {
@@ -37,7 +37,9 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
37
37
  for (const [key, value] of Object.entries(conf.env)) {
38
38
  process.env[key] = String(value);
39
39
  }
40
- augmentFsModule({ promises, staticManifest, pageRoot });
40
+ // Set during the request as it needs to get it from the request URL. Defaults to the URL env var
41
+ let base = process.env.URL;
42
+ augmentFsModule({ promises, staticManifest, pageRoot, getBase: () => base });
41
43
  // We memoize this because it can be shared between requests, but don't instantiate it until
42
44
  // the first request because we need the host and port.
43
45
  let bridge;
@@ -47,6 +49,7 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
47
49
  }
48
50
  const url = new URL(event.rawUrl);
49
51
  const port = Number.parseInt(url.port) || 80;
52
+ base = url.origin;
50
53
  const NextServer = getNextServer();
51
54
  const nextServer = new NextServer({
52
55
  conf,
@@ -72,6 +75,10 @@ const makeHandler = (conf, app, pageRoot, staticManifest = [], mode = 'ssr') =>
72
75
  return async function handler(event, context) {
73
76
  var _a, _b, _c;
74
77
  let requestMode = mode;
78
+ const prefetchResponse = getPrefetchResponse(event, mode);
79
+ if (prefetchResponse) {
80
+ return prefetchResponse;
81
+ }
75
82
  // Ensure that paths are encoded - but don't double-encode them
76
83
  event.path = new URL(event.rawUrl).pathname;
77
84
  // Next expects to be able to parse the query from the URL
@@ -135,7 +142,7 @@ const getHandler = ({ isODB = false, publishDir = '../../../.next', appDir = '..
135
142
  const { promises } = require("fs");
136
143
  // We copy the file here rather than requiring from the node module
137
144
  const { Bridge } = require("./bridge");
138
- const { augmentFsModule, getMaxAge, getMultiValueHeaders, getNextServer } = require('./handlerUtils')
145
+ const { augmentFsModule, getMaxAge, getMultiValueHeaders, getPrefetchResponse, getNextServer } = require('./handlerUtils')
139
146
 
140
147
  ${isODB ? `const { builder } = require("@netlify/functions")` : ''}
141
148
  const { config } = require("${publishDir}/required-server-files.json")
@@ -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.getNextServer = exports.augmentFsModule = exports.getMultiValueHeaders = exports.getMaxAge = exports.downloadFile = void 0;
6
+ exports.getPrefetchResponse = exports.getNextServer = 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"));
@@ -75,7 +75,7 @@ exports.getMultiValueHeaders = getMultiValueHeaders;
75
75
  /**
76
76
  * Monkey-patch the fs module to download missing files from the CDN
77
77
  */
78
- const augmentFsModule = ({ promises, staticManifest, pageRoot, }) => {
78
+ const augmentFsModule = ({ promises, staticManifest, pageRoot, getBase, }) => {
79
79
  // Only do this if we have some static files moved to the CDN
80
80
  if (staticManifest.length === 0) {
81
81
  return;
@@ -93,8 +93,7 @@ const augmentFsModule = ({ promises, staticManifest, pageRoot, }) => {
93
93
  const statsOrig = promises.stat;
94
94
  // ...then money-patch it to see if it's requesting a CDN file
95
95
  promises.readFile = (async (file, options) => {
96
- // In production use the public URL (e.g. https://example.com). Otherwise use the deploy URL, e.g. https://deploy-preview-123--example.netlify.app
97
- const baseUrl = process.env.CONTEXT === 'production' ? process.env.URL : process.env.DEPLOY_PRIME_URL;
96
+ const baseUrl = getBase();
98
97
  // We only care about page files
99
98
  if (file.startsWith(pageRoot)) {
100
99
  // We only want the part after `.next/server/`
@@ -173,3 +172,22 @@ const getNextServer = () => {
173
172
  return NextServer;
174
173
  };
175
174
  exports.getNextServer = getNextServer;
175
+ /**
176
+ * Prefetch requests are used to check for middleware redirects, and shouldn't trigger SSR.
177
+ */
178
+ const getPrefetchResponse = (event, mode) => {
179
+ if (event.headers['x-middleware-prefetch'] && mode === 'ssr') {
180
+ return {
181
+ statusCode: 200,
182
+ body: '{}',
183
+ headers: {
184
+ 'Content-Type': 'application/json',
185
+ 'x-middleware-skip': '1',
186
+ // https://github.com/vercel/next.js/pull/42936/files#r1027563953
187
+ vary: 'x-middleware-prefetch',
188
+ },
189
+ };
190
+ }
191
+ return false;
192
+ };
193
+ exports.getPrefetchResponse = getPrefetchResponse;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "4.29.3-appdir.0",
3
+ "version": "4.29.4",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "index.js",
6
6
  "files": [
@@ -13,7 +13,7 @@
13
13
  "dependencies": {
14
14
  "@netlify/esbuild": "0.14.39",
15
15
  "@netlify/functions": "^1.3.0",
16
- "@netlify/ipx": "^1.3.1",
16
+ "@netlify/ipx": "^1.3.2",
17
17
  "@vercel/node-bridge": "^2.1.0",
18
18
  "chalk": "^4.1.2",
19
19
  "destr": "^1.1.1",
@@ -36,12 +36,12 @@
36
36
  },
37
37
  "devDependencies": {
38
38
  "@delucis/if-env": "^1.1.2",
39
- "@netlify/build": "^28.4.4",
39
+ "@netlify/build": "^29.1.3",
40
40
  "@types/fs-extra": "^9.0.13",
41
41
  "@types/jest": "^27.4.1",
42
42
  "@types/merge-stream": "^1.1.2",
43
43
  "@types/node": "^17.0.25",
44
- "next": "^13.0.3",
44
+ "next": "^13.0.6",
45
45
  "npm-run-all": "^4.1.5",
46
46
  "typescript": "^4.6.3"
47
47
  },
@@ -6,6 +6,17 @@ import { buildResponse } from '../edge-shared/utils.ts'
6
6
  globalThis.NFRequestContextMap ||= new Map()
7
7
  globalThis.__dirname = fromFileUrl(new URL('./', import.meta.url)).slice(0, -1)
8
8
 
9
+ // Next.js uses this extension to the Headers API implemented by Cloudflare workerd
10
+ if (!('getAll' in Headers.prototype)) {
11
+ Headers.prototype.getAll = function getAll(name) {
12
+ name = name.toLowerCase()
13
+ if (name !== 'set-cookie') {
14
+ throw new Error('Headers.getAll is only supported for Set-Cookie')
15
+ }
16
+ return [...this.entries()].filter(([key]) => key === name).map(([, value]) => value)
17
+ }
18
+ }
19
+
9
20
  // Check if a file exists, given a relative path
10
21
  const exists = async (relativePath) => {
11
22
  const path = fromFileUrl(new URL(relativePath, import.meta.url))