@netlify/plugin-nextjs 5.5.1 → 5.7.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -169,7 +169,7 @@ var copyNextDependencies = async (ctx) => {
169
169
  await tracer.withActiveSpan("copyNextDependencies", async () => {
170
170
  const entries = await readdir(ctx.standaloneDir);
171
171
  const promises = entries.map(async (entry) => {
172
- if (entry === "package.json" || entry === ctx.nextDistDir) {
172
+ if (entry === ctx.nextDistDir) {
173
173
  return;
174
174
  }
175
175
  const src = join(ctx.standaloneDir, entry);
@@ -431,10 +431,7 @@ var writeHandlerFile = async (ctx, { matchers, name }) => {
431
431
  const handlerDirectory = join(ctx.edgeFunctionsDir, handlerName);
432
432
  const handlerRuntimeDirectory = join(handlerDirectory, "edge-runtime");
433
433
  await copyRuntime(ctx, handlerDirectory);
434
- await writeFile(
435
- join(handlerRuntimeDirectory, "matchers.json"),
436
- JSON.stringify(augmentMatchers(matchers, ctx))
437
- );
434
+ await writeFile(join(handlerRuntimeDirectory, "matchers.json"), JSON.stringify(matchers));
438
435
  const minimalNextConfig = {
439
436
  basePath: nextConfig.basePath,
440
437
  i18n: nextConfig.i18n,
@@ -479,7 +476,7 @@ var copyHandlerDependencies = async (ctx, { name, files, wasm }) => {
479
476
  parts.push(`;// Concatenated file: ${file}
480
477
  `, entrypoint);
481
478
  }
482
- const exports = `export default _ENTRIES["middleware_${name}"].default;`;
479
+ const exports = `const middlewareEntryKey = Object.keys(_ENTRIES).find(entryKey => entryKey.startsWith("middleware_${name}")); export default _ENTRIES[middlewareEntryKey].default;`;
483
480
  await mkdir(dirname(join(destDir, `server/${name}.js`)), { recursive: true });
484
481
  await writeFile(join(destDir, `server/${name}.js`), [...parts, exports].join("\n"));
485
482
  };
@@ -61,10 +61,16 @@ var copyHandlerDependencies = async (ctx) => {
61
61
  )
62
62
  );
63
63
  }
64
+ promises.push(
65
+ writeFile(
66
+ join(ctx.serverHandlerRuntimeModulesDir, "package.json"),
67
+ JSON.stringify({ type: "module" })
68
+ )
69
+ );
64
70
  const fileList = await (0, import_fast_glob.glob)("dist/**/*", { cwd: ctx.pluginDir });
65
71
  for (const filePath of fileList) {
66
72
  promises.push(
67
- cp(join(ctx.pluginDir, filePath), join(ctx.serverHandlerDir, ".netlify", filePath), {
73
+ cp(join(ctx.pluginDir, filePath), join(ctx.serverHandlerRuntimeModulesDir, filePath), {
68
74
  recursive: true,
69
75
  force: true
70
76
  })
@@ -90,12 +96,6 @@ var writeHandlerManifest = async (ctx) => {
90
96
  "utf-8"
91
97
  );
92
98
  };
93
- var writePackageMetadata = async (ctx) => {
94
- await writeFile(
95
- join(ctx.serverHandlerRootDir, "package.json"),
96
- JSON.stringify({ type: "module" })
97
- );
98
- };
99
99
  var applyTemplateVariables = (template, variables) => {
100
100
  return Object.entries(variables).reduce((acc, [key, value]) => {
101
101
  return acc.replaceAll(key, value);
@@ -126,12 +126,11 @@ var clearStaleServerHandlers = async (ctx) => {
126
126
  };
127
127
  var createServerHandler = async (ctx) => {
128
128
  await tracer.withActiveSpan("createServerHandler", async () => {
129
- await mkdir(join(ctx.serverHandlerDir, ".netlify"), { recursive: true });
129
+ await mkdir(join(ctx.serverHandlerRuntimeModulesDir), { recursive: true });
130
130
  await copyNextServerCode(ctx);
131
131
  await copyNextDependencies(ctx);
132
132
  await copyHandlerDependencies(ctx);
133
133
  await writeHandlerManifest(ctx);
134
- await writePackageMetadata(ctx);
135
134
  await writeHandlerFile(ctx);
136
135
  await verifyHandlerDirStructure(ctx);
137
136
  });
@@ -133,6 +133,9 @@ var PluginContext = class {
133
133
  }
134
134
  return join(this.serverHandlerRootDir, this.distDirParent);
135
135
  }
136
+ get serverHandlerRuntimeModulesDir() {
137
+ return join(this.serverHandlerDir, ".netlify");
138
+ }
136
139
  get nextServerHandler() {
137
140
  if (this.relativeAppDir.length !== 0) {
138
141
  return join(this.lambdaWorkingDirectory, ".netlify/dist/run/handlers/server.js");
@@ -103,11 +103,10 @@ async function verifyNetlifyFormsWorkaround(ctx) {
103
103
  }
104
104
  }
105
105
  function verifyNetlifyForms(ctx, html) {
106
- if (!verifications.has("netlifyForms") && !verifications.has("netlifyFormsWorkaround") && formDetectionRegex.test(html)) {
107
- console.warn(
106
+ if (process.env.NETLIFY_NEXT_VERIFY_FORMS !== "0" && process.env.NETLIFY_NEXT_VERIFY_FORMS?.toUpperCase() !== "FALSE" && !verifications.has("netlifyFormsWorkaround") && formDetectionRegex.test(html)) {
107
+ ctx.failBuild(
108
108
  "@netlify/plugin-nextjs@5 requires migration steps to support Netlify Forms. Refer to https://ntl.fyi/next-runtime-forms-migration for migration example."
109
109
  );
110
- verifications.add("netlifyForms");
111
110
  }
112
111
  }
113
112
  export {
@@ -8,7 +8,7 @@ import "./chunk-OEQOKJGE.js";
8
8
 
9
9
  // package.json
10
10
  var name = "@netlify/plugin-nextjs";
11
- var version = "5.5.1";
11
+ var version = "5.7.0-canary.0";
12
12
  var description = "Run Next.js seamlessly on Netlify";
13
13
  var main = "./dist/index.js";
14
14
  var type = "module";
@@ -60,10 +60,10 @@ var devDependencies = {
60
60
  "@netlify/blobs": "^7.3.0",
61
61
  "@netlify/build": "^29.50.2",
62
62
  "@netlify/edge-bundler": "^12.1.1",
63
- "@netlify/edge-functions": "^2.8.1",
63
+ "@netlify/edge-functions": "^2.10.0",
64
64
  "@netlify/eslint-config-node": "^7.0.1",
65
65
  "@netlify/functions": "^2.8.1",
66
- "@netlify/serverless-functions-api": "^1.19.1",
66
+ "@netlify/serverless-functions-api": "^1.22.0",
67
67
  "@netlify/zip-it-and-ship-it": "^9.37.3",
68
68
  "@opentelemetry/api": "^1.8.0",
69
69
  "@opentelemetry/exporter-trace-otlp-http": "^0.51.0",
package/dist/index.js CHANGED
@@ -62,7 +62,7 @@ var onBuild = async (options) => {
62
62
  await saveBuildCache(ctx);
63
63
  }
64
64
  if (ctx.buildConfig.output === "export") {
65
- return copyStaticExport(ctx);
65
+ return Promise.all([copyStaticExport(ctx), setImageConfig(ctx)]);
66
66
  }
67
67
  await verifyAdvancedAPIRoutes(ctx);
68
68
  await verifyNetlifyFormsWorkaround(ctx);
@@ -3161,8 +3161,7 @@ var server_default = async (request, context) => {
3161
3161
  span.setAttribute("responseCacheKey", requestContext.responseCacheKey);
3162
3162
  }
3163
3163
  await adjustDateHeader({ headers: response.headers, request, span, tracer, requestContext });
3164
- const useDurableCache = context.flags.get("serverless_functions_nextjs_durable_cache_disable") !== true;
3165
- setCacheControlHeaders(response.headers, request, requestContext, useDurableCache);
3164
+ setCacheControlHeaders(response.headers, request, requestContext);
3166
3165
  setCacheTagsHeaders(response.headers, requestContext);
3167
3166
  setVaryHeaders(response.headers, request, nextConfig);
3168
3167
  setCacheStatusHeader(response.headers);
@@ -67385,7 +67385,7 @@ var import_semantic_conventions = __toESM(require_src(), 1);
67385
67385
  import { getLogger } from "./request-context.cjs";
67386
67386
  var {
67387
67387
  default: { version, name }
67388
- } = await import("../../esm-chunks/package-SHOGGUO3.js");
67388
+ } = await import("../../esm-chunks/package-SRGJOMMG.js");
67389
67389
  var sdk = new import_sdk_node.NodeSDK({
67390
67390
  resource: new import_resources.Resource({
67391
67391
  [import_semantic_conventions.SEMRESATTRS_SERVICE_NAME]: name,
@@ -142,12 +142,11 @@ var adjustDateHeader = async ({
142
142
  headers.set("x-nextjs-date", headers.get("date") ?? lastModifiedDate.toUTCString());
143
143
  headers.set("date", lastModifiedDate.toUTCString());
144
144
  };
145
- var setCacheControlHeaders = (headers, request, requestContext, useDurableCache) => {
146
- const durableCacheDirective = useDurableCache ? ", durable" : "";
145
+ var setCacheControlHeaders = (headers, request, requestContext) => {
147
146
  if (typeof requestContext.routeHandlerRevalidate !== "undefined" && ["GET", "HEAD"].includes(request.method) && !headers.has("cdn-cache-control") && !headers.has("netlify-cdn-cache-control")) {
148
147
  const cdnCacheControl = (
149
148
  // if we are serving already stale response, instruct edge to not attempt to cache that response
150
- headers.get("x-nextjs-cache") === "STALE" ? "public, max-age=0, must-revalidate" : `s-maxage=${requestContext.routeHandlerRevalidate === false ? 31536e3 : requestContext.routeHandlerRevalidate}, stale-while-revalidate=31536000${durableCacheDirective}`
149
+ headers.get("x-nextjs-cache") === "STALE" ? "public, max-age=0, must-revalidate" : `s-maxage=${requestContext.routeHandlerRevalidate === false ? 31536e3 : requestContext.routeHandlerRevalidate}, stale-while-revalidate=31536000, durable`
151
150
  );
152
151
  headers.set("netlify-cdn-cache-control", cdnCacheControl);
153
152
  return;
@@ -164,7 +163,7 @@ var setCacheControlHeaders = (headers, request, requestContext, useDurableCache)
164
163
  ...getHeaderValueArray(cacheControl).map(
165
164
  (value) => value === "stale-while-revalidate" ? "stale-while-revalidate=31536000" : value
166
165
  ),
167
- ...useDurableCache ? ["durable"] : []
166
+ "durable"
168
167
  ].join(", ")
169
168
  );
170
169
  headers.set("cache-control", browserCacheControl || "public, max-age=0, must-revalidate");
@@ -173,7 +172,7 @@ var setCacheControlHeaders = (headers, request, requestContext, useDurableCache)
173
172
  }
174
173
  if (cacheControl === null && !headers.has("cdn-cache-control") && !headers.has("netlify-cdn-cache-control") && requestContext.usedFsRead) {
175
174
  headers.set("cache-control", "public, max-age=0, must-revalidate");
176
- headers.set("netlify-cdn-cache-control", `max-age=31536000${durableCacheDirective}`);
175
+ headers.set("netlify-cdn-cache-control", `max-age=31536000, durable`);
177
176
  }
178
177
  };
179
178
  var setCacheTagsHeaders = (headers, requestContext) => {
@@ -2,6 +2,7 @@ import type { Context } from '@netlify/edge-functions'
2
2
 
3
3
  import {
4
4
  addBasePath,
5
+ addLocale,
5
6
  addTrailingSlash,
6
7
  normalizeDataUrl,
7
8
  normalizeLocalePath,
@@ -73,6 +74,33 @@ const normalizeRequestURL = (
73
74
  }
74
75
  }
75
76
 
77
+ export const localizeRequest = (
78
+ url: URL,
79
+ nextConfig?: {
80
+ basePath?: string
81
+ i18n?: I18NConfig | null
82
+ },
83
+ ): { localizedUrl: URL; locale?: string } => {
84
+ const localizedUrl = new URL(url)
85
+ localizedUrl.pathname = removeBasePath(localizedUrl.pathname, nextConfig?.basePath)
86
+
87
+ // Detect the locale from the URL
88
+ const { detectedLocale } = normalizeLocalePath(localizedUrl.pathname, nextConfig?.i18n?.locales)
89
+
90
+ // Add the locale to the URL if not already present
91
+ localizedUrl.pathname = addLocale(
92
+ localizedUrl.pathname,
93
+ detectedLocale ?? nextConfig?.i18n?.defaultLocale,
94
+ )
95
+
96
+ localizedUrl.pathname = addBasePath(localizedUrl.pathname, nextConfig?.basePath)
97
+
98
+ return {
99
+ localizedUrl,
100
+ locale: detectedLocale,
101
+ }
102
+ }
103
+
76
104
  export const buildNextRequest = (
77
105
  request: Request,
78
106
  context: Context,
@@ -116,9 +116,10 @@ export const buildResponse = async ({
116
116
  const res = new Response(result.response.body, result.response)
117
117
  request.headers.set('x-nf-next-middleware', 'skip')
118
118
 
119
- let rewrite = res.headers.get('x-middleware-rewrite')
120
119
  let redirect = res.headers.get('location')
121
120
  let nextRedirect = res.headers.get('x-nextjs-redirect')
121
+ // If we have both a redirect and a rewrite, the redirect must take precedence
122
+ let rewrite = !redirect && !nextRedirect ? res.headers.get('x-middleware-rewrite') : false
122
123
 
123
124
  // Data requests (i.e. requests for /_next/data ) need special handling
124
125
  const isDataReq = request.headers.has('x-nextjs-data')
@@ -29,6 +29,20 @@ export const addBasePath = (path: string, basePath?: string) => {
29
29
  return path
30
30
  }
31
31
 
32
+ // add locale prefix if not present, allowing for locale fallbacks
33
+ export const addLocale = (path: string, locale?: string) => {
34
+ if (
35
+ locale &&
36
+ path.toLowerCase() !== `/${locale.toLowerCase()}` &&
37
+ !path.toLowerCase().startsWith(`/${locale.toLowerCase()}/`) &&
38
+ !path.startsWith(`/api/`) &&
39
+ !path.startsWith(`/_next/`)
40
+ ) {
41
+ return `/${locale}${path}`
42
+ }
43
+ return path
44
+ }
45
+
32
46
  // https://github.com/vercel/next.js/blob/canary/packages/next/src/shared/lib/i18n/normalize-locale-path.ts
33
47
 
34
48
  export interface PathLocale {
@@ -5,7 +5,7 @@ import nextConfig from './next.config.json' with { type: 'json' }
5
5
 
6
6
  import { InternalHeaders } from './lib/headers.ts'
7
7
  import { logger, LogLevel } from './lib/logging.ts'
8
- import { buildNextRequest, RequestData } from './lib/next-request.ts'
8
+ import { buildNextRequest, localizeRequest, RequestData } from './lib/next-request.ts'
9
9
  import { buildResponse, FetchEventResult } from './lib/response.ts'
10
10
  import {
11
11
  getMiddlewareRouteMatcher,
@@ -31,8 +31,8 @@ export async function handleMiddleware(
31
31
  context: Context,
32
32
  nextHandler: NextHandler,
33
33
  ) {
34
- const nextRequest = buildNextRequest(request, context, nextConfig)
35
34
  const url = new URL(request.url)
35
+
36
36
  const reqLogger = logger
37
37
  .withLogLevel(
38
38
  request.headers.has(InternalHeaders.NFDebugLogging) ? LogLevel.Debug : LogLevel.Log,
@@ -40,16 +40,20 @@ export async function handleMiddleware(
40
40
  .withFields({ url_path: url.pathname })
41
41
  .withRequestID(request.headers.get(InternalHeaders.NFRequestID))
42
42
 
43
+ const { localizedUrl } = localizeRequest(url, nextConfig)
43
44
  // While we have already checked the path when mapping to the edge function,
44
45
  // Next.js supports extra rules that we need to check here too, because we
45
46
  // might be running an edge function for a path we should not. If we find
46
47
  // that's the case, short-circuit the execution.
47
- if (!matchesMiddleware(url.pathname, request, searchParamsToUrlQuery(url.searchParams))) {
48
+ if (
49
+ !matchesMiddleware(localizedUrl.pathname, request, searchParamsToUrlQuery(url.searchParams))
50
+ ) {
48
51
  reqLogger.debug('Aborting middleware due to runtime rules')
49
52
 
50
53
  return
51
54
  }
52
55
 
56
+ const nextRequest = buildNextRequest(request, context, nextConfig)
53
57
  try {
54
58
  const result = await nextHandler({ request: nextRequest })
55
59
  const response = await buildResponse({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@netlify/plugin-nextjs",
3
- "version": "5.5.1",
3
+ "version": "5.7.0-canary.0",
4
4
  "description": "Run Next.js seamlessly on Netlify",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",