vinext 1.0.0-beta.1 → 1.0.0-beta.3
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 +10 -10
- package/dist/build/client-build-config.js +8 -1
- package/dist/build/report.d.ts +8 -1
- package/dist/build/report.js +12 -2
- package/dist/check.js +19 -2
- package/dist/cli.js +3 -2
- package/dist/config/config-matchers.d.ts +13 -37
- package/dist/config/config-matchers.js +35 -122
- package/dist/config/next-config.d.ts +15 -5
- package/dist/config/next-config.js +7 -1
- package/dist/config/request-context.d.ts +14 -0
- package/dist/config/request-context.js +26 -0
- package/dist/entries/app-browser-entry.d.ts +4 -10
- package/dist/entries/app-browser-entry.js +20 -4
- package/dist/entries/app-rsc-entry.js +30 -9
- package/dist/entries/app-rsc-manifest.js +36 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +13 -5
- package/dist/index.js +194 -101
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/init.d.ts +6 -4
- package/dist/init.js +20 -22
- package/dist/plugins/extensionless-dynamic-import.js +23 -18
- package/dist/plugins/ignore-dynamic-requests.js +3 -1
- package/dist/plugins/import-meta-url.js +17 -6
- package/dist/plugins/og-assets.js +1 -1
- package/dist/plugins/require-context.js +20 -15
- package/dist/plugins/transform-cache.d.ts +19 -0
- package/dist/plugins/transform-cache.js +36 -0
- package/dist/routing/app-route-graph.d.ts +27 -11
- package/dist/routing/app-route-graph.js +158 -22
- package/dist/routing/app-router.js +12 -6
- package/dist/routing/file-matcher.d.ts +11 -22
- package/dist/routing/file-matcher.js +21 -33
- package/dist/routing/pages-router.js +23 -29
- package/dist/routing/route-matching.d.ts +14 -1
- package/dist/routing/route-matching.js +13 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +7 -4
- package/dist/routing/route-trie.d.ts +5 -1
- package/dist/routing/route-trie.js +5 -2
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +3 -3
- package/dist/server/app-browser-entry.js +110 -37
- package/dist/server/app-browser-history-controller.d.ts +1 -1
- package/dist/server/app-browser-history-controller.js +3 -3
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +2 -2
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +3 -0
- package/dist/server/app-fallback-renderer.js +4 -2
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-page-boundary-render.d.ts +12 -1
- package/dist/server/app-page-boundary-render.js +84 -2
- package/dist/server/app-page-cache-finalizer.d.ts +1 -1
- package/dist/server/app-page-cache-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +2 -2
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +19 -2
- package/dist/server/app-page-dispatch.js +45 -23
- package/dist/server/app-page-element-builder.d.ts +9 -2
- package/dist/server/app-page-element-builder.js +144 -33
- package/dist/server/app-page-head.d.ts +56 -4
- package/dist/server/app-page-head.js +216 -85
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
- package/dist/server/app-page-http-access-fallback-metadata.js +114 -0
- package/dist/server/app-page-probe.d.ts +4 -0
- package/dist/server/app-page-probe.js +6 -2
- package/dist/server/app-page-render.d.ts +2 -2
- package/dist/server/app-page-render.js +1 -1
- package/dist/server/app-page-request.d.ts +15 -0
- package/dist/server/app-page-request.js +134 -14
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +16 -0
- package/dist/server/app-page-route-wiring.js +254 -52
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -1
- package/dist/server/app-post-middleware-context.d.ts +1 -1
- package/dist/server/app-post-middleware-context.js +1 -1
- package/dist/server/app-route-handler-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +40 -20
- package/dist/server/app-route-handler-execution.d.ts +6 -1
- package/dist/server/app-route-handler-execution.js +42 -16
- package/dist/server/app-route-handler-policy.d.ts +2 -0
- package/dist/server/app-route-handler-policy.js +3 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +9 -1
- package/dist/server/app-route-module-loader.js +4 -0
- package/dist/server/app-router-entry.js +6 -3
- package/dist/server/app-rsc-cache-busting.d.ts +1 -1
- package/dist/server/app-rsc-errors.d.ts +1 -1
- package/dist/server/app-rsc-errors.js +22 -2
- package/dist/server/app-rsc-handler.d.ts +10 -5
- package/dist/server/app-rsc-handler.js +73 -33
- package/dist/server/app-rsc-request-normalization.d.ts +3 -2
- package/dist/server/app-rsc-request-normalization.js +8 -2
- package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
- package/dist/server/app-rsc-response-finalizer.js +5 -11
- package/dist/server/app-rsc-route-matching.d.ts +29 -0
- package/dist/server/app-rsc-route-matching.js +93 -11
- package/dist/server/app-segment-config.js +13 -3
- package/dist/server/app-server-action-execution.d.ts +3 -1
- package/dist/server/app-server-action-execution.js +9 -7
- package/dist/server/app-ssr-entry.js +6 -6
- package/dist/server/app-ssr-stream.d.ts +1 -7
- package/dist/server/app-ssr-stream.js +2 -10
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- package/dist/server/config-headers.d.ts +24 -0
- package/dist/server/config-headers.js +52 -0
- package/dist/server/dev-initial-server-error.d.ts +1 -1
- package/dist/server/dev-server.d.ts +3 -1
- package/dist/server/dev-server.js +170 -321
- package/dist/server/headers.d.ts +2 -2
- package/dist/server/headers.js +4 -3
- package/dist/server/isr-cache.d.ts +11 -34
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- package/dist/server/middleware-matcher-pattern.d.ts +22 -0
- package/dist/server/middleware-matcher-pattern.js +219 -0
- package/dist/server/middleware-matcher.d.ts +2 -7
- package/dist/server/middleware-matcher.js +13 -60
- package/dist/server/middleware-path-to-regexp.d.ts +14 -0
- package/dist/server/middleware-path-to-regexp.js +228 -0
- package/dist/server/middleware-runtime.js +4 -3
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/pages-api-route.d.ts +2 -0
- package/dist/server/pages-api-route.js +4 -1
- package/dist/server/pages-data-route.d.ts +11 -1
- package/dist/server/pages-data-route.js +16 -1
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-get-initial-props.d.ts +16 -3
- package/dist/server/pages-get-initial-props.js +17 -10
- package/dist/server/pages-i18n.js +64 -16
- package/dist/server/pages-node-compat.d.ts +2 -0
- package/dist/server/pages-node-compat.js +7 -3
- package/dist/server/pages-page-data.d.ts +64 -10
- package/dist/server/pages-page-data.js +349 -80
- package/dist/server/pages-page-handler.d.ts +9 -3
- package/dist/server/pages-page-handler.js +131 -36
- package/dist/server/pages-page-response.d.ts +5 -3
- package/dist/server/pages-page-response.js +24 -27
- package/dist/server/pages-readiness.d.ts +3 -3
- package/dist/server/pages-request-pipeline.d.ts +8 -0
- package/dist/server/pages-request-pipeline.js +30 -12
- package/dist/server/pages-revalidate.d.ts +1 -1
- package/dist/server/pages-revalidate.js +54 -8
- package/dist/server/pages-router-entry.d.ts +1 -0
- package/dist/server/pages-router-entry.js +16 -4
- package/dist/server/prerender-route-params.js +2 -2
- package/dist/server/prod-server.d.ts +3 -5
- package/dist/server/prod-server.js +72 -35
- package/dist/server/request-pipeline.d.ts +15 -29
- package/dist/server/request-pipeline.js +24 -59
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/rsc-stream-hints.js +122 -18
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +29 -0
- package/dist/shims/app.d.ts +1 -1
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- package/dist/shims/cache-request-state.d.ts +27 -1
- package/dist/shims/cache-request-state.js +60 -1
- package/dist/shims/cache-runtime.d.ts +18 -1
- package/dist/shims/cache-runtime.js +26 -8
- package/dist/shims/cache.d.ts +5 -5
- package/dist/shims/cache.js +27 -11
- package/dist/shims/document.d.ts +42 -83
- package/dist/shims/document.js +62 -53
- package/dist/shims/dynamic.d.ts +5 -25
- package/dist/shims/dynamic.js +29 -8
- package/dist/shims/error.d.ts +1 -1
- package/dist/shims/error.js +2 -2
- package/dist/shims/fetch-cache.js +13 -4
- package/dist/shims/font-google-base.d.ts +11 -4
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.d.ts +10 -3
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/head.d.ts +13 -2
- package/dist/shims/head.js +3 -3
- package/dist/shims/headers.d.ts +37 -43
- package/dist/shims/headers.js +183 -74
- package/dist/shims/image.d.ts +27 -37
- package/dist/shims/image.js +21 -14
- package/dist/shims/internal/cookie-serialize.d.ts +12 -9
- package/dist/shims/internal/cookie-serialize.js +10 -7
- package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- package/dist/shims/internal/utils.d.ts +3 -3
- package/dist/shims/legacy-image.d.ts +3 -37
- package/dist/shims/legacy-image.js +6 -16
- package/dist/shims/link.d.ts +15 -22
- package/dist/shims/link.js +34 -13
- package/dist/shims/metadata.d.ts +7 -17
- package/dist/shims/metadata.js +74 -24
- package/dist/shims/navigation-context-state.d.ts +1 -1
- package/dist/shims/navigation.d.ts +7 -3
- package/dist/shims/navigation.js +25 -14
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/next-shims-augmentations.d.ts +1 -0
- package/dist/shims/next-shims-public.d.ts +1 -0
- package/dist/shims/public-shim-map.json.js +103 -0
- package/dist/shims/readonly-url-search-params.d.ts +3 -3
- package/dist/shims/readonly-url-search-params.js +3 -3
- package/dist/shims/request-context.d.ts +4 -1
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/router.d.ts +55 -21
- package/dist/shims/router.js +180 -44
- package/dist/shims/script.d.ts +6 -10
- package/dist/shims/script.js +7 -4
- package/dist/shims/server.d.ts +50 -26
- package/dist/shims/server.js +193 -55
- package/dist/shims/unified-request-context.d.ts +22 -3
- package/dist/shims/unified-request-context.js +84 -1
- package/dist/shims/url-safety.d.ts +1 -1
- package/dist/shims/url-safety.js +2 -2
- package/dist/typegen.d.ts +8 -2
- package/dist/typegen.js +76 -15
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- package/dist/utils/domain-locale.d.ts +2 -2
- package/dist/utils/external-url.d.ts +5 -0
- package/dist/utils/external-url.js +7 -0
- package/dist/utils/html-limited-bots.js +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/dist/utils/regex-safety.d.ts +8 -0
- package/dist/utils/regex-safety.js +737 -0
- package/package.json +13 -2
package/README.md
CHANGED
|
@@ -206,7 +206,10 @@ vinext works everywhere. It natively supports Cloudflare Workers (with `npx @vin
|
|
|
206
206
|
vinext is a Vite plugin that reimplements the public Next.js API — routing, server rendering, `next/*` module imports, the CLI — so you can run Next.js applications on Vite instead of the Next.js compiler toolchain. It can be deployed anywhere: Cloudflare Workers is the first natively supported target, with other platforms available via Nitro. Native adapters for more platforms are [planned](https://github.com/cloudflare/vinext/issues/80).
|
|
207
207
|
|
|
208
208
|
**Is this a fork of Next.js?**
|
|
209
|
-
No. vinext is an alternative implementation of the Next.js API surface built on Vite.
|
|
209
|
+
No. vinext is an alternative implementation of the Next.js API surface built on Vite. The core is written from scratch. The goal is not to create a competing framework or add features beyond what Next.js offers; it is to provide the same well-defined API surface on Vite's toolchain.
|
|
210
|
+
|
|
211
|
+
**Does vinext require Next.js to be installed?**
|
|
212
|
+
No. vinext ships fallback declarations for the supported `next` and `next/*` APIs, so applications can run and type-check without the `next` package. If both packages are installed, vinext keeps using Next.js's authoritative types and adds only its own extensions. Compatibility features that consume Next.js internals, such as `styled-jsx`, may still require a matching Next.js installation when used.
|
|
210
213
|
|
|
211
214
|
**How is this different from OpenNext?**
|
|
212
215
|
[OpenNext](https://opennext.js.org/) adapts the _output_ of a standard `next build` to run on various platforms. Because it builds on Next.js's own output, it inherits broad API coverage and has been well-tested for much longer. vinext takes a different approach: it reimplements the Next.js APIs on Vite from scratch, which means faster builds and smaller bundles, but less coverage of the long tail of Next.js features. If you need a mature, well-tested way to run Next.js outside Vercel, OpenNext is the safer choice. If you want a lighter Vite-based toolchain and do not need every Next.js API, vinext may be a good fit.
|
|
@@ -342,24 +345,16 @@ Requires a custom domain (zone analytics are unavailable on `*.workers.dev`) and
|
|
|
342
345
|
|
|
343
346
|
#### Custom Vite configuration
|
|
344
347
|
|
|
345
|
-
If you need to customize the Vite config, create a `vite.config.ts`. vinext will merge its config with yours.
|
|
348
|
+
If you need to customize the Vite config, create a `vite.config.ts`. vinext will merge its config with yours. For Cloudflare Workers deployment with the App Router, configure `@cloudflare/vite-plugin` so the RSC environment runs in workerd:
|
|
346
349
|
|
|
347
350
|
```ts
|
|
348
351
|
import { defineConfig } from "vite";
|
|
349
352
|
import vinext from "vinext";
|
|
350
|
-
import rsc from "@vitejs/plugin-rsc";
|
|
351
353
|
import { cloudflare } from "@cloudflare/vite-plugin";
|
|
352
354
|
|
|
353
355
|
export default defineConfig({
|
|
354
356
|
plugins: [
|
|
355
357
|
vinext(),
|
|
356
|
-
rsc({
|
|
357
|
-
entries: {
|
|
358
|
-
rsc: "virtual:vinext-rsc-entry",
|
|
359
|
-
ssr: "virtual:vinext-app-ssr-entry",
|
|
360
|
-
client: "virtual:vinext-app-browser-entry",
|
|
361
|
-
},
|
|
362
|
-
}),
|
|
363
358
|
cloudflare({
|
|
364
359
|
viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] },
|
|
365
360
|
}),
|
|
@@ -367,6 +362,11 @@ export default defineConfig({
|
|
|
367
362
|
});
|
|
368
363
|
```
|
|
369
364
|
|
|
365
|
+
> **Do not register `@vitejs/plugin-rsc` yourself.** It is an optional peer dependency, so it must be
|
|
366
|
+
> _installed_ in your project, but vinext auto-registers it whenever an `app/` directory is detected.
|
|
367
|
+
> Adding an explicit `rsc()` call fails the build with `[vinext] Duplicate @vitejs/plugin-rsc detected`.
|
|
368
|
+
> Pass `rsc: false` to `vinext()` only if you want to own that registration.
|
|
369
|
+
|
|
370
370
|
See the [examples](#live-examples) for complete working configurations.
|
|
371
371
|
|
|
372
372
|
### Other platforms (via Nitro)
|
|
@@ -6,6 +6,7 @@ const ROUTE_OWNED_CLIENT_SHIMS = /* @__PURE__ */ new Set([
|
|
|
6
6
|
"dynamic-preload-chunks",
|
|
7
7
|
"form",
|
|
8
8
|
"image",
|
|
9
|
+
"internal/hybrid-client-route-owner",
|
|
9
10
|
"layout-segment-context",
|
|
10
11
|
"legacy-image",
|
|
11
12
|
"link",
|
|
@@ -88,7 +89,13 @@ function createClientManualChunks(shimsDir, preserveRouteBoundaries = false) {
|
|
|
88
89
|
if (id.includes("node_modules")) {
|
|
89
90
|
const pkg = getPackageName(id);
|
|
90
91
|
if (!pkg) return void 0;
|
|
91
|
-
if (pkg === "react
|
|
92
|
+
if (pkg === "react-dom") {
|
|
93
|
+
const slashedId = toSlash(id);
|
|
94
|
+
const sub = slashedId.slice(slashedId.lastIndexOf("react-dom/") + 10);
|
|
95
|
+
if (sub.startsWith("server.") || sub.startsWith("static.") || sub.startsWith("cjs/react-dom-server")) return "react-dom-server";
|
|
96
|
+
return "framework";
|
|
97
|
+
}
|
|
98
|
+
if (pkg === "react" || pkg === "scheduler") return "framework";
|
|
92
99
|
return;
|
|
93
100
|
}
|
|
94
101
|
const slashedId = toSlash(id);
|
package/dist/build/report.d.ts
CHANGED
|
@@ -44,6 +44,13 @@ declare function hasExportedName(code: string, name: string): boolean;
|
|
|
44
44
|
*/
|
|
45
45
|
declare function extractExportConstString(code: string, name: string): string | null;
|
|
46
46
|
declare function extractMiddlewareMatcherConfig(filePath: string): StaticMiddlewareMatcher | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Extract the statically analyzable `config.matcher` value without first
|
|
49
|
+
* narrowing it to vinext's runtime matcher type. Build validation needs the
|
|
50
|
+
* raw value so malformed matcher objects are rejected instead of disappearing
|
|
51
|
+
* as though no matcher had been configured.
|
|
52
|
+
*/
|
|
53
|
+
declare function extractMiddlewareMatcherConfigValue(filePath: string): unknown;
|
|
47
54
|
/**
|
|
48
55
|
* Extracts the numeric value of `export const <name> = <number|false>`.
|
|
49
56
|
* Supports integers, decimals, negative values, `Infinity`, and `false`.
|
|
@@ -137,4 +144,4 @@ declare function printBuildReport(options: {
|
|
|
137
144
|
prerenderResult?: PrerenderResult;
|
|
138
145
|
}): Promise<void>;
|
|
139
146
|
//#endregion
|
|
140
|
-
export { RouteRow, RouteType, StaticMiddlewareMatcher, buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
|
147
|
+
export { RouteRow, RouteType, StaticMiddlewareMatcher, buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, extractMiddlewareMatcherConfigValue, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
package/dist/build/report.js
CHANGED
|
@@ -135,6 +135,16 @@ function extractStringFromConstInitializer(initializer) {
|
|
|
135
135
|
return null;
|
|
136
136
|
}
|
|
137
137
|
function extractMiddlewareMatcherConfig(filePath) {
|
|
138
|
+
const value = extractMiddlewareMatcherConfigValue(filePath);
|
|
139
|
+
return isStaticMiddlewareMatcher(value) ? value : void 0;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Extract the statically analyzable `config.matcher` value without first
|
|
143
|
+
* narrowing it to vinext's runtime matcher type. Build validation needs the
|
|
144
|
+
* raw value so malformed matcher objects are rejected instead of disappearing
|
|
145
|
+
* as though no matcher had been configured.
|
|
146
|
+
*/
|
|
147
|
+
function extractMiddlewareMatcherConfigValue(filePath) {
|
|
138
148
|
let code;
|
|
139
149
|
try {
|
|
140
150
|
code = fs.readFileSync(filePath, "utf8");
|
|
@@ -148,7 +158,7 @@ function extractMiddlewareMatcherConfig(filePath) {
|
|
|
148
158
|
const matcherExpression = objectPropertyValue(config, "matcher");
|
|
149
159
|
if (!matcherExpression) return void 0;
|
|
150
160
|
const value = extractStaticJsonValue(matcherExpression);
|
|
151
|
-
return
|
|
161
|
+
return value === UNSUPPORTED_STATIC_VALUE ? void 0 : value;
|
|
152
162
|
}
|
|
153
163
|
function objectPropertyValue(object, key) {
|
|
154
164
|
for (const property of object.properties) {
|
|
@@ -596,4 +606,4 @@ async function printBuildReport(options) {
|
|
|
596
606
|
}
|
|
597
607
|
}
|
|
598
608
|
//#endregion
|
|
599
|
-
export { buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
|
609
|
+
export { buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, extractMiddlewareMatcherConfigValue, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
|
package/dist/check.js
CHANGED
|
@@ -230,6 +230,10 @@ const CONFIG_SUPPORT = {
|
|
|
230
230
|
status: "supported",
|
|
231
231
|
detail: "server actions via 'use server' directive"
|
|
232
232
|
},
|
|
233
|
+
"experimental.allowedRevalidateHeaderKeys": {
|
|
234
|
+
status: "supported",
|
|
235
|
+
detail: "forwards explicitly allowed request headers during Pages Router revalidation"
|
|
236
|
+
},
|
|
233
237
|
"experimental.prefetchInlining": {
|
|
234
238
|
status: "partial",
|
|
235
239
|
detail: "config recognized; Link prefetch preserves pending/dedup semantics, but vinext does not implement per-segment cache storage"
|
|
@@ -390,6 +394,19 @@ function findSourceFiles(dir, extensions = [
|
|
|
390
394
|
}
|
|
391
395
|
return results;
|
|
392
396
|
}
|
|
397
|
+
/**
|
|
398
|
+
* Find files that can contribute to the application compatibility surface.
|
|
399
|
+
* Test modules and test-runner configuration are executed by their own runners
|
|
400
|
+
* rather than bundled into the vinext application, so reporting their imports
|
|
401
|
+
* or CJS globals as migration blockers produces false positives.
|
|
402
|
+
*/
|
|
403
|
+
function findRuntimeSourceFiles(root) {
|
|
404
|
+
return findSourceFiles(root).filter((file) => {
|
|
405
|
+
const basename = path.basename(file);
|
|
406
|
+
const isTestRunnerConfig = /^(?:jest|playwright|vitest)\.config\.[cm]?[jt]sx?$/.test(basename);
|
|
407
|
+
return !/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(basename) && !isTestRunnerConfig;
|
|
408
|
+
});
|
|
409
|
+
}
|
|
393
410
|
function isIdentStart(c) {
|
|
394
411
|
return c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c === "_" || c === "$";
|
|
395
412
|
}
|
|
@@ -588,7 +605,7 @@ function hasFreeCjsGlobal(content) {
|
|
|
588
605
|
* Scan source files for `import ... from 'next/...'` statements.
|
|
589
606
|
*/
|
|
590
607
|
function scanImports(root) {
|
|
591
|
-
const files =
|
|
608
|
+
const files = findRuntimeSourceFiles(root);
|
|
592
609
|
const importUsage = /* @__PURE__ */ new Map();
|
|
593
610
|
const importRegex = /(?:import\s+(?:[\w{},\s*]+\s+from\s+)?|require\s*\()['"]([^'"]+)['"]\)?/g;
|
|
594
611
|
const typeOnlyImportRegex = /import\s+type\s+/;
|
|
@@ -911,7 +928,7 @@ function checkConventions(root) {
|
|
|
911
928
|
detail: "required for Vite — vinext init will add it automatically"
|
|
912
929
|
});
|
|
913
930
|
}
|
|
914
|
-
const allSourceFiles =
|
|
931
|
+
const allSourceFiles = findRuntimeSourceFiles(root);
|
|
915
932
|
const viewTransitionRegex = /import\s+\{[^}]*\bViewTransition\b[^}]*\}\s+from\s+['"]react['"]/;
|
|
916
933
|
const viewTransitionFiles = [];
|
|
917
934
|
const cjsGlobalFiles = [];
|
package/dist/cli.js
CHANGED
|
@@ -482,11 +482,12 @@ async function typegen() {
|
|
|
482
482
|
root,
|
|
483
483
|
mode: "production"
|
|
484
484
|
});
|
|
485
|
-
const
|
|
485
|
+
const result = await generateRouteTypes({
|
|
486
486
|
root,
|
|
487
487
|
pageExtensions: (await resolveNextConfig(await loadNextConfig(root, PHASE_PRODUCTION_BUILD), root)).pageExtensions
|
|
488
488
|
});
|
|
489
|
-
|
|
489
|
+
const nextEnvMessage = result.nextEnvStatus === "unchanged" ? `${path.relative(root, result.nextEnvPath)} is up to date` : `${result.nextEnvStatus === "created" ? "Created" : "Updated"} ${path.relative(root, result.nextEnvPath)}`;
|
|
490
|
+
console.log(`\n Generated route types at ${path.relative(root, result.routeTypesPath)}\n ${nextEnvMessage}\n`);
|
|
490
491
|
}
|
|
491
492
|
async function initCommand() {
|
|
492
493
|
const parsed = parseArgs(rawArgs);
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { HasCondition, NextHeader, NextI18nConfig, NextRedirect, NextRewrite } from "./next-config.js";
|
|
2
|
+
import { RequestContext, normalizeHost, parseCookies, requestContextFromRequest } from "./request-context.js";
|
|
3
|
+
import { isExternalUrl } from "../utils/external-url.js";
|
|
2
4
|
|
|
3
5
|
//#region src/config/config-matchers.d.ts
|
|
4
6
|
/**
|
|
5
7
|
* Detect regex patterns vulnerable to catastrophic backtracking (ReDoS).
|
|
6
8
|
*
|
|
7
|
-
* Uses
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Uses the same deterministic structural analysis as middleware matcher
|
|
10
|
+
* validation. Nested bounded repetition is accepted only when its repeated
|
|
11
|
+
* language has fixed width and unambiguous branches; a fixed outer count can
|
|
12
|
+
* otherwise still cause polynomially catastrophic backtracking on long near
|
|
13
|
+
* misses.
|
|
11
14
|
*
|
|
12
15
|
* Returns true if the pattern appears safe, false if it's potentially dangerous.
|
|
13
16
|
*/
|
|
14
|
-
declare function isSafeRegex(pattern: string): boolean;
|
|
17
|
+
declare function isSafeRegex(pattern: string, flags?: string): boolean;
|
|
15
18
|
/**
|
|
16
19
|
* Compile a regex pattern safely. Returns the compiled RegExp or null if the
|
|
17
20
|
* pattern is invalid or vulnerable to ReDoS.
|
|
@@ -27,16 +30,6 @@ declare function safeRegExp(pattern: string, flags?: string): RegExp | null;
|
|
|
27
30
|
* which CodeQL flags as incomplete sanitization), then groups are restored.
|
|
28
31
|
*/
|
|
29
32
|
declare function escapeHeaderSource(source: string): string;
|
|
30
|
-
/**
|
|
31
|
-
* Request context needed for evaluating has/missing conditions.
|
|
32
|
-
* Callers extract the relevant parts from the incoming Request.
|
|
33
|
-
*/
|
|
34
|
-
type RequestContext = {
|
|
35
|
-
readonly headers: Headers;
|
|
36
|
-
readonly cookies: Record<string, string>;
|
|
37
|
-
readonly query: URLSearchParams;
|
|
38
|
-
readonly host: string;
|
|
39
|
-
};
|
|
40
33
|
/**
|
|
41
34
|
* basePath gating state passed alongside the pathname to every matcher.
|
|
42
35
|
*
|
|
@@ -59,22 +52,6 @@ type BasePathMatchState = {
|
|
|
59
52
|
*/
|
|
60
53
|
hadBasePath: boolean;
|
|
61
54
|
};
|
|
62
|
-
/**
|
|
63
|
-
* Parse a Cookie header string into a key-value record.
|
|
64
|
-
*/
|
|
65
|
-
declare function parseCookies(cookieHeader: string | null): Record<string, string>;
|
|
66
|
-
/**
|
|
67
|
-
* Build a RequestContext from a Web Request object.
|
|
68
|
-
*
|
|
69
|
-
* `cookies` and `query` are lazy memoized getters: they are consumed only by
|
|
70
|
-
* `has`/`missing` condition evaluation (`checkHasConditions` /
|
|
71
|
-
* `matchesRuleConditions`), and most apps configure no such conditions. The
|
|
72
|
-
* cookie split and `searchParams` access are therefore deferred until first
|
|
73
|
-
* read and computed at most once. Mirrors `headersContextFromRequest` in
|
|
74
|
-
* `shims/headers.ts`.
|
|
75
|
-
*/
|
|
76
|
-
declare function requestContextFromRequest(request: Request): RequestContext;
|
|
77
|
-
declare function normalizeHost(hostHeader: string | null, fallbackHostname: string): string;
|
|
78
55
|
/**
|
|
79
56
|
* Unpack `x-middleware-request-*` headers from the collected middleware
|
|
80
57
|
* response headers into the actual request, and strip all `x-middleware-*`
|
|
@@ -147,7 +124,7 @@ declare function matchRedirect(pathname: string, redirects: NextRedirect[], ctx:
|
|
|
147
124
|
* to evaluate has/missing conditions. Next.js always has request context
|
|
148
125
|
* when evaluating rewrites, so this parameter is required.
|
|
149
126
|
*/
|
|
150
|
-
declare function matchRewrite(pathname: string, rewrites: NextRewrite[], ctx: RequestContext, basePathState?: BasePathMatchState): string | null;
|
|
127
|
+
declare function matchRewrite(pathname: string, rewrites: NextRewrite[], ctx: RequestContext, basePathState?: BasePathMatchState, paramsPathname?: string): string | null;
|
|
151
128
|
/**
|
|
152
129
|
* Check whether a rewrite source can match a pathname without evaluating its
|
|
153
130
|
* request-dependent `has` / `missing` conditions.
|
|
@@ -175,7 +152,6 @@ declare function sanitizeDestination(dest: string): string;
|
|
|
175
152
|
* Detects any URL scheme (http:, https:, data:, javascript:, blob:, etc.)
|
|
176
153
|
* per RFC 3986, plus protocol-relative URLs (//).
|
|
177
154
|
*/
|
|
178
|
-
declare function isExternalUrl(url: string): boolean;
|
|
179
155
|
/**
|
|
180
156
|
* Merge the original request's query params into a config-redirect
|
|
181
157
|
* destination, preserving them on the resulting `Location`.
|
|
@@ -218,8 +194,8 @@ declare function matchHeaders(pathname: string, headers: NextHeader[], ctx: Requ
|
|
|
218
194
|
value: string;
|
|
219
195
|
}>;
|
|
220
196
|
/**
|
|
221
|
-
* Apply Next.js i18n locale-prefix transformation to a set of redirect
|
|
222
|
-
* rewrite rules. Mirrors the relevant slice of Next.js's `processRoutes`
|
|
197
|
+
* Apply Next.js i18n locale-prefix transformation to a set of redirect,
|
|
198
|
+
* rewrite, or header rules. Mirrors the relevant slice of Next.js's `processRoutes`
|
|
223
199
|
* (load-custom-routes.ts) with one deliberate divergence noted below.
|
|
224
200
|
*
|
|
225
201
|
* For each rule:
|
|
@@ -250,8 +226,8 @@ declare function matchHeaders(pathname: string, headers: NextHeader[], ctx: Requ
|
|
|
250
226
|
* Mirrors the Next.js reference in
|
|
251
227
|
* packages/next/src/lib/load-custom-routes.ts — see `processRoutes`.
|
|
252
228
|
*/
|
|
253
|
-
declare function applyLocaleToRoutes<T extends NextRedirect | NextRewrite>(routes: T[], i18n: NextI18nConfig | null | undefined, type: "redirect" | "rewrite", options?: {
|
|
229
|
+
declare function applyLocaleToRoutes<T extends NextRedirect | NextRewrite | NextHeader>(routes: T[], i18n: NextI18nConfig | null | undefined, type: "redirect" | "rewrite" | "header", options?: {
|
|
254
230
|
trailingSlash?: boolean;
|
|
255
231
|
}): T[];
|
|
256
232
|
//#endregion
|
|
257
|
-
export { BasePathMatchState, RequestContext, applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, escapeHeaderSource, isExternalUrl, isSafeRegex, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, matchesRewriteSource, normalizeHost, parseCookies, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, safeRegExp, sanitizeDestination };
|
|
233
|
+
export { BasePathMatchState, type RequestContext, applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, escapeHeaderSource, isExternalUrl, isSafeRegex, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, matchesRewriteSource, normalizeHost, parseCookies, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, safeRegExp, sanitizeDestination };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../utils/protocol-headers.js";
|
|
1
|
+
import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
|
|
2
2
|
import { buildRequestHeadersFromMiddlewareResponse } from "../utils/middleware-request-headers.js";
|
|
3
|
-
import {
|
|
3
|
+
import { analyzeRegexSafety } from "../utils/regex-safety.js";
|
|
4
|
+
import { normalizeHost, parseCookies, requestContextFromRequest } from "./request-context.js";
|
|
5
|
+
import { isExternalUrl } from "../utils/external-url.js";
|
|
4
6
|
//#region src/config/config-matchers.ts
|
|
5
7
|
/**
|
|
6
8
|
* Cache for compiled regex patterns in matchConfigPattern.
|
|
@@ -191,75 +193,16 @@ function stripHopByHopRequestHeaders(headers) {
|
|
|
191
193
|
/**
|
|
192
194
|
* Detect regex patterns vulnerable to catastrophic backtracking (ReDoS).
|
|
193
195
|
*
|
|
194
|
-
* Uses
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
196
|
+
* Uses the same deterministic structural analysis as middleware matcher
|
|
197
|
+
* validation. Nested bounded repetition is accepted only when its repeated
|
|
198
|
+
* language has fixed width and unambiguous branches; a fixed outer count can
|
|
199
|
+
* otherwise still cause polynomially catastrophic backtracking on long near
|
|
200
|
+
* misses.
|
|
198
201
|
*
|
|
199
202
|
* Returns true if the pattern appears safe, false if it's potentially dangerous.
|
|
200
203
|
*/
|
|
201
|
-
function isSafeRegex(pattern) {
|
|
202
|
-
|
|
203
|
-
let depth = 0;
|
|
204
|
-
let i = 0;
|
|
205
|
-
while (i < pattern.length) {
|
|
206
|
-
const ch = pattern[i];
|
|
207
|
-
if (ch === "\\") {
|
|
208
|
-
i += 2;
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
if (ch === "[") {
|
|
212
|
-
i++;
|
|
213
|
-
while (i < pattern.length && pattern[i] !== "]") {
|
|
214
|
-
if (pattern[i] === "\\") i++;
|
|
215
|
-
i++;
|
|
216
|
-
}
|
|
217
|
-
i++;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
if (ch === "(") {
|
|
221
|
-
depth++;
|
|
222
|
-
if (quantifierAtDepth.length <= depth) quantifierAtDepth.push(false);
|
|
223
|
-
else quantifierAtDepth[depth] = false;
|
|
224
|
-
i++;
|
|
225
|
-
continue;
|
|
226
|
-
}
|
|
227
|
-
if (ch === ")") {
|
|
228
|
-
const hadQuantifier = depth > 0 && quantifierAtDepth[depth];
|
|
229
|
-
if (depth > 0) depth--;
|
|
230
|
-
const next = pattern[i + 1];
|
|
231
|
-
if (next === "+" || next === "*" || next === "{") {
|
|
232
|
-
if (hadQuantifier) return false;
|
|
233
|
-
if (depth >= 0 && depth < quantifierAtDepth.length) quantifierAtDepth[depth] = true;
|
|
234
|
-
}
|
|
235
|
-
i++;
|
|
236
|
-
continue;
|
|
237
|
-
}
|
|
238
|
-
if (ch === "+" || ch === "*") {
|
|
239
|
-
if (depth > 0) quantifierAtDepth[depth] = true;
|
|
240
|
-
i++;
|
|
241
|
-
continue;
|
|
242
|
-
}
|
|
243
|
-
if (ch === "?") {
|
|
244
|
-
const prev = i > 0 ? pattern[i - 1] : "";
|
|
245
|
-
if (prev !== "+" && prev !== "*" && prev !== "?" && prev !== "}") {
|
|
246
|
-
if (depth > 0) quantifierAtDepth[depth] = true;
|
|
247
|
-
}
|
|
248
|
-
i++;
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
if (ch === "{") {
|
|
252
|
-
let j = i + 1;
|
|
253
|
-
while (j < pattern.length && /[\d,]/.test(pattern[j])) j++;
|
|
254
|
-
if (j < pattern.length && pattern[j] === "}" && j > i + 1) {
|
|
255
|
-
if (depth > 0) quantifierAtDepth[depth] = true;
|
|
256
|
-
i = j + 1;
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
i++;
|
|
261
|
-
}
|
|
262
|
-
return true;
|
|
204
|
+
function isSafeRegex(pattern, flags) {
|
|
205
|
+
return analyzeRegexSafety(pattern, { ignoreCase: flags?.includes("i") }) === null;
|
|
263
206
|
}
|
|
264
207
|
/**
|
|
265
208
|
* Compile a regex pattern safely. Returns the compiled RegExp or null if the
|
|
@@ -268,8 +211,8 @@ function isSafeRegex(pattern) {
|
|
|
268
211
|
* Logs a warning when a pattern is rejected so developers can fix their config.
|
|
269
212
|
*/
|
|
270
213
|
function safeRegExp(pattern, flags) {
|
|
271
|
-
if (!isSafeRegex(pattern)) {
|
|
272
|
-
console.warn(`[vinext] Rejecting potentially unsafe regex pattern (ReDoS risk): ${pattern}\n
|
|
214
|
+
if (!isSafeRegex(pattern, flags)) {
|
|
215
|
+
console.warn(`[vinext] Rejecting potentially unsafe regex pattern (ReDoS risk): ${pattern}\n Nested or ambiguous repetition can cause catastrophic backtracking.\n Simplify the pattern to make repeated matches fixed and unambiguous.`);
|
|
273
216
|
return null;
|
|
274
217
|
}
|
|
275
218
|
try {
|
|
@@ -342,40 +285,6 @@ function shouldEvaluateRule(ruleBasePath, state) {
|
|
|
342
285
|
return ruleBasePath === false ? !state.hadBasePath : state.hadBasePath;
|
|
343
286
|
}
|
|
344
287
|
/**
|
|
345
|
-
* Parse a Cookie header string into a key-value record.
|
|
346
|
-
*/
|
|
347
|
-
function parseCookies(cookieHeader) {
|
|
348
|
-
return parseCookieHeader(cookieHeader);
|
|
349
|
-
}
|
|
350
|
-
/**
|
|
351
|
-
* Build a RequestContext from a Web Request object.
|
|
352
|
-
*
|
|
353
|
-
* `cookies` and `query` are lazy memoized getters: they are consumed only by
|
|
354
|
-
* `has`/`missing` condition evaluation (`checkHasConditions` /
|
|
355
|
-
* `matchesRuleConditions`), and most apps configure no such conditions. The
|
|
356
|
-
* cookie split and `searchParams` access are therefore deferred until first
|
|
357
|
-
* read and computed at most once. Mirrors `headersContextFromRequest` in
|
|
358
|
-
* `shims/headers.ts`.
|
|
359
|
-
*/
|
|
360
|
-
function requestContextFromRequest(request) {
|
|
361
|
-
const url = new URL(request.url);
|
|
362
|
-
let cookies;
|
|
363
|
-
let query;
|
|
364
|
-
return {
|
|
365
|
-
headers: request.headers,
|
|
366
|
-
get cookies() {
|
|
367
|
-
return cookies ??= parseCookies(request.headers.get("cookie"));
|
|
368
|
-
},
|
|
369
|
-
get query() {
|
|
370
|
-
return query ??= url.searchParams;
|
|
371
|
-
},
|
|
372
|
-
host: normalizeHost(request.headers.get("host"), url.hostname)
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
function normalizeHost(hostHeader, fallbackHostname) {
|
|
376
|
-
return (hostHeader ?? fallbackHostname).split(":", 1)[0].toLowerCase();
|
|
377
|
-
}
|
|
378
|
-
/**
|
|
379
288
|
* Unpack `x-middleware-request-*` headers from the collected middleware
|
|
380
289
|
* response headers into the actual request, and strip all `x-middleware-*`
|
|
381
290
|
* internal signals so they never reach clients.
|
|
@@ -410,7 +319,7 @@ function _emptyParams() {
|
|
|
410
319
|
return Object.create(null);
|
|
411
320
|
}
|
|
412
321
|
function _matchConditionValue(actualValue, expectedValue) {
|
|
413
|
-
if (expectedValue
|
|
322
|
+
if (!expectedValue) return actualValue ? _emptyParams() : null;
|
|
414
323
|
const re = _cachedConditionRegex(expectedValue);
|
|
415
324
|
if (re) {
|
|
416
325
|
const match = re.exec(actualValue);
|
|
@@ -440,9 +349,10 @@ function matchSingleCondition(condition, ctx) {
|
|
|
440
349
|
return _matchConditionValue(cookieValue, condition.value);
|
|
441
350
|
}
|
|
442
351
|
case "query": {
|
|
443
|
-
const
|
|
444
|
-
if (
|
|
445
|
-
|
|
352
|
+
const queryValues = ctx.query.getAll(condition.key);
|
|
353
|
+
if (queryValues.length === 0) return null;
|
|
354
|
+
if (!condition.value && queryValues.length > 1) return _emptyParams();
|
|
355
|
+
return _matchConditionValue(queryValues[queryValues.length - 1], condition.value);
|
|
446
356
|
}
|
|
447
357
|
case "host":
|
|
448
358
|
if (condition.value !== void 0) return _matchConditionValue(ctx.host, condition.value);
|
|
@@ -710,11 +620,12 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
|
|
|
710
620
|
* to evaluate has/missing conditions. Next.js always has request context
|
|
711
621
|
* when evaluating rewrites, so this parameter is required.
|
|
712
622
|
*/
|
|
713
|
-
function matchRewrite(pathname, rewrites, ctx, basePathState = _BASEPATH_DEFAULT) {
|
|
623
|
+
function matchRewrite(pathname, rewrites, ctx, basePathState = _BASEPATH_DEFAULT, paramsPathname = pathname) {
|
|
714
624
|
for (const rewrite of rewrites) {
|
|
715
625
|
if (!shouldEvaluateRule(rewrite.basePath, basePathState)) continue;
|
|
716
|
-
const
|
|
717
|
-
if (
|
|
626
|
+
const matchedParams = matchConfigPattern(pathname, rewrite.source);
|
|
627
|
+
if (matchedParams) {
|
|
628
|
+
const params = paramsPathname === pathname ? matchedParams : matchConfigPattern(paramsPathname, rewrite.source) ?? matchedParams;
|
|
718
629
|
const conditionParams = rewrite.has || rewrite.missing ? collectConditionParams(rewrite.has, rewrite.missing, ctx) : _emptyParams();
|
|
719
630
|
if (!conditionParams) continue;
|
|
720
631
|
const rewriteParams = {
|
|
@@ -868,9 +779,6 @@ function sanitizeDestination(dest) {
|
|
|
868
779
|
* Detects any URL scheme (http:, https:, data:, javascript:, blob:, etc.)
|
|
869
780
|
* per RFC 3986, plus protocol-relative URLs (//).
|
|
870
781
|
*/
|
|
871
|
-
function isExternalUrl(url) {
|
|
872
|
-
return /^[a-z][a-z0-9+.-]*:/i.test(url) || url.startsWith("//");
|
|
873
|
-
}
|
|
874
782
|
/**
|
|
875
783
|
* Merge the original request's query params into a config-redirect
|
|
876
784
|
* destination, preserving them on the resulting `Location`.
|
|
@@ -927,6 +835,9 @@ async function proxyExternalRequest(request, externalUrl) {
|
|
|
927
835
|
for (const key of keysToDelete) headers.delete(key);
|
|
928
836
|
headers.delete(VINEXT_PRERENDER_SECRET_HEADER);
|
|
929
837
|
headers.delete(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER);
|
|
838
|
+
headers.delete(PRERENDER_REVALIDATE_HEADER);
|
|
839
|
+
headers.delete(PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER);
|
|
840
|
+
headers.delete(VINEXT_REVALIDATE_HOST_HEADER);
|
|
930
841
|
headers.delete(VINEXT_MW_CTX_HEADER);
|
|
931
842
|
const method = request.method;
|
|
932
843
|
const hasBody = method !== "GET" && method !== "HEAD";
|
|
@@ -1004,8 +915,8 @@ function _escapeRegexString(value) {
|
|
|
1004
915
|
return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
|
|
1005
916
|
}
|
|
1006
917
|
/**
|
|
1007
|
-
* Apply Next.js i18n locale-prefix transformation to a set of redirect
|
|
1008
|
-
* rewrite rules. Mirrors the relevant slice of Next.js's `processRoutes`
|
|
918
|
+
* Apply Next.js i18n locale-prefix transformation to a set of redirect,
|
|
919
|
+
* rewrite, or header rules. Mirrors the relevant slice of Next.js's `processRoutes`
|
|
1009
920
|
* (load-custom-routes.ts) with one deliberate divergence noted below.
|
|
1010
921
|
*
|
|
1011
922
|
* For each rule:
|
|
@@ -1048,7 +959,8 @@ function applyLocaleToRoutes(routes, i18n, type, options = {}) {
|
|
|
1048
959
|
out.push(r);
|
|
1049
960
|
continue;
|
|
1050
961
|
}
|
|
1051
|
-
const
|
|
962
|
+
const destination = "destination" in r ? r.destination : void 0;
|
|
963
|
+
const isExternal = !!destination && !destination.startsWith("/");
|
|
1052
964
|
if (!isExternal) for (const locale of defaultLocales) {
|
|
1053
965
|
const localizedSource = `/${locale}${suffixFor(r.source)}`;
|
|
1054
966
|
out.push({
|
|
@@ -1057,13 +969,14 @@ function applyLocaleToRoutes(routes, i18n, type, options = {}) {
|
|
|
1057
969
|
});
|
|
1058
970
|
}
|
|
1059
971
|
const internalSource = `${internalLocale}${suffixFor(r.source)}`;
|
|
1060
|
-
let internalDestination =
|
|
972
|
+
let internalDestination = destination;
|
|
1061
973
|
if (internalDestination && internalDestination.startsWith("/") && !isExternal) internalDestination = `/:nextInternalLocale${internalDestination === "/" && !trailingSlash ? "" : internalDestination}`;
|
|
1062
|
-
|
|
974
|
+
const internalRoute = {
|
|
1063
975
|
...r,
|
|
1064
|
-
source: internalSource
|
|
1065
|
-
|
|
1066
|
-
|
|
976
|
+
source: internalSource
|
|
977
|
+
};
|
|
978
|
+
if ("destination" in internalRoute && internalDestination !== void 0) internalRoute.destination = internalDestination;
|
|
979
|
+
out.push(internalRoute);
|
|
1067
980
|
out.push(r);
|
|
1068
981
|
}
|
|
1069
982
|
return out;
|
|
@@ -54,7 +54,8 @@ type NextHeader = {
|
|
|
54
54
|
key: string;
|
|
55
55
|
value: string;
|
|
56
56
|
}>; /** See {@link NextRedirect.basePath}. */
|
|
57
|
-
basePath?: false;
|
|
57
|
+
basePath?: false; /** See {@link NextRedirect.locale}. */
|
|
58
|
+
locale?: false;
|
|
58
59
|
};
|
|
59
60
|
type NextI18nConfig = {
|
|
60
61
|
/** List of supported locales */locales: string[]; /** The default locale (used when no locale prefix is in the URL) */
|
|
@@ -71,7 +72,7 @@ type NextI18nConfig = {
|
|
|
71
72
|
domain: string;
|
|
72
73
|
defaultLocale: string;
|
|
73
74
|
locales?: string[];
|
|
74
|
-
http?:
|
|
75
|
+
http?: true;
|
|
75
76
|
}>;
|
|
76
77
|
};
|
|
77
78
|
/**
|
|
@@ -99,7 +100,11 @@ type NextConfig = {
|
|
|
99
100
|
* @see https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix
|
|
100
101
|
*/
|
|
101
102
|
assetPrefix?: string; /** Whether to add trailing slashes */
|
|
102
|
-
trailingSlash?: boolean; /**
|
|
103
|
+
trailingSlash?: boolean; /** TypeScript build settings. */
|
|
104
|
+
typescript?: {
|
|
105
|
+
/** Project-relative path to the TypeScript configuration file. */tsconfigPath?: string;
|
|
106
|
+
[key: string]: unknown;
|
|
107
|
+
}; /** Internationalization routing config */
|
|
103
108
|
i18n?: NextI18nConfig; /** URL redirect rules */
|
|
104
109
|
redirects?: () => Promise<NextRedirect[]> | NextRedirect[]; /** URL rewrite rules */
|
|
105
110
|
rewrites?: () => Promise<NextRewrite[] | {
|
|
@@ -216,7 +221,8 @@ type NextConfig = {
|
|
|
216
221
|
prefetchInlining?: boolean | {
|
|
217
222
|
maxBundleSize?: number;
|
|
218
223
|
maxSize?: number;
|
|
219
|
-
};
|
|
224
|
+
}; /** Header names forwarded by Pages Router `res.revalidate()` internal requests. */
|
|
225
|
+
allowedRevalidateHeaderKeys?: string[];
|
|
220
226
|
[key: string]: unknown;
|
|
221
227
|
};
|
|
222
228
|
/**
|
|
@@ -265,6 +271,9 @@ type ResolvedNextConfig = {
|
|
|
265
271
|
*/
|
|
266
272
|
assetPrefix: string;
|
|
267
273
|
trailingSlash: boolean;
|
|
274
|
+
typescript: {
|
|
275
|
+
tsconfigPath?: string;
|
|
276
|
+
};
|
|
268
277
|
output: "" | "export" | "standalone";
|
|
269
278
|
pageExtensions: string[];
|
|
270
279
|
resolveExtensions: string[] | null;
|
|
@@ -294,7 +303,8 @@ type ResolvedNextConfig = {
|
|
|
294
303
|
mdx: MdxOptions | null; /** Explicit module aliases preserved from wrapped next.config plugins. */
|
|
295
304
|
aliases: Record<string, string>; /** Extra allowed origins for dev server access (from allowedDevOrigins). */
|
|
296
305
|
allowedDevOrigins: string[]; /** Extra allowed origins for server action CSRF validation (from experimental.serverActions.allowedOrigins). */
|
|
297
|
-
serverActionsAllowedOrigins: string[]; /**
|
|
306
|
+
serverActionsAllowedOrigins: string[]; /** Header names forwarded by Pages Router `res.revalidate()` internal requests. */
|
|
307
|
+
allowedRevalidateHeaderKeys: string[]; /** Packages whose barrel imports should be optimized (from experimental.optimizePackageImports). */
|
|
298
308
|
optimizePackageImports: string[]; /** Packages explicitly requested for server/client transpilation. */
|
|
299
309
|
transpilePackages: string[]; /** Packages treated as application code by Turbopack's foreign-code condition. */
|
|
300
310
|
turbopackTranspilePackages: string[]; /** Inline app CSS into production HTML (from experimental.inlineCss). */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
import { normalizePageExtensions } from "../routing/file-matcher.js";
|
|
3
|
-
import {
|
|
3
|
+
import { isExternalUrl } from "../utils/external-url.js";
|
|
4
|
+
import { applyLocaleToRoutes } from "./config-matchers.js";
|
|
4
5
|
import { isUnknownRecord } from "../utils/record.js";
|
|
5
6
|
import { getHtmlLimitedBotRegex } from "../utils/html-limited-bots.js";
|
|
6
7
|
import { flattenPluginOptions } from "../utils/plugin-options.js";
|
|
@@ -657,6 +658,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
|
|
|
657
658
|
basePath: "",
|
|
658
659
|
assetPrefix: "",
|
|
659
660
|
trailingSlash: false,
|
|
661
|
+
typescript: {},
|
|
660
662
|
output: "",
|
|
661
663
|
pageExtensions: normalizePageExtensions(),
|
|
662
664
|
resolveExtensions: null,
|
|
@@ -678,6 +680,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
|
|
|
678
680
|
aliases: {},
|
|
679
681
|
allowedDevOrigins: [],
|
|
680
682
|
serverActionsAllowedOrigins: [],
|
|
683
|
+
allowedRevalidateHeaderKeys: [],
|
|
681
684
|
optimizePackageImports: [],
|
|
682
685
|
transpilePackages: [],
|
|
683
686
|
turbopackTranspilePackages: [...DEFAULT_TRANSPILED_PACKAGES],
|
|
@@ -815,6 +818,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
|
|
|
815
818
|
afterFiles: applyLocaleToRoutes(rewrites.afterFiles, i18n, "rewrite", opts),
|
|
816
819
|
fallback: applyLocaleToRoutes(rewrites.fallback, i18n, "rewrite", opts)
|
|
817
820
|
};
|
|
821
|
+
headers = applyLocaleToRoutes(headers, i18n, "header", opts);
|
|
818
822
|
}
|
|
819
823
|
const images = config.images ? {
|
|
820
824
|
...config.images,
|
|
@@ -831,6 +835,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
|
|
|
831
835
|
basePath: config.basePath ?? "",
|
|
832
836
|
assetPrefix: normalizeAssetPrefix(config.assetPrefix),
|
|
833
837
|
trailingSlash: config.trailingSlash ?? false,
|
|
838
|
+
typescript: typeof config.typescript?.tsconfigPath === "string" ? { tsconfigPath: config.typescript.tsconfigPath } : {},
|
|
834
839
|
output: output === "export" || output === "standalone" ? output : "",
|
|
835
840
|
pageExtensions,
|
|
836
841
|
resolveExtensions: resolveExtensions ?? webpackProbe.resolveExtensions,
|
|
@@ -849,6 +854,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
|
|
|
849
854
|
aliases,
|
|
850
855
|
allowedDevOrigins,
|
|
851
856
|
serverActionsAllowedOrigins,
|
|
857
|
+
allowedRevalidateHeaderKeys: Array.isArray(experimental?.allowedRevalidateHeaderKeys) ? experimental.allowedRevalidateHeaderKeys.filter((value) => typeof value === "string").map((value) => value.toLowerCase()) : [],
|
|
852
858
|
optimizePackageImports,
|
|
853
859
|
transpilePackages,
|
|
854
860
|
turbopackTranspilePackages,
|