@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.
- package/dist/build/content/server.js +1 -1
- package/dist/build/functions/edge.js +2 -5
- package/dist/build/functions/server.js +8 -9
- package/dist/build/plugin-context.js +3 -0
- package/dist/build/verification.js +2 -3
- package/dist/esm-chunks/{package-SHOGGUO3.js → package-SRGJOMMG.js} +3 -3
- package/dist/index.js +1 -1
- package/dist/run/handlers/server.js +1 -2
- package/dist/run/handlers/tracing.js +1 -1
- package/dist/run/headers.js +4 -5
- package/edge-runtime/lib/next-request.ts +28 -0
- package/edge-runtime/lib/response.ts +2 -1
- package/edge-runtime/lib/util.ts +14 -0
- package/edge-runtime/middleware.ts +7 -3
- package/package.json +1 -1
|
@@ -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 ===
|
|
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 = `
|
|
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.
|
|
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.
|
|
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 (
|
|
107
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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-
|
|
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,
|
package/dist/run/headers.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
|
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')
|
package/edge-runtime/lib/util.ts
CHANGED
|
@@ -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 (
|
|
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({
|