vinext 1.0.0-beta.0 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/dist/build/assets-ignore.js +1 -1
- package/dist/build/clean-output.js +1 -1
- package/dist/build/client-build-config.js +6 -4
- package/dist/build/css-url-assets.js +3 -3
- package/dist/build/google-fonts/fallback-metrics.js +1 -1
- package/dist/build/google-fonts/font-metadata.js +1 -1
- package/dist/build/inject-pregenerated-paths.js +1 -1
- package/dist/build/inline-css.js +2 -5
- package/dist/build/module-dependency-cache.d.ts +4 -0
- package/dist/build/module-dependency-cache.js +13 -0
- package/dist/build/next-client-runtime-manifests.js +1 -1
- package/dist/build/pages-client-assets-module.js +1 -1
- package/dist/build/precompress.js +7 -7
- package/dist/build/prerender-paths.d.ts +2 -2
- package/dist/build/prerender-paths.js +10 -15
- package/dist/build/prerender-server-pool.js +2 -2
- package/dist/build/prerender.js +1 -1
- package/dist/build/preview-credentials.d.ts +11 -0
- package/dist/build/preview-credentials.js +19 -0
- package/dist/build/report.d.ts +0 -3
- package/dist/build/report.js +2 -4
- package/dist/build/run-prerender.d.ts +2 -11
- package/dist/build/run-prerender.js +2 -10
- package/dist/build/server-manifest.js +1 -1
- package/dist/build/ssr-manifest.d.ts +4 -0
- package/dist/build/ssr-manifest.js +10 -6
- package/dist/build/standalone.js +37 -37
- package/dist/cache/cache-adapters-virtual.d.ts +1 -17
- package/dist/cache/cache-adapters-virtual.js +20 -11
- package/dist/check.d.ts +0 -16
- package/dist/check.js +12 -34
- package/dist/cli.js +58 -53
- package/dist/client/instrumentation-client-inject.js +1 -1
- package/dist/client/pages-router-link-navigation.js +1 -1
- package/dist/config/config-matchers.js +2 -2
- package/dist/config/dotenv.js +1 -1
- package/dist/config/next-config.d.ts +4 -1
- package/dist/config/next-config.js +81 -14
- package/dist/config/prerender.d.ts +2 -2
- package/dist/config/prerender.js +7 -15
- package/dist/config/server-external-packages.js +1 -1
- package/dist/config/tsconfig-paths.js +2 -2
- package/dist/{node_modules/.pnpm/am-i-vibing@0.5.0/node_modules → deps/.pnpm/am-i-vibing@0.5.0/deps}/am-i-vibing/dist/detector-1yx2Hoe0.js +2 -2
- package/dist/deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js +50 -0
- package/dist/entries/app-rsc-entry.js +12 -5
- package/dist/entries/app-rsc-manifest.js +4 -4
- package/dist/entries/pages-client-entry.js +6 -5
- package/dist/entries/pages-server-entry.js +15 -26
- package/dist/entries/runtime-entry-module.js +2 -2
- package/dist/index.js +255 -195
- package/dist/init-cloudflare.js +1 -1
- package/dist/init-platform.js +1 -1
- package/dist/init.js +5 -5
- package/dist/plugins/ast-utils.js +1 -1
- package/dist/plugins/dynamic-preload-metadata.js +7 -7
- package/dist/plugins/extensionless-dynamic-import.js +1 -1
- package/dist/plugins/fonts.js +3 -3
- package/dist/plugins/ignore-dynamic-requests.js +13 -8
- package/dist/plugins/import-meta-url.js +16 -17
- package/dist/plugins/middleware-export-validation.js +1 -1
- package/dist/plugins/middleware-server-only.js +2 -2
- package/dist/plugins/og-asset-ownership.js +7 -6
- package/dist/plugins/og-assets.js +4 -5
- package/dist/plugins/optimize-imports.js +12 -13
- package/dist/plugins/postcss.js +1 -1
- package/dist/plugins/remove-console.js +1 -1
- package/dist/plugins/require-context.js +1 -1
- package/dist/plugins/sass.js +2 -2
- package/dist/plugins/server-externals-manifest.js +1 -1
- package/dist/plugins/strip-server-exports.js +1 -1
- package/dist/plugins/styled-jsx.js +3 -2
- package/dist/plugins/typeof-window.js +1 -1
- package/dist/routing/app-route-graph.d.ts +3 -10
- package/dist/routing/app-route-graph.js +28 -76
- package/dist/routing/app-router.d.ts +0 -5
- package/dist/routing/app-router.js +0 -5
- package/dist/routing/file-matcher.d.ts +2 -6
- package/dist/routing/file-matcher.js +10 -11
- package/dist/routing/pages-router.js +3 -3
- package/dist/server/api-handler.js +41 -33
- package/dist/server/app-browser-entry.js +17 -11
- package/dist/server/app-browser-navigation-controller.js +5 -3
- package/dist/server/app-browser-server-action-client.js +1 -1
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-elements-wire.d.ts +2 -1
- package/dist/server/app-fallback-renderer.d.ts +8 -0
- package/dist/server/app-fallback-renderer.js +13 -1
- package/dist/server/app-page-boundary-render.d.ts +14 -1
- package/dist/server/app-page-boundary-render.js +107 -54
- package/dist/server/app-page-dispatch.d.ts +1 -0
- package/dist/server/app-page-dispatch.js +26 -37
- package/dist/server/app-page-element-builder.d.ts +2 -1
- package/dist/server/app-page-element-builder.js +3 -3
- package/dist/server/app-page-execution.d.ts +3 -1
- package/dist/server/app-page-execution.js +31 -39
- package/dist/server/app-page-request.js +1 -1
- package/dist/server/app-page-route-wiring.js +1 -1
- package/dist/server/app-route-handler-execution.js +1 -1
- package/dist/server/app-route-tree-prefetch.js +1 -1
- package/dist/server/app-rsc-cache-busting.js +1 -1
- package/dist/server/app-rsc-handler.js +3 -3
- package/dist/server/app-rsc-redirect-flight.d.ts +66 -0
- package/dist/server/app-rsc-redirect-flight.js +71 -0
- package/dist/server/app-segment-config.js +2 -2
- package/dist/server/app-server-action-execution.js +1 -1
- package/dist/server/app-ssr-entry.js +6 -3
- package/dist/server/app-ssr-stream.d.ts +2 -1
- package/dist/server/app-ssr-stream.js +4 -1
- package/dist/server/cache-proof.js +1 -1
- package/dist/server/default-global-error-module.d.ts +3 -1
- package/dist/server/default-not-found-module.d.ts +3 -1
- package/dist/server/dev-lockfile.js +2 -3
- package/dist/server/dev-route-files.js +1 -1
- package/dist/server/dev-server.js +253 -161
- package/dist/server/dev-stack-sourcemap.js +4 -3
- package/dist/server/headers.d.ts +3 -1
- package/dist/server/headers.js +3 -1
- package/dist/server/image-optimization.js +2 -2
- package/dist/server/implicit-tags.js +2 -1
- package/dist/server/instrumentation.js +2 -2
- package/dist/server/isr-cache.js +1 -1
- package/dist/server/metadata-routes.js +4 -4
- package/dist/server/middleware-response-headers.js +1 -1
- package/dist/server/middleware-runtime.js +1 -1
- package/dist/server/middleware.d.ts +1 -0
- package/dist/server/middleware.js +1 -1
- package/dist/server/navigation-planner.d.ts +2 -0
- package/dist/server/navigation-planner.js +8 -2
- package/dist/server/next-error-digest.d.ts +9 -24
- package/dist/server/next-error-digest.js +23 -15
- package/dist/server/operation-token.js +1 -1
- package/dist/server/pages-body-parser-config.js +2 -1
- package/dist/server/pages-dev-hydration.d.ts +14 -0
- package/dist/server/pages-dev-hydration.js +83 -0
- package/dist/server/pages-dev-module-url.js +2 -2
- package/dist/server/pages-node-compat.d.ts +13 -6
- package/dist/server/pages-node-compat.js +44 -45
- package/dist/server/pages-page-data.d.ts +1 -1
- package/dist/server/pages-page-data.js +4 -3
- package/dist/server/pages-page-handler.js +61 -29
- package/dist/server/pages-page-response.d.ts +2 -2
- package/dist/server/pages-preview.d.ts +26 -0
- package/dist/server/pages-preview.js +165 -0
- package/dist/server/pages-readiness.d.ts +2 -1
- package/dist/server/pages-readiness.js +4 -2
- package/dist/server/prod-server.js +9 -8
- package/dist/server/request-pipeline.js +1 -1
- package/dist/server/seed-cache.js +1 -1
- package/dist/server/socket-error-backstop.js +1 -1
- package/dist/server/static-file-cache.js +7 -8
- package/dist/server/worker-utils.js +1 -1
- package/dist/shims/cache.js +2 -1
- package/dist/shims/constants.js +1 -1
- package/dist/shims/error-boundary.d.ts +4 -4
- package/dist/shims/error.d.ts +21 -5
- package/dist/shims/error.js +52 -14
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/font-utils.js +1 -1
- package/dist/shims/form.d.ts +1 -1
- package/dist/shims/head.js +3 -3
- package/dist/shims/headers.js +6 -4
- package/dist/shims/internal/pages-data-fetch-dedup.js +5 -3
- package/dist/shims/layout-segment-context.d.ts +1 -1
- package/dist/shims/link.d.ts +3 -1
- package/dist/shims/link.js +6 -3
- package/dist/shims/navigation-errors.d.ts +2 -1
- package/dist/shims/navigation-errors.js +9 -13
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.js +5 -5
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router.d.ts +2 -1
- package/dist/shims/router.js +18 -11
- package/dist/shims/script.js +1 -1
- package/dist/shims/server.js +2 -2
- package/dist/shims/slot.d.ts +1 -1
- package/dist/shims/thenable-params.js +1 -1
- package/dist/typegen.js +9 -11
- package/dist/utils/client-build-manifest.js +2 -2
- package/dist/utils/client-entry-manifest.js +1 -1
- package/dist/utils/client-runtime-metadata.js +1 -1
- package/dist/utils/commonjs-loader.js +3 -3
- package/dist/utils/mdx-scan.js +1 -1
- package/dist/utils/path.d.ts +1 -13
- package/dist/utils/path.js +1 -15
- package/dist/utils/plugin-options.d.ts +4 -0
- package/dist/utils/plugin-options.js +8 -0
- package/dist/utils/project.d.ts +0 -6
- package/dist/utils/project.js +2 -8
- package/dist/utils/public-routes.js +2 -2
- package/dist/utils/react-version.js +1 -1
- package/dist/utils/redirect-digest.d.ts +9 -0
- package/dist/utils/redirect-digest.js +25 -0
- package/dist/utils/vinext-root.js +1 -1
- package/dist/utils/vite-version.d.ts +5 -2
- package/dist/utils/vite-version.js +43 -17
- package/package.json +4 -3
- /package/dist/build/google-fonts/{fallback-metrics-data.js → fallback-metrics-data.json.js} +0 -0
- /package/dist/build/google-fonts/{font-data.js → font-data.json.js} +0 -0
- /package/dist/{node_modules/.pnpm/process-ancestry@0.1.0/node_modules → deps/.pnpm/process-ancestry@0.1.0/deps}/process-ancestry/dist/index.js +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
import { decodeRouteSegment, isInvisibleSegment, sortRoutes } from "./utils.js";
|
|
3
3
|
import { findFileWithExts, scanWithExtensions } from "./file-matcher.js";
|
|
4
4
|
import { validateRoutePatterns } from "./route-validation.js";
|
|
5
5
|
import { compareStrings } from "../utils/compare.js";
|
|
6
6
|
import fs from "node:fs";
|
|
7
|
-
import path from "node:path";
|
|
8
7
|
import { createHash } from "node:crypto";
|
|
9
8
|
//#region src/routing/app-route-graph.ts
|
|
10
9
|
/**
|
|
@@ -363,10 +362,6 @@ function createRouteManifestGraphVersion(segmentGraph) {
|
|
|
363
362
|
}
|
|
364
363
|
/**
|
|
365
364
|
* Build the App Router route graph by scanning `appDir`.
|
|
366
|
-
*
|
|
367
|
-
* `appDir` must be forward-slash. Every path in the graph is derived from it
|
|
368
|
-
* with `path.posix.*` and `findFile`, so a native appDir would produce mixed
|
|
369
|
-
* separators on Windows. Production callers normalize it at their entry.
|
|
370
365
|
*/
|
|
371
366
|
async function buildAppRouteGraph(appDir, matcher) {
|
|
372
367
|
const routes = [];
|
|
@@ -384,8 +379,8 @@ async function buildAppRouteGraph(appDir, matcher) {
|
|
|
384
379
|
const routePatterns = new Set(routes.map((route) => route.pattern));
|
|
385
380
|
const ghostParentRoutes = [];
|
|
386
381
|
for await (const file of scanWithExtensions("**/layout", appDir, scanMatcher.extensions, excludeDir)) {
|
|
387
|
-
const dir = path.
|
|
388
|
-
const routeDir = dir === "." ? appDir : path.
|
|
382
|
+
const dir = path.dirname(file);
|
|
383
|
+
const routeDir = dir === "." ? appDir : path.join(appDir, dir);
|
|
389
384
|
if (!hasParallelSlotDirectory(routeDir)) continue;
|
|
390
385
|
if (discoverParallelSlots(routeDir, appDir, scanMatcher, true).length === 0) continue;
|
|
391
386
|
const route = directoryToAppRoute(dir, appDir, scanMatcher, null, null, true);
|
|
@@ -440,9 +435,9 @@ function validatePageRouteConflicts(routes, appDir) {
|
|
|
440
435
|
}
|
|
441
436
|
}
|
|
442
437
|
function formatAppFilePath(filePath, appDir) {
|
|
443
|
-
const relativePath =
|
|
438
|
+
const relativePath = path.relative(appDir, filePath);
|
|
444
439
|
const parsedPath = path.parse(relativePath);
|
|
445
|
-
const withoutExtension =
|
|
440
|
+
const withoutExtension = path.join(parsedPath.dir, parsedPath.name);
|
|
446
441
|
return withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`;
|
|
447
442
|
}
|
|
448
443
|
/**
|
|
@@ -704,27 +699,15 @@ function findCatchAllPage(dir, matcher) {
|
|
|
704
699
|
}
|
|
705
700
|
/**
|
|
706
701
|
* Convert a file path relative to app/ into an AppRoute.
|
|
707
|
-
*
|
|
708
|
-
* `file` and `appDir` must be forward-slash. `file` comes from
|
|
709
|
-
* `scanWithExtensions` (already forward-slash) and is joined onto `appDir` with
|
|
710
|
-
* `path.posix.join` to form the page/route path, so a native input would
|
|
711
|
-
* produce a mixed separator on Windows.
|
|
712
702
|
*/
|
|
713
703
|
function fileToAppRoute(file, appDir, type, matcher) {
|
|
714
|
-
let dir = path.
|
|
715
|
-
if (type === "page" && dir !== "." && path.
|
|
716
|
-
const parent = path.
|
|
704
|
+
let dir = path.dirname(file);
|
|
705
|
+
if (type === "page" && dir !== "." && path.basename(dir) === "@children") {
|
|
706
|
+
const parent = path.dirname(dir);
|
|
717
707
|
dir = parent === "" || parent === "." ? "." : parent;
|
|
718
708
|
}
|
|
719
|
-
return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.
|
|
709
|
+
return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.join(appDir, file) : null, type === "route" ? path.join(appDir, file) : null);
|
|
720
710
|
}
|
|
721
|
-
/**
|
|
722
|
-
* `dir`, `appDir`, `pagePath`, and `routePath` must all be forward-slash. `dir`
|
|
723
|
-
* is split on `path.posix.sep` and joined onto `appDir` with `path.posix.join`.
|
|
724
|
-
* `appDir` is threaded to the layout/slot/boundary discovery below, which builds
|
|
725
|
-
* paths the same way. `pagePath` and `routePath` are stored on the route node as
|
|
726
|
-
* canonical ids that get compared and re-joined downstream.
|
|
727
|
-
*/
|
|
728
711
|
function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeNestedOnlySlots = false) {
|
|
729
712
|
const segments = dir === "." ? [] : dir.split("/");
|
|
730
713
|
const params = [];
|
|
@@ -743,7 +726,7 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
743
726
|
const errorEntries = discoverSegmentErrors(segments, appDir, matcher);
|
|
744
727
|
const errorPaths = errorEntries.map((entry) => entry.path);
|
|
745
728
|
const errorTreePositions = errorEntries.map((entry) => entry.treePosition);
|
|
746
|
-
const routeDir = dir === "." ? appDir : path.
|
|
729
|
+
const routeDir = dir === "." ? appDir : path.join(appDir, dir);
|
|
747
730
|
const effectivePagePath = pagePath ?? (routePath ? null : findFile(routeDir, "default", matcher));
|
|
748
731
|
const loadingPath = findFile(routeDir, "loading", matcher);
|
|
749
732
|
const errorPath = findFile(routeDir, "error", matcher);
|
|
@@ -850,8 +833,9 @@ function convertTreePathToRouteParts(treePath) {
|
|
|
850
833
|
function computeLayoutTreePositions(appDir, layouts) {
|
|
851
834
|
return layouts.map((layoutPath) => {
|
|
852
835
|
const layoutDir = path.dirname(layoutPath);
|
|
853
|
-
|
|
854
|
-
|
|
836
|
+
const relative = path.relative(appDir, layoutDir);
|
|
837
|
+
if (relative === "") return 0;
|
|
838
|
+
return relative.split(path.sep).length;
|
|
855
839
|
});
|
|
856
840
|
}
|
|
857
841
|
/**
|
|
@@ -976,10 +960,6 @@ function discoverBoundaryFilePerLayout(layouts, fileName, matcher) {
|
|
|
976
960
|
* Walk from appDir through each segment to the route's directory. At each level
|
|
977
961
|
* that has @slot dirs, collect them. Slots at the route's own directory level
|
|
978
962
|
* use page.tsx; slots at ancestor levels use default.tsx only.
|
|
979
|
-
*
|
|
980
|
-
* `appDir` and `routeDir` must be forward-slash — `currentDir` descends from
|
|
981
|
-
* `appDir` via `path.posix.join`, and the `dir === routeDir` active-level test
|
|
982
|
-
* below only matches when both share the canonical separator.
|
|
983
963
|
*/
|
|
984
964
|
function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, includeNestedOnlySlots = false) {
|
|
985
965
|
const slotMap = /* @__PURE__ */ new Map();
|
|
@@ -992,7 +972,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
|
|
|
992
972
|
segmentIndex: 0
|
|
993
973
|
});
|
|
994
974
|
for (let i = 0; i < segments.length; i++) {
|
|
995
|
-
currentDir = path.
|
|
975
|
+
currentDir = path.join(currentDir, segments[i]);
|
|
996
976
|
if (findFile(currentDir, "layout", matcher)) layoutIdx++;
|
|
997
977
|
dirsToCheck.push({
|
|
998
978
|
dir: currentDir,
|
|
@@ -1155,9 +1135,6 @@ function patternsStructurallyEquivalent(a, b) {
|
|
|
1155
1135
|
*
|
|
1156
1136
|
* Returns the absolute page path, or null if no root-level page is found.
|
|
1157
1137
|
*
|
|
1158
|
-
* `slotDir` must be forward-slash: the `path.posix.join` descent stays a
|
|
1159
|
-
* canonical id only when the base already is.
|
|
1160
|
-
*
|
|
1161
1138
|
* Only descends into route-group directories (those whose name starts with `(`
|
|
1162
1139
|
* and ends with `)`). Dynamic segments, regular named dirs, and `@slot` dirs
|
|
1163
1140
|
* are not transparent and are therefore not searched.
|
|
@@ -1169,7 +1146,7 @@ function findSlotRootPage(slotDir, matcher) {
|
|
|
1169
1146
|
for (const entry of entries) {
|
|
1170
1147
|
if (!entry.isDirectory()) continue;
|
|
1171
1148
|
if (!entry.name.startsWith("(") || !entry.name.endsWith(")")) continue;
|
|
1172
|
-
const found = findSlotRootPage(path.
|
|
1149
|
+
const found = findSlotRootPage(path.join(slotDir, entry.name), matcher);
|
|
1173
1150
|
if (found) return found;
|
|
1174
1151
|
}
|
|
1175
1152
|
return null;
|
|
@@ -1178,9 +1155,6 @@ function findSlotRootPage(slotDir, matcher) {
|
|
|
1178
1155
|
* Discover parallel route slots (@team, @analytics, etc.) in a directory.
|
|
1179
1156
|
* Returns a ParallelSlot for each @-prefixed subdirectory that has a page,
|
|
1180
1157
|
* default component, intercepting route, or nested page-backed sub-route.
|
|
1181
|
-
*
|
|
1182
|
-
* `dir` and `appDir` must be forward-slash. The slot directory is built from
|
|
1183
|
-
* `dir` with `path.posix.join`.
|
|
1184
1158
|
*/
|
|
1185
1159
|
function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = false) {
|
|
1186
1160
|
const entries = readDirEntriesCached(dir, matcher);
|
|
@@ -1189,7 +1163,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1189
1163
|
if (!entry.isDirectory() || !entry.name.startsWith("@")) continue;
|
|
1190
1164
|
if (entry.name === "@children") continue;
|
|
1191
1165
|
const slotName = entry.name.slice(1);
|
|
1192
|
-
const slotDir = path.
|
|
1166
|
+
const slotDir = path.join(dir, entry.name);
|
|
1193
1167
|
const pagePath = findSlotRootPage(slotDir, matcher);
|
|
1194
1168
|
const defaultPath = findFile(slotDir, "default", matcher);
|
|
1195
1169
|
const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
|
|
@@ -1200,7 +1174,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1200
1174
|
const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
|
|
1201
1175
|
slots.push({
|
|
1202
1176
|
id: createAppRouteGraphSlotId(slotName, ownerTreePath),
|
|
1203
|
-
key: `${slotName}@${
|
|
1177
|
+
key: `${slotName}@${path.relative(appDir, slotDir)}`,
|
|
1204
1178
|
name: slotName,
|
|
1205
1179
|
ownerDir: slotDir,
|
|
1206
1180
|
ownerTreePath,
|
|
@@ -1259,9 +1233,6 @@ function isInterceptionMarkerDir(name) {
|
|
|
1259
1233
|
* Intercepting routes use conventions like (.)photo, (..)feed, (...), etc.
|
|
1260
1234
|
* They intercept navigation to another route and render within the slot instead.
|
|
1261
1235
|
*
|
|
1262
|
-
* `slotDir`, `routeDir`, and `appDir` must be forward-slash. They are passed
|
|
1263
|
-
* down to `path.posix.join` when building the intercept page paths.
|
|
1264
|
-
*
|
|
1265
1236
|
* @param slotDir - The parallel slot directory (e.g. app/feed/@modal)
|
|
1266
1237
|
* @param routeDir - The directory of the route that owns this slot (e.g. app/feed)
|
|
1267
1238
|
* @param appDir - The root app directory
|
|
@@ -1280,17 +1251,13 @@ function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
|
|
|
1280
1251
|
* Sibling intercepts use the same conventions and target-computation logic as
|
|
1281
1252
|
* slot intercepts, but their intercepting page replaces the full page response
|
|
1282
1253
|
* (not a slot) during soft navigation.
|
|
1283
|
-
*
|
|
1284
|
-
* `appDir` and each route's `pagePath` / `routePath` must be forward-slash. The
|
|
1285
|
-
* owner directories are derived from `pagePath` / `routePath` and matched
|
|
1286
|
-
* against `appDir`-relative paths built with `path.posix.*`.
|
|
1287
1254
|
*/
|
|
1288
1255
|
function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
1289
1256
|
const routesByDir = /* @__PURE__ */ new Map();
|
|
1290
1257
|
for (const route of routes) {
|
|
1291
1258
|
const filePath = route.pagePath ?? route.routePath;
|
|
1292
1259
|
if (!filePath) continue;
|
|
1293
|
-
const routeDir = path.
|
|
1260
|
+
const routeDir = path.dirname(filePath);
|
|
1294
1261
|
if (!routesByDir.has(routeDir)) routesByDir.set(routeDir, route);
|
|
1295
1262
|
}
|
|
1296
1263
|
function walk(dir) {
|
|
@@ -1304,7 +1271,7 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1304
1271
|
if (!entry.isDirectory()) continue;
|
|
1305
1272
|
if (entry.name.startsWith("_")) continue;
|
|
1306
1273
|
if (entry.name.startsWith("@")) continue;
|
|
1307
|
-
const childDir = path.
|
|
1274
|
+
const childDir = path.join(dir, entry.name);
|
|
1308
1275
|
const marker = matchInterceptConvention(entry.name);
|
|
1309
1276
|
if (marker) {
|
|
1310
1277
|
const restOfName = entry.name.slice(marker.prefix.length);
|
|
@@ -1335,18 +1302,15 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1335
1302
|
* no sibling pages at all (e.g. `deep/path/(...)target` with no
|
|
1336
1303
|
* `deep/path/page.tsx`).
|
|
1337
1304
|
*
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
* backslashes on Windows. Without normalizing, the `current === appDir`
|
|
1342
|
-
* termination never fires there and the walk overshoots the app root.
|
|
1343
|
-
* `routesByDir` keys must be forward-slash dirnames of the route file paths.
|
|
1305
|
+
* Comparisons happen in forward-slash space — inputs go through `toSlash` so
|
|
1306
|
+
* callers still holding native-separator paths hit the `current === appDir`
|
|
1307
|
+
* termination instead of overshooting the app root.
|
|
1344
1308
|
*
|
|
1345
1309
|
* Exported for tests.
|
|
1346
1310
|
*/
|
|
1347
1311
|
function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
1348
|
-
const appRoot =
|
|
1349
|
-
let current =
|
|
1312
|
+
const appRoot = toSlash(appDir);
|
|
1313
|
+
let current = toSlash(dir);
|
|
1350
1314
|
while (true) {
|
|
1351
1315
|
const exact = routesByDir.get(current);
|
|
1352
1316
|
if (exact) return exact;
|
|
@@ -1355,7 +1319,7 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1355
1319
|
for (const route of routes) {
|
|
1356
1320
|
const filePath = route.pagePath ?? route.routePath;
|
|
1357
1321
|
if (!filePath) continue;
|
|
1358
|
-
if (!
|
|
1322
|
+
if (!toSlash(filePath).startsWith(currentWithSep)) continue;
|
|
1359
1323
|
if (!best || route.patternParts.length < best.patternParts.length) best = route;
|
|
1360
1324
|
}
|
|
1361
1325
|
if (best) return best;
|
|
@@ -1369,9 +1333,6 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1369
1333
|
/**
|
|
1370
1334
|
* Recursively scan a directory tree for page.tsx files that are inside
|
|
1371
1335
|
* intercepting route directories.
|
|
1372
|
-
*
|
|
1373
|
-
* `currentDir`, `routeDir`, and `appDir` must be forward-slash. `currentDir`
|
|
1374
|
-
* descends with `path.posix.join` when building the intercept page paths.
|
|
1375
1336
|
*/
|
|
1376
1337
|
function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
|
|
1377
1338
|
if (!fs.existsSync(currentDir)) return;
|
|
@@ -1380,7 +1341,7 @@ function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher
|
|
|
1380
1341
|
if (!entry.isDirectory()) continue;
|
|
1381
1342
|
if (entry.name.startsWith("_")) continue;
|
|
1382
1343
|
const interceptMatch = matchInterceptConvention(entry.name);
|
|
1383
|
-
const interceptDir = path.
|
|
1344
|
+
const interceptDir = path.join(currentDir, entry.name);
|
|
1384
1345
|
if (interceptMatch) {
|
|
1385
1346
|
const restOfName = entry.name.slice(interceptMatch.prefix.length);
|
|
1386
1347
|
collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, results, matcher);
|
|
@@ -1397,12 +1358,6 @@ function matchInterceptConvention(name) {
|
|
|
1397
1358
|
/**
|
|
1398
1359
|
* Collect page.tsx files inside an intercepting route directory tree
|
|
1399
1360
|
* and compute their target URL patterns.
|
|
1400
|
-
*
|
|
1401
|
-
* `currentDir`, `interceptRoot`, `routeDir`, `appDir`, and `interceptParentDir`
|
|
1402
|
-
* must all be forward-slash. `currentDir` descends with `path.posix.join` and
|
|
1403
|
-
* its `findFile` results become the stored layout/page paths. The others are
|
|
1404
|
-
* relativized against `appDir` (and each other) to derive the intercept page
|
|
1405
|
-
* segments and URL patterns.
|
|
1406
1361
|
*/
|
|
1407
1362
|
function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, parentLayoutPaths = []) {
|
|
1408
1363
|
const currentLayoutPath = findFile(currentDir, "layout", matcher);
|
|
@@ -1422,7 +1377,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1422
1377
|
targetPattern: targetPattern.pattern,
|
|
1423
1378
|
sourceMatchPattern,
|
|
1424
1379
|
pagePath: page,
|
|
1425
|
-
sourcePageSegments:
|
|
1380
|
+
sourcePageSegments: path.relative(appDir, path.dirname(page)).split("/").filter(Boolean),
|
|
1426
1381
|
params: targetPattern.params
|
|
1427
1382
|
});
|
|
1428
1383
|
}
|
|
@@ -1432,7 +1387,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1432
1387
|
for (const entry of entries) {
|
|
1433
1388
|
if (!entry.isDirectory()) continue;
|
|
1434
1389
|
if (entry.name.startsWith("_")) continue;
|
|
1435
|
-
collectInterceptingPages(path.
|
|
1390
|
+
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, layoutPaths);
|
|
1436
1391
|
}
|
|
1437
1392
|
}
|
|
1438
1393
|
/**
|
|
@@ -1552,9 +1507,6 @@ const findFileProbeCache = /* @__PURE__ */ new WeakMap();
|
|
|
1552
1507
|
* registered per-scan cache; otherwise falls back to a direct probe (identical
|
|
1553
1508
|
* result). The `null` "not found" outcome is cached too, so repeated misses on
|
|
1554
1509
|
* shared ancestors cost a single set of `existsSync` calls per scan.
|
|
1555
|
-
*
|
|
1556
|
-
* `dir` must be forward-slash. The returned path comes from `findFileWithExts`,
|
|
1557
|
-
* so it is forward-slash too.
|
|
1558
1510
|
*/
|
|
1559
1511
|
function findFile(dir, name, matcher) {
|
|
1560
1512
|
const cache = findFileProbeCache.get(matcher);
|
|
@@ -12,16 +12,11 @@ declare function invalidateAppRouteCache(): void;
|
|
|
12
12
|
* TODO(#726): Layer 4 should consume this read model directly once the
|
|
13
13
|
* navigation planner owns route graph facts.
|
|
14
14
|
*
|
|
15
|
-
* `appDir` must be forward-slash — callers normalize it at their entry, and it
|
|
16
|
-
* flows into `buildAppRouteGraph`, which builds every path with `path.posix.*`.
|
|
17
|
-
*
|
|
18
15
|
* @internal
|
|
19
16
|
*/
|
|
20
17
|
declare function appRouteGraph(appDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<AppRouteGraph>;
|
|
21
18
|
/**
|
|
22
19
|
* Scan the app/ directory and return a list of routes.
|
|
23
|
-
*
|
|
24
|
-
* `appDir` must be forward-slash — it is forwarded to `appRouteGraph`.
|
|
25
20
|
*/
|
|
26
21
|
declare function appRouter(appDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<AppRouteGraphRoute[]>;
|
|
27
22
|
/**
|
|
@@ -30,9 +30,6 @@ function invalidateAppRouteCache() {
|
|
|
30
30
|
* TODO(#726): Layer 4 should consume this read model directly once the
|
|
31
31
|
* navigation planner owns route graph facts.
|
|
32
32
|
*
|
|
33
|
-
* `appDir` must be forward-slash — callers normalize it at their entry, and it
|
|
34
|
-
* flows into `buildAppRouteGraph`, which builds every path with `path.posix.*`.
|
|
35
|
-
*
|
|
36
33
|
* @internal
|
|
37
34
|
*/
|
|
38
35
|
async function appRouteGraph(appDir, pageExtensions, matcher) {
|
|
@@ -47,8 +44,6 @@ async function appRouteGraph(appDir, pageExtensions, matcher) {
|
|
|
47
44
|
}
|
|
48
45
|
/**
|
|
49
46
|
* Scan the app/ directory and return a list of routes.
|
|
50
|
-
*
|
|
51
|
-
* `appDir` must be forward-slash — it is forwarded to `appRouteGraph`.
|
|
52
47
|
*/
|
|
53
48
|
async function appRouter(appDir, pageExtensions, matcher) {
|
|
54
49
|
return (await appRouteGraph(appDir, pageExtensions, matcher)).routes;
|
|
@@ -21,9 +21,6 @@ declare function findFileWithExtensions(basePath: string, matcher: ValidFileMatc
|
|
|
21
21
|
/**
|
|
22
22
|
* Find a file by basename and configured page extension in a directory.
|
|
23
23
|
* Returns the first matching absolute path, or null if not found.
|
|
24
|
-
*
|
|
25
|
-
* `dir` must be forward-slash. The returned path is built with `path.posix.join`,
|
|
26
|
-
* so it is forward-slash too.
|
|
27
24
|
*/
|
|
28
25
|
declare function findFileWithExts(dir: string, name: string, matcher: ValidFileMatcher): string | null;
|
|
29
26
|
/**
|
|
@@ -66,9 +63,8 @@ declare function normalizeViteResolveExtensions(extensions: readonly string[]):
|
|
|
66
63
|
* Use function-form exclude for Node < 22.14 compatibility.
|
|
67
64
|
*
|
|
68
65
|
* Yields forward-slash relative paths: node's glob emits native (backslash)
|
|
69
|
-
* separators on Windows, so each match
|
|
70
|
-
*
|
|
71
|
-
* forward-slash ids.
|
|
66
|
+
* separators on Windows, so each match goes through `toSlash` — this is the
|
|
67
|
+
* boundary where external fs output enters the canonical forward-slash space.
|
|
72
68
|
*/
|
|
73
69
|
declare function scanWithExtensions(stem: string, cwd: string, extensions: readonly string[], exclude?: (name: string) => boolean): AsyncGenerator<string>;
|
|
74
70
|
//#endregion
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
import { escapeRegExp } from "../utils/regex.js";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
4
|
import { glob } from "node:fs/promises";
|
|
6
5
|
//#region src/routing/file-matcher.ts
|
|
7
6
|
const DEFAULT_PAGE_EXTENSIONS = [
|
|
@@ -19,6 +18,10 @@ function buildExtensionGlob(stem, extensions) {
|
|
|
19
18
|
if (extensions.length === 1) return `${stem}.${extensions[0]}`;
|
|
20
19
|
return `${stem}.{${extensions.join(",")}}`;
|
|
21
20
|
}
|
|
21
|
+
function includeDotDirectoryMatches(pattern) {
|
|
22
|
+
if (!pattern.startsWith("**/")) return pattern;
|
|
23
|
+
return `{**,**/.*/**}/${pattern.slice(3)}`;
|
|
24
|
+
}
|
|
22
25
|
/**
|
|
23
26
|
* Ported in spirit from Next.js createValidFileMatcher:
|
|
24
27
|
* packages/next/src/server/lib/find-page-file.ts
|
|
@@ -67,13 +70,10 @@ function findFileWithExtensions(basePath, matcher) {
|
|
|
67
70
|
/**
|
|
68
71
|
* Find a file by basename and configured page extension in a directory.
|
|
69
72
|
* Returns the first matching absolute path, or null if not found.
|
|
70
|
-
*
|
|
71
|
-
* `dir` must be forward-slash. The returned path is built with `path.posix.join`,
|
|
72
|
-
* so it is forward-slash too.
|
|
73
73
|
*/
|
|
74
74
|
function findFileWithExts(dir, name, matcher) {
|
|
75
75
|
for (const ext of matcher.dottedExtensions) {
|
|
76
|
-
const filePath = path.
|
|
76
|
+
const filePath = path.join(dir, name + ext);
|
|
77
77
|
if (existsSync(filePath)) return filePath;
|
|
78
78
|
}
|
|
79
79
|
return null;
|
|
@@ -153,16 +153,15 @@ function normalizeViteResolveExtensions(extensions) {
|
|
|
153
153
|
* Use function-form exclude for Node < 22.14 compatibility.
|
|
154
154
|
*
|
|
155
155
|
* Yields forward-slash relative paths: node's glob emits native (backslash)
|
|
156
|
-
* separators on Windows, so each match
|
|
157
|
-
*
|
|
158
|
-
* forward-slash ids.
|
|
156
|
+
* separators on Windows, so each match goes through `toSlash` — this is the
|
|
157
|
+
* boundary where external fs output enters the canonical forward-slash space.
|
|
159
158
|
*/
|
|
160
159
|
async function* scanWithExtensions(stem, cwd, extensions, exclude) {
|
|
161
|
-
const pattern = buildExtensionGlob(stem, extensions);
|
|
160
|
+
const pattern = includeDotDirectoryMatches(buildExtensionGlob(stem, extensions));
|
|
162
161
|
for await (const file of glob(pattern, {
|
|
163
162
|
cwd,
|
|
164
163
|
...exclude ? { exclude } : {}
|
|
165
|
-
})) yield
|
|
164
|
+
})) yield toSlash(file);
|
|
166
165
|
}
|
|
167
166
|
//#endregion
|
|
168
167
|
export { buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, normalizeViteResolveExtensions, scanWithExtensions };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
1
2
|
import { decodeRouteSegment, sortRoutes } from "./utils.js";
|
|
2
3
|
import { createValidFileMatcher, scanWithExtensions } from "./file-matcher.js";
|
|
3
4
|
import { patternToNextFormat, validateRoutePatterns } from "./route-validation.js";
|
|
4
5
|
import { createRouteTrieCache, matchRouteWithTrie } from "./route-matching.js";
|
|
5
|
-
import path from "node:path";
|
|
6
6
|
//#region src/routing/pages-router.ts
|
|
7
7
|
/** Next.js special pages that should not produce routes. */
|
|
8
|
-
const RESERVED_PAGE_NAMES = new Set([
|
|
8
|
+
const RESERVED_PAGE_NAMES = /* @__PURE__ */ new Set([
|
|
9
9
|
"_app",
|
|
10
10
|
"_document",
|
|
11
11
|
"_error"
|
|
@@ -152,7 +152,7 @@ async function scanApiRoutes(pagesDir, matcher) {
|
|
|
152
152
|
}
|
|
153
153
|
const routes = [];
|
|
154
154
|
for (const file of files) {
|
|
155
|
-
const route = fileToRoute(path.
|
|
155
|
+
const route = fileToRoute(path.join("api", file), pagesDir, matcher);
|
|
156
156
|
if (route) routes.push(route);
|
|
157
157
|
}
|
|
158
158
|
validateRoutePatterns(routes.map((route) => route.pattern));
|
|
@@ -8,7 +8,7 @@ import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT, resolveBodyParserConfig } from "./pa
|
|
|
8
8
|
import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-media-type.js";
|
|
9
9
|
import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
|
|
10
10
|
import { performOnDemandRevalidate } from "./pages-revalidate.js";
|
|
11
|
-
import { attachPagesRequestCookies } from "./pages-node-compat.js";
|
|
11
|
+
import { attachPagesPreviewApi, attachPagesRequestCookies } from "./pages-node-compat.js";
|
|
12
12
|
import { isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
13
13
|
import { decode } from "node:querystring";
|
|
14
14
|
import { Buffer } from "node:buffer";
|
|
@@ -166,41 +166,49 @@ function enhanceApiObjects(req, res, query, body) {
|
|
|
166
166
|
query
|
|
167
167
|
});
|
|
168
168
|
attachPagesRequestCookies(apiReq);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
169
|
+
const apiRes = Object.assign(res, {
|
|
170
|
+
status(code) {
|
|
171
|
+
this.statusCode = code;
|
|
172
|
+
return this;
|
|
173
|
+
},
|
|
174
|
+
json(data) {
|
|
175
|
+
this.setHeader("Content-Type", "application/json");
|
|
176
|
+
this.end(JSON.stringify(data));
|
|
177
|
+
},
|
|
178
|
+
send(data) {
|
|
179
|
+
if (Buffer.isBuffer(data)) {
|
|
180
|
+
if (!this.getHeader("Content-Type")) this.setHeader("Content-Type", "application/octet-stream");
|
|
181
|
+
this.setHeader("Content-Length", String(data.length));
|
|
182
|
+
this.end(data);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (typeof data === "object" && data !== null) {
|
|
177
186
|
this.setHeader("Content-Type", "application/json");
|
|
178
187
|
this.end(JSON.stringify(data));
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
this.setHeader("Content-Type", "application/json");
|
|
189
|
-
this.end(JSON.stringify(data));
|
|
190
|
-
} else {
|
|
191
|
-
if (!this.getHeader("Content-Type")) this.setHeader("Content-Type", "text/plain");
|
|
192
|
-
this.end(String(data));
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
redirect(statusOrUrl, url) {
|
|
196
|
-
if (typeof statusOrUrl === "string") this.writeHead(307, { Location: statusOrUrl });
|
|
197
|
-
else this.writeHead(statusOrUrl, { Location: url ?? "" });
|
|
198
|
-
this.end();
|
|
199
|
-
},
|
|
200
|
-
async revalidate(urlPath, opts) {
|
|
201
|
-
await performOnDemandRevalidate(req, urlPath, opts);
|
|
188
|
+
} else {
|
|
189
|
+
if (!this.getHeader("Content-Type")) this.setHeader("Content-Type", "text/plain");
|
|
190
|
+
this.end(String(data));
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
redirect(statusOrUrl, url) {
|
|
194
|
+
if (typeof statusOrUrl === "string") {
|
|
195
|
+
url = statusOrUrl;
|
|
196
|
+
statusOrUrl = 307;
|
|
202
197
|
}
|
|
203
|
-
|
|
198
|
+
if (typeof statusOrUrl !== "number" || typeof url !== "string") throw new Error("Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').");
|
|
199
|
+
this.writeHead(statusOrUrl, { Location: url });
|
|
200
|
+
this.write(url);
|
|
201
|
+
this.end();
|
|
202
|
+
return this;
|
|
203
|
+
},
|
|
204
|
+
async revalidate(urlPath, opts) {
|
|
205
|
+
await performOnDemandRevalidate(req, urlPath, opts);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
attachPagesPreviewApi(apiReq, apiRes);
|
|
209
|
+
return {
|
|
210
|
+
apiReq,
|
|
211
|
+
apiRes
|
|
204
212
|
};
|
|
205
213
|
}
|
|
206
214
|
/**
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RSC_REDIRECT_HEADER } from "./headers.js";
|
|
1
|
+
import { VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER } from "./headers.js";
|
|
2
2
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
3
3
|
import { getMountedSlotIdsHeader, resolveVisitedResponseInterceptionContext } from "./app-elements.js";
|
|
4
4
|
import { AppRouterContext } from "../shims/internal/app-router-context.js";
|
|
@@ -16,9 +16,6 @@ import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscR
|
|
|
16
16
|
import { getNavigationRuntime, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
|
|
17
17
|
import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
|
|
18
18
|
import "../client/instrumentation-client.js";
|
|
19
|
-
import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap } from "./app-bfcache-identity.js";
|
|
20
|
-
import { createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, resolveServerActionOperationLane } from "./app-browser-action-result.js";
|
|
21
|
-
import { createClientReuseManifestHeaderFromVisibleAppState } from "./app-browser-client-reuse-manifest.js";
|
|
22
19
|
import { resolveManifestNavigationInterceptionContext, resolveMiddlewareRewriteNavigationInterceptionContext } from "./app-browser-interception-context.js";
|
|
23
20
|
import { readHistoryStatePreviousNextUrl } from "./app-history-state.js";
|
|
24
21
|
import { AppBrowserMpaNavigationScheduler } from "./app-browser-mpa-navigation.js";
|
|
@@ -26,6 +23,9 @@ import { blockDangerousStreamedRscRedirect } from "./app-browser-rsc-redirect.js
|
|
|
26
23
|
import { navigationPlanner } from "./navigation-planner.js";
|
|
27
24
|
import { resolveHybridClientRewriteHref } from "../shims/internal/hybrid-client-route-owner.js";
|
|
28
25
|
import { DYNAMIC_NAVIGATION_CACHE_TTL, PREFETCH_CACHE_TTL, __basePath, appRouterInstance, commitClientNavigationState, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, deletePrefetchResponseSnapshot, getClientNavigationRenderContext, getMountedSlotsHeader, getPrefetchCache, hasPrefetchCacheEntryForNavigation, invalidatePrefetchCache, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setPendingPathname, useRouter } from "../shims/navigation.js";
|
|
26
|
+
import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap } from "./app-bfcache-identity.js";
|
|
27
|
+
import { createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, resolveServerActionOperationLane } from "./app-browser-action-result.js";
|
|
28
|
+
import { createClientReuseManifestHeaderFromVisibleAppState } from "./app-browser-client-reuse-manifest.js";
|
|
29
29
|
import { chunksToReadableStream, createProgressiveRscStream, getVinextBrowserGlobal } from "./app-browser-stream.js";
|
|
30
30
|
import { COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, isCacheRestorableAppPayloadMetadata, isCompleteAppPayloadMetadata, resolveInterceptionContextFromPreviousNextUrl } from "./app-browser-state.js";
|
|
31
31
|
import { clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch } from "./app-browser-navigation-controller.js";
|
|
@@ -597,9 +597,10 @@ function BrowserRoot({ hydrationCachePublication, initialElements, initialNaviga
|
|
|
597
597
|
resetKey: treeState.renderId,
|
|
598
598
|
onCatch: handleDevRecoveryBoundaryCatch
|
|
599
599
|
}, innerTree) : innerTree;
|
|
600
|
+
const scrollScopedTree = createElement(AppRouterScrollCommitProvider, { commitId: treeState.renderId }, committedTree);
|
|
600
601
|
const rootErrorTree = createElement(GlobalErrorBoundary, {
|
|
601
602
|
fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
|
|
602
|
-
children:
|
|
603
|
+
children: scrollScopedTree
|
|
603
604
|
});
|
|
604
605
|
const ClientNavigationRenderContext = getClientNavigationRenderContext();
|
|
605
606
|
if (!ClientNavigationRenderContext) return rootErrorTree;
|
|
@@ -763,7 +764,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
763
764
|
const metadata = AppElementsWire.readMetadata(elements);
|
|
764
765
|
if (!isCompleteAppPayloadMetadata(metadata)) return;
|
|
765
766
|
const mountedSlotsHeader = getMountedSlotIdsHeader(elements);
|
|
766
|
-
const
|
|
767
|
+
const headers = createRscRequestHeaders({ mountedSlotsHeader });
|
|
768
|
+
const rscUrl = await createRscRequestUrl(initialPathAndSearch, headers);
|
|
767
769
|
if (cacheGeneration !== clientNavigationCacheGeneration) return;
|
|
768
770
|
const snapshot = {
|
|
769
771
|
compatibilityIdHeader: CLIENT_RSC_COMPATIBILITY_ID,
|
|
@@ -851,9 +853,9 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
851
853
|
let redirectCount = redirectDepth;
|
|
852
854
|
let detachedNavigationCommits = false;
|
|
853
855
|
const activeTraversalIntent = navigationKind === "traverse" ? traversalIntent ?? historyController.resolveTraversalIntent(window.history.state) : null;
|
|
854
|
-
const performHardNavigationForScrollIntent = (targetHref) => {
|
|
856
|
+
const performHardNavigationForScrollIntent = (targetHref, mode) => {
|
|
855
857
|
consumeAppRouterScrollIntent(scrollIntent ?? null);
|
|
856
|
-
const didNavigate = browserNavigationController.performHardNavigation(targetHref);
|
|
858
|
+
const didNavigate = browserNavigationController.performHardNavigation(targetHref, mode);
|
|
857
859
|
if (!didNavigate) clearAppNavigationFailureTarget(targetHref);
|
|
858
860
|
return didNavigate;
|
|
859
861
|
};
|
|
@@ -1033,6 +1035,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1033
1035
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
1034
1036
|
const navContentType = navResponse.headers.get("content-type") ?? "";
|
|
1035
1037
|
const streamedRedirectTarget = navResponse.headers.get(VINEXT_RSC_REDIRECT_HEADER);
|
|
1038
|
+
const streamedRedirectTypeHeader = navResponse.headers.get(VINEXT_RSC_REDIRECT_TYPE_HEADER);
|
|
1039
|
+
const streamedRedirectType = streamedRedirectTypeHeader === "push" || streamedRedirectTypeHeader === "replace" ? streamedRedirectTypeHeader : null;
|
|
1036
1040
|
if (blockDangerousStreamedRscRedirect(navResponse, streamedRedirectTarget)) return;
|
|
1037
1041
|
const liveFetchDecision = navigationPlanner.classifyRscFetchResult({
|
|
1038
1042
|
clientCompatibilityId: CLIENT_RSC_COMPATIBILITY_ID,
|
|
@@ -1047,13 +1051,14 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1047
1051
|
responseOk: navResponse.ok,
|
|
1048
1052
|
responseUrl: navResponseUrl ?? navResponse.url,
|
|
1049
1053
|
source: "live",
|
|
1050
|
-
streamedRedirectTarget
|
|
1054
|
+
streamedRedirectTarget,
|
|
1055
|
+
streamedRedirectType
|
|
1051
1056
|
});
|
|
1052
1057
|
if (liveFetchDecision.kind === "hardNavigate") {
|
|
1053
1058
|
if (liveFetchDecision.discardBody) navResponse.body?.cancel().catch(() => {});
|
|
1054
1059
|
if (liveFetchDecision.reason === "redirectDepthExhausted") console.error("[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.");
|
|
1055
1060
|
if (liveFetchDecision.reason === "streamedRedirectLoop") console.error("[vinext] RSC streamed redirect resolved to the current URL — aborting navigation to prevent infinite loop.");
|
|
1056
|
-
performHardNavigationForScrollIntent(liveFetchDecision.url);
|
|
1061
|
+
performHardNavigationForScrollIntent(liveFetchDecision.url, liveFetchDecision.hardNavigationMode);
|
|
1057
1062
|
return;
|
|
1058
1063
|
}
|
|
1059
1064
|
if (liveFetchDecision.kind === "followRedirect") {
|
|
@@ -1204,7 +1209,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1204
1209
|
}
|
|
1205
1210
|
};
|
|
1206
1211
|
import.meta.hot.on("rsc:update", () => {
|
|
1207
|
-
|
|
1212
|
+
const updateId = ++latestRscHmrUpdateId;
|
|
1213
|
+
handleRscUpdate(updateId);
|
|
1208
1214
|
});
|
|
1209
1215
|
}
|
|
1210
1216
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { stripBasePath } from "../utils/base-path.js";
|
|
2
2
|
import { clearAppNavigationFailureTarget, getAppNavigationFailureTarget } from "../client/app-nav-failure-handler.js";
|
|
3
3
|
import { claimAppRouterScrollIntentForCommit, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
|
|
4
|
-
import { resolveServerActionOperationLane, shouldScheduleRefreshForDiscardedServerAction } from "./app-browser-action-result.js";
|
|
5
4
|
import { activateNavigationSnapshot, clearPendingPathname, commitClientNavigationState, createSnapshotPathAndSearch } from "../shims/navigation.js";
|
|
5
|
+
import { resolveServerActionOperationLane, shouldScheduleRefreshForDiscardedServerAction } from "./app-browser-action-result.js";
|
|
6
6
|
import { FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, createPendingNavigationCommit } from "./app-browser-state.js";
|
|
7
7
|
import { applyApprovedVisibleCommit, approveHmrVisibleCommit, approvePendingNavigationCommit, resolveAndClassifyNavigationCommit } from "./app-browser-visible-commit.js";
|
|
8
8
|
import { startTransition, useInsertionEffect, useLayoutEffect } from "react";
|
|
@@ -259,12 +259,14 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
259
259
|
}
|
|
260
260
|
if (visibleCommitMode === "synchronous") {
|
|
261
261
|
flushSync(() => {
|
|
262
|
-
|
|
262
|
+
const committedState = captureCandidateState(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
|
|
263
|
+
setter(committedState);
|
|
263
264
|
});
|
|
264
265
|
return;
|
|
265
266
|
}
|
|
266
267
|
startTransition(() => {
|
|
267
|
-
|
|
268
|
+
const committedState = captureCandidateState(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
|
|
269
|
+
setter(committedState);
|
|
268
270
|
});
|
|
269
271
|
}
|
|
270
272
|
function dispatchSynchronousVisibleCommit(commit) {
|