vinext 1.0.0-beta.2 → 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 +6 -9
- package/dist/build/client-build-config.js +7 -1
- package/dist/check.js +4 -0
- package/dist/config/config-matchers.js +4 -1
- package/dist/config/next-config.d.ts +12 -3
- package/dist/config/next-config.js +4 -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 +3 -0
- package/dist/entries/app-rsc-manifest.js +19 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +10 -3
- package/dist/index.js +112 -43
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- 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/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 +14 -6
- package/dist/routing/app-route-graph.js +106 -16
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +3 -3
- package/dist/server/app-browser-entry.js +103 -31
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-page-cache-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +1 -1
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +8 -0
- package/dist/server/app-page-dispatch.js +31 -6
- package/dist/server/app-page-element-builder.d.ts +2 -0
- package/dist/server/app-page-element-builder.js +33 -7
- package/dist/server/app-page-head.d.ts +3 -3
- package/dist/server/app-page-head.js +58 -13
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
- package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
- package/dist/server/app-page-probe.d.ts +4 -0
- package/dist/server/app-page-probe.js +6 -2
- package/dist/server/app-page-request.d.ts +3 -0
- package/dist/server/app-page-request.js +1 -1
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +7 -0
- package/dist/server/app-page-route-wiring.js +211 -49
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -1
- package/dist/server/app-route-handler-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +9 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +6 -0
- package/dist/server/app-route-module-loader.js +3 -0
- package/dist/server/app-router-entry.js +6 -3
- package/dist/server/app-rsc-errors.js +10 -0
- package/dist/server/app-rsc-handler.js +21 -9
- package/dist/server/app-rsc-route-matching.d.ts +9 -0
- package/dist/server/app-rsc-route-matching.js +6 -0
- package/dist/server/app-ssr-entry.js +4 -4
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- package/dist/server/dev-server.d.ts +3 -1
- package/dist/server/dev-server.js +145 -312
- package/dist/server/headers.d.ts +2 -2
- package/dist/server/headers.js +4 -3
- package/dist/server/isr-cache.d.ts +10 -33
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- package/dist/server/pages-api-route.d.ts +2 -0
- package/dist/server/pages-api-route.js +4 -1
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-get-initial-props.d.ts +1 -1
- package/dist/server/pages-get-initial-props.js +1 -4
- 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 +61 -9
- package/dist/server/pages-page-data.js +334 -74
- package/dist/server/pages-page-handler.d.ts +5 -1
- package/dist/server/pages-page-handler.js +115 -19
- package/dist/server/pages-page-response.d.ts +5 -3
- package/dist/server/pages-page-response.js +23 -26
- package/dist/server/pages-request-pipeline.d.ts +7 -0
- package/dist/server/pages-request-pipeline.js +5 -1
- 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 +10 -4
- package/dist/server/prod-server.d.ts +2 -1
- package/dist/server/prod-server.js +37 -13
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +29 -0
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- package/dist/shims/cache-runtime.d.ts +6 -0
- package/dist/shims/cache-runtime.js +4 -1
- package/dist/shims/cache.js +1 -1
- package/dist/shims/error.js +1 -1
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/headers.d.ts +10 -13
- package/dist/shims/headers.js +85 -36
- package/dist/shims/internal/cookie-serialize.d.ts +10 -9
- package/dist/shims/internal/cookie-serialize.js +9 -8
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- package/dist/shims/link.js +18 -6
- package/dist/shims/metadata.d.ts +7 -17
- package/dist/shims/metadata.js +74 -24
- package/dist/shims/navigation.js +1 -1
- package/dist/shims/request-context.d.ts +4 -1
- package/dist/shims/router.js +18 -7
- package/dist/shims/script.js +1 -1
- package/dist/shims/server.d.ts +7 -6
- package/dist/shims/server.js +156 -57
- package/dist/shims/unified-request-context.d.ts +21 -2
- package/dist/shims/unified-request-context.js +82 -1
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- 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/package.json +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/plugins/transform-cache.ts
|
|
2
|
+
/**
|
|
3
|
+
* Memoizes a pure per-module transform across the repeated build passes
|
|
4
|
+
* (scan client/server references + rsc/client/ssr builds), which feed the
|
|
5
|
+
* same module source through the same transform up to five times.
|
|
6
|
+
*
|
|
7
|
+
* Entries are keyed by module id and replaced when the source changes, so dev
|
|
8
|
+
* rebuilds retain at most one source version per id. `variant` distinguishes
|
|
9
|
+
* results that also depend on an environment-derived input (e.g. the
|
|
10
|
+
* typeof-window replacement); callers must pass a primitive or stable
|
|
11
|
+
* reference. Cached values include `null` ("no transform applies") so the
|
|
12
|
+
* negative case is not recomputed either.
|
|
13
|
+
*
|
|
14
|
+
* Only safe for transforms whose result depends solely on (id, source,
|
|
15
|
+
* variant) — no filesystem or plugin-state reads inside `compute`.
|
|
16
|
+
*/
|
|
17
|
+
function createTransformCache() {
|
|
18
|
+
const cache = /* @__PURE__ */ new Map();
|
|
19
|
+
return (id, source, variant, compute) => {
|
|
20
|
+
let entry = cache.get(id);
|
|
21
|
+
if (entry?.source !== source) {
|
|
22
|
+
entry = {
|
|
23
|
+
source,
|
|
24
|
+
results: /* @__PURE__ */ new Map()
|
|
25
|
+
};
|
|
26
|
+
cache.set(id, entry);
|
|
27
|
+
}
|
|
28
|
+
const boxed = entry.results.get(variant);
|
|
29
|
+
if (boxed) return boxed.value;
|
|
30
|
+
const value = compute();
|
|
31
|
+
entry.results.set(variant, { value });
|
|
32
|
+
return value;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { createTransformCache };
|
|
@@ -24,7 +24,9 @@ type InterceptingRoute = {
|
|
|
24
24
|
pagePath: string; /** Filesystem segments from app/ root to the intercepting page directory. */
|
|
25
25
|
sourcePageSegments?: string[]; /** Absolute layout paths inside the intercepting route tree, outermost to innermost */
|
|
26
26
|
layoutPaths: string[]; /** Normalized branch segments accumulated at each intercept layout. */
|
|
27
|
-
layoutSegments?: string[][]; /**
|
|
27
|
+
layoutSegments?: string[][]; /** Loading conventions inside the intercepted branch, outermost to innermost. */
|
|
28
|
+
loadingPaths?: string[]; /** Tree positions for loadingPaths in the normalized interception branch. */
|
|
29
|
+
loadingTreePositions?: number[]; /** Full normalized interception branch segments through the page. */
|
|
28
30
|
branchSegments?: string[]; /** Nearest not-found convention inside the interception branch. */
|
|
29
31
|
notFoundPath?: string | null; /** Slot-root-relative branch segments used to scope not-found params. */
|
|
30
32
|
notFoundBranchSegments?: string[]; /** Tree position relative to the normalized interception branch. */
|
|
@@ -41,14 +43,17 @@ type ParallelSlot = {
|
|
|
41
43
|
key: string; /** Slot name (e.g. "team" from @team) */
|
|
42
44
|
name: string; /** Absolute path to the @slot directory that owns this slot. Internal routing metadata. */
|
|
43
45
|
ownerDir: string; /** Stable tree path for the directory whose layout owns this slot. */
|
|
44
|
-
ownerTreePath: string; /**
|
|
46
|
+
ownerTreePath: string; /** Directory depth of the slot owner from app/ root. */
|
|
47
|
+
ownerTreePosition?: number; /** Whether the slot owner directory declares its own page component. */
|
|
45
48
|
hasPage: boolean; /** Absolute path to the slot's page component */
|
|
46
49
|
pagePath: string | null; /** Absolute path to the slot's default.tsx fallback */
|
|
47
50
|
defaultPath: string | null; /** Absolute path to the slot's layout component (wraps slot content) */
|
|
48
51
|
layoutPath: string | null; /** Nested active-branch layouts whose exports contribute route config. */
|
|
49
52
|
configLayoutPaths?: string[]; /** Tree positions of configLayoutPaths relative to the slot root. */
|
|
50
53
|
configLayoutTreePositions?: number[]; /** Absolute path to the slot's loading component */
|
|
51
|
-
loadingPath: string | null; /**
|
|
54
|
+
loadingPath: string | null; /** Per-segment loading components from the slot root to its active page. */
|
|
55
|
+
loadingPaths?: string[]; /** Slot-root-relative tree positions aligned with loadingPaths. */
|
|
56
|
+
loadingTreePositions?: number[]; /** Absolute path to the slot's error component */
|
|
52
57
|
errorPath: string | null; /** Nearest not-found convention for the slot's active branch. */
|
|
53
58
|
notFoundPath?: string | null; /** Tree position of that convention relative to the slot root. */
|
|
54
59
|
notFoundTreePosition?: number | null; /** Intercepting routes within this slot */
|
|
@@ -99,8 +104,10 @@ type AppRoute = {
|
|
|
99
104
|
* On soft-nav, the intercepting page replaces the entire page response.
|
|
100
105
|
* Empty array when there are no sibling-style interception markers.
|
|
101
106
|
*/
|
|
102
|
-
siblingIntercepts: InterceptingRoute[]; /** Loading component path */
|
|
103
|
-
loadingPath: string | null; /**
|
|
107
|
+
siblingIntercepts: InterceptingRoute[]; /** Loading component path (leaf directory only) */
|
|
108
|
+
loadingPath: string | null; /** Per-segment loading component paths, aligned with loadingTreePositions. */
|
|
109
|
+
loadingPaths?: string[]; /** Tree position (directory depth from app/ root) for each loading boundary. */
|
|
110
|
+
loadingTreePositions?: number[]; /** Error component path (leaf directory only) */
|
|
104
111
|
errorPath: string | null;
|
|
105
112
|
/**
|
|
106
113
|
* Per-layout error boundary paths, aligned with the layouts array.
|
|
@@ -306,6 +313,7 @@ declare function buildAppRouteGraph(appDir: string, matcher: ValidFileMatcher):
|
|
|
306
313
|
routeManifest: RouteManifest;
|
|
307
314
|
}>;
|
|
308
315
|
declare function computeRootParamNames(routeSegments: readonly string[], layoutTreePositions: readonly number[]): string[];
|
|
316
|
+
declare function patternsStructurallyEquivalent(a: readonly string[], b: readonly string[]): boolean;
|
|
309
317
|
/**
|
|
310
318
|
* Find the best route to attach a sibling intercept to, given the directory
|
|
311
319
|
* that contains the interception marker.
|
|
@@ -359,4 +367,4 @@ declare function computeAppRouteStaticSiblings(allRoutes: readonly {
|
|
|
359
367
|
patternParts?: readonly string[] | null;
|
|
360
368
|
}): string[];
|
|
361
369
|
//#endregion
|
|
362
|
-
export { AppRoute, AppRouteGraphRoute, AppRouteSemanticIds, GraphVersion, RootBoundaryId, RouteManifest, RouteManifestInterception, RouteManifestRootBoundary, RouteManifestRoute, RouteManifestSlotBinding, StaticSegmentGraph, buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment };
|
|
370
|
+
export { AppRoute, AppRouteGraphRoute, AppRouteSemanticIds, GraphVersion, RootBoundaryId, RouteManifest, RouteManifestInterception, RouteManifestRootBoundary, RouteManifestRoute, RouteManifestSlotBinding, StaticSegmentGraph, buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment, patternsStructurallyEquivalent };
|
|
@@ -460,7 +460,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
460
460
|
if (subPage !== void 0) {
|
|
461
461
|
const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage, matcher);
|
|
462
462
|
const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage, matcher);
|
|
463
|
-
return {
|
|
463
|
+
return withSlotLoadingEntries({
|
|
464
464
|
...slot,
|
|
465
465
|
pagePath: subPage,
|
|
466
466
|
configLayoutPaths,
|
|
@@ -468,7 +468,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
468
468
|
notFoundPath: notFoundBoundary.path,
|
|
469
469
|
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
470
470
|
routeSegments: rawSegments
|
|
471
|
-
};
|
|
471
|
+
}, subPage, matcher);
|
|
472
472
|
}
|
|
473
473
|
return slot;
|
|
474
474
|
});
|
|
@@ -549,7 +549,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
549
549
|
const subPage = slotPages.get(slot.key);
|
|
550
550
|
const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage ?? null, matcher);
|
|
551
551
|
const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage ?? null, matcher);
|
|
552
|
-
return {
|
|
552
|
+
return withSlotLoadingEntries({
|
|
553
553
|
...slot,
|
|
554
554
|
pagePath: subPage || null,
|
|
555
555
|
configLayoutPaths,
|
|
@@ -557,7 +557,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
557
557
|
notFoundPath: notFoundBoundary.path,
|
|
558
558
|
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
559
559
|
routeSegments: subPage ? rawSegments : null
|
|
560
|
-
};
|
|
560
|
+
}, subPage ?? null, matcher);
|
|
561
561
|
});
|
|
562
562
|
const newRoute = {
|
|
563
563
|
ids: createAppRouteSemanticIds({
|
|
@@ -580,7 +580,9 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
580
580
|
ownerTreePath: childrenOwnerTreePath,
|
|
581
581
|
state: childrenDefault ? "default" : childrenCatchAll ? "active" : "unmatched"
|
|
582
582
|
},
|
|
583
|
-
loadingPath:
|
|
583
|
+
loadingPath: null,
|
|
584
|
+
loadingPaths: parentRoute.loadingPaths,
|
|
585
|
+
loadingTreePositions: parentRoute.loadingTreePositions,
|
|
584
586
|
errorPath: parentRoute.errorPath,
|
|
585
587
|
layoutErrorPaths: parentRoute.layoutErrorPaths,
|
|
586
588
|
notFoundPath: parentRoute.notFoundPath,
|
|
@@ -678,6 +680,36 @@ function findSlotConfigLayoutTreePositions(slotDir, layoutPaths) {
|
|
|
678
680
|
return relativeDir ? relativeDir.split(path.sep).filter(Boolean).length : 0;
|
|
679
681
|
});
|
|
680
682
|
}
|
|
683
|
+
function findSlotLoadingEntries(slotDir, pagePath, matcher) {
|
|
684
|
+
const pageDir = pagePath ? path.dirname(pagePath) : slotDir;
|
|
685
|
+
if (pageDir !== slotDir && !pageDir.startsWith(`${slotDir}${path.sep}`)) return [];
|
|
686
|
+
const relativeDir = path.relative(slotDir, pageDir);
|
|
687
|
+
const segments = relativeDir ? relativeDir.split(path.sep).filter(Boolean) : [];
|
|
688
|
+
const loadings = [];
|
|
689
|
+
let currentDir = slotDir;
|
|
690
|
+
const rootLoading = findFile(currentDir, "loading", matcher);
|
|
691
|
+
if (rootLoading) loadings.push({
|
|
692
|
+
path: rootLoading,
|
|
693
|
+
treePosition: 0
|
|
694
|
+
});
|
|
695
|
+
for (let index = 0; index < segments.length; index++) {
|
|
696
|
+
currentDir = path.join(currentDir, segments[index]);
|
|
697
|
+
const loading = findFile(currentDir, "loading", matcher);
|
|
698
|
+
if (loading) loadings.push({
|
|
699
|
+
path: loading,
|
|
700
|
+
treePosition: index + 1
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
return loadings;
|
|
704
|
+
}
|
|
705
|
+
function withSlotLoadingEntries(slot, pagePath, matcher) {
|
|
706
|
+
const loadingEntries = findSlotLoadingEntries(slot.ownerDir, pagePath, matcher);
|
|
707
|
+
return {
|
|
708
|
+
...slot,
|
|
709
|
+
loadingPaths: loadingEntries.map((entry) => entry.path),
|
|
710
|
+
loadingTreePositions: loadingEntries.map((entry) => entry.treePosition)
|
|
711
|
+
};
|
|
712
|
+
}
|
|
681
713
|
function findSlotNotFoundBoundary(slotDir, pagePath, matcher) {
|
|
682
714
|
let dir = pagePath ? path.dirname(pagePath) : slotDir;
|
|
683
715
|
while (dir === slotDir || dir.startsWith(`${slotDir}${path.sep}`)) {
|
|
@@ -751,6 +783,9 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
751
783
|
const templateTreePositions = computeLayoutTreePositions(appDir, templates);
|
|
752
784
|
const layoutTreePositions = computeLayoutTreePositions(appDir, layouts);
|
|
753
785
|
const layoutErrorPaths = discoverLayoutAlignedErrors(segments, appDir, matcher);
|
|
786
|
+
const loadingEntries = discoverSegmentLoadings(segments, appDir, matcher);
|
|
787
|
+
const loadingPaths = loadingEntries.map((entry) => entry.path);
|
|
788
|
+
const loadingTreePositions = loadingEntries.map((entry) => entry.treePosition);
|
|
754
789
|
const errorEntries = discoverSegmentErrors(segments, appDir, matcher);
|
|
755
790
|
const errorPaths = errorEntries.map((entry) => entry.path);
|
|
756
791
|
const errorTreePositions = errorEntries.map((entry) => entry.treePosition);
|
|
@@ -785,6 +820,8 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
785
820
|
templates,
|
|
786
821
|
parallelSlots,
|
|
787
822
|
loadingPath,
|
|
823
|
+
loadingPaths,
|
|
824
|
+
loadingTreePositions,
|
|
788
825
|
errorPath,
|
|
789
826
|
layoutErrorPaths,
|
|
790
827
|
errorPaths,
|
|
@@ -933,6 +970,31 @@ function discoverSegmentErrors(segments, appDir, matcher) {
|
|
|
933
970
|
return errors;
|
|
934
971
|
}
|
|
935
972
|
/**
|
|
973
|
+
* Discover loading.tsx files by segment tree position.
|
|
974
|
+
*
|
|
975
|
+
* Loading conventions belong to loader-tree segments rather than layouts. A
|
|
976
|
+
* segment without layout.tsx can therefore provide the boundary that suspends
|
|
977
|
+
* while its child layout renders.
|
|
978
|
+
*/
|
|
979
|
+
function discoverSegmentLoadings(segments, appDir, matcher) {
|
|
980
|
+
const loadings = [];
|
|
981
|
+
const rootLoading = findFile(appDir, "loading", matcher);
|
|
982
|
+
if (rootLoading) loadings.push({
|
|
983
|
+
path: rootLoading,
|
|
984
|
+
treePosition: 0
|
|
985
|
+
});
|
|
986
|
+
let currentDir = appDir;
|
|
987
|
+
for (let index = 0; index < segments.length; index++) {
|
|
988
|
+
currentDir = path.join(currentDir, segments[index]);
|
|
989
|
+
const loading = findFile(currentDir, "loading", matcher);
|
|
990
|
+
if (loading) loadings.push({
|
|
991
|
+
path: loading,
|
|
992
|
+
treePosition: index + 1
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
return loadings;
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
936
998
|
* Discover error.tsx files aligned with the layouts array.
|
|
937
999
|
*
|
|
938
1000
|
* Route manifests still model layout-owned boundary facts by layout index.
|
|
@@ -1037,7 +1099,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
|
|
|
1037
1099
|
slotParamNames = [...ownerUrl?.params ?? [], ...mirror.slotParamNames];
|
|
1038
1100
|
}
|
|
1039
1101
|
const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, mirror?.pagePath ?? null, matcher);
|
|
1040
|
-
const inheritedSlot = {
|
|
1102
|
+
const inheritedSlot = withSlotLoadingEntries({
|
|
1041
1103
|
...slot,
|
|
1042
1104
|
pagePath: mirror?.pagePath ?? null,
|
|
1043
1105
|
configLayoutPaths,
|
|
@@ -1046,7 +1108,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
|
|
|
1046
1108
|
routeSegments: mirror?.segments ?? null,
|
|
1047
1109
|
slotPatternParts,
|
|
1048
1110
|
slotParamNames
|
|
1049
|
-
};
|
|
1111
|
+
}, mirror?.pagePath ?? null, matcher);
|
|
1050
1112
|
slotMap.set(slot.key, inheritedSlot);
|
|
1051
1113
|
}
|
|
1052
1114
|
}
|
|
@@ -1209,6 +1271,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1209
1271
|
const ownerSegments = path.relative(appDir, dir).split(path.sep).filter((segment) => segment.length > 0);
|
|
1210
1272
|
const ownerTreePath = createAppRouteGraphTreePath(ownerSegments, ownerSegments.length);
|
|
1211
1273
|
const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
|
|
1274
|
+
const loadingEntries = findSlotLoadingEntries(slotDir, pagePath, matcher);
|
|
1212
1275
|
const notFoundBoundary = findSlotNotFoundBoundary(slotDir, pagePath, matcher);
|
|
1213
1276
|
slots.push({
|
|
1214
1277
|
id: createAppRouteGraphSlotId(slotName, ownerTreePath),
|
|
@@ -1216,6 +1279,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1216
1279
|
name: slotName,
|
|
1217
1280
|
ownerDir: slotDir,
|
|
1218
1281
|
ownerTreePath,
|
|
1282
|
+
ownerTreePosition: ownerSegments.length,
|
|
1219
1283
|
hasPage: pagePath !== null,
|
|
1220
1284
|
pagePath,
|
|
1221
1285
|
defaultPath,
|
|
@@ -1223,6 +1287,8 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1223
1287
|
configLayoutPaths,
|
|
1224
1288
|
configLayoutTreePositions: findSlotConfigLayoutTreePositions(slotDir, configLayoutPaths),
|
|
1225
1289
|
loadingPath: findFile(slotDir, "loading", matcher),
|
|
1290
|
+
loadingPaths: loadingEntries.map((loading) => loading.path),
|
|
1291
|
+
loadingTreePositions: loadingEntries.map((loading) => loading.treePosition),
|
|
1226
1292
|
errorPath: findFile(slotDir, "error", matcher),
|
|
1227
1293
|
notFoundPath: notFoundBoundary.path,
|
|
1228
1294
|
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
@@ -1374,7 +1440,7 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1374
1440
|
* Recursively scan a directory tree for page.tsx files that are inside
|
|
1375
1441
|
* intercepting route directories.
|
|
1376
1442
|
*/
|
|
1377
|
-
function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, results, matcher) {
|
|
1443
|
+
function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, results, matcher, parentLoadingEntries = []) {
|
|
1378
1444
|
if (!fs.existsSync(currentDir)) return;
|
|
1379
1445
|
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
1380
1446
|
for (const entry of entries) {
|
|
@@ -1384,8 +1450,14 @@ function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, res
|
|
|
1384
1450
|
const interceptDir = path.join(currentDir, entry.name);
|
|
1385
1451
|
if (interceptMatch) {
|
|
1386
1452
|
const restOfName = entry.name.slice(interceptMatch.prefix.length);
|
|
1387
|
-
collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, slotRootDir, results, matcher);
|
|
1388
|
-
} else
|
|
1453
|
+
collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, slotRootDir, results, matcher, [], parentLoadingEntries, path.relative(slotRootDir, currentDir).split(path.sep).filter(Boolean).length);
|
|
1454
|
+
} else {
|
|
1455
|
+
const loading = findFile(interceptDir, "loading", matcher);
|
|
1456
|
+
scanForInterceptingPages(interceptDir, slotRootDir, routeDir, appDir, results, matcher, loading ? [...parentLoadingEntries, {
|
|
1457
|
+
path: loading,
|
|
1458
|
+
treePosition: path.relative(slotRootDir, interceptDir).split(path.sep).filter(Boolean).length
|
|
1459
|
+
}] : parentLoadingEntries);
|
|
1460
|
+
}
|
|
1389
1461
|
}
|
|
1390
1462
|
}
|
|
1391
1463
|
/**
|
|
@@ -1399,25 +1471,43 @@ function matchInterceptConvention(name) {
|
|
|
1399
1471
|
* Collect page.tsx files inside an intercepting route directory tree
|
|
1400
1472
|
* and compute their target URL patterns.
|
|
1401
1473
|
*/
|
|
1402
|
-
function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, parentLayoutPaths = []) {
|
|
1474
|
+
function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, parentLayoutPaths = [], parentLoadingEntries = [], treePositionOffset = 0) {
|
|
1403
1475
|
const currentLayoutPath = findFile(currentDir, "layout", matcher);
|
|
1404
1476
|
const layoutPaths = currentLayoutPath ? [...parentLayoutPaths, currentLayoutPath] : parentLayoutPaths;
|
|
1477
|
+
const currentLoadingPath = findFile(currentDir, "loading", matcher);
|
|
1478
|
+
const relativeCurrentDir = path.relative(interceptRoot, currentDir);
|
|
1479
|
+
const currentTreePosition = treePositionOffset + 1 + (relativeCurrentDir ? relativeCurrentDir.split(path.sep).filter(Boolean).length : 0);
|
|
1480
|
+
const loadingEntries = currentLoadingPath ? [...parentLoadingEntries, {
|
|
1481
|
+
path: currentLoadingPath,
|
|
1482
|
+
treePosition: currentTreePosition
|
|
1483
|
+
}] : parentLoadingEntries;
|
|
1405
1484
|
const page = findFile(currentDir, "page", matcher);
|
|
1406
1485
|
if (page) {
|
|
1407
1486
|
const targetPattern = computeInterceptTarget(convention, interceptSegment, currentDir, interceptRoot, routeDir, appDir);
|
|
1408
1487
|
if (targetPattern) {
|
|
1409
1488
|
const sourceMatchPattern = computeInterceptSourceMatchPattern(interceptParentDir, appDir);
|
|
1410
1489
|
const notFoundBoundary = findSlotNotFoundBoundary(slotRootDir ?? interceptRoot, page, matcher);
|
|
1411
|
-
const branchSegments = [interceptSegment, ...path.relative(interceptRoot, path.dirname(page)).split(path.sep).filter(Boolean)];
|
|
1412
1490
|
const slotParentSegments = slotRootDir ? path.relative(slotRootDir, interceptParentDir).split(path.sep).filter(Boolean) : [];
|
|
1491
|
+
const branchSegments = [
|
|
1492
|
+
...slotParentSegments,
|
|
1493
|
+
interceptSegment,
|
|
1494
|
+
...path.relative(interceptRoot, path.dirname(page)).split(path.sep).filter(Boolean)
|
|
1495
|
+
];
|
|
1413
1496
|
results.push({
|
|
1414
1497
|
branchSegments,
|
|
1415
1498
|
convention,
|
|
1416
1499
|
layoutPaths: [...layoutPaths],
|
|
1417
1500
|
layoutSegments: layoutPaths.map((layoutPath) => {
|
|
1418
|
-
|
|
1501
|
+
const relativeDir = path.relative(interceptRoot, path.dirname(layoutPath));
|
|
1502
|
+
return [
|
|
1503
|
+
...slotParentSegments,
|
|
1504
|
+
interceptSegment,
|
|
1505
|
+
...relativeDir.split(path.sep).filter(Boolean)
|
|
1506
|
+
];
|
|
1419
1507
|
}),
|
|
1420
|
-
|
|
1508
|
+
loadingPaths: loadingEntries.map((loading) => loading.path),
|
|
1509
|
+
loadingTreePositions: loadingEntries.map((loading) => loading.treePosition),
|
|
1510
|
+
notFoundBranchSegments: branchSegments,
|
|
1421
1511
|
notFoundPath: notFoundBoundary.path,
|
|
1422
1512
|
notFoundTreePosition: notFoundBoundary.treePosition === null ? null : slotRootDir ? notFoundBoundary.treePosition : notFoundBoundary.treePosition + 1,
|
|
1423
1513
|
targetPattern: targetPattern.pattern,
|
|
@@ -1433,7 +1523,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1433
1523
|
for (const entry of entries) {
|
|
1434
1524
|
if (!entry.isDirectory()) continue;
|
|
1435
1525
|
if (entry.name.startsWith("_")) continue;
|
|
1436
|
-
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, layoutPaths);
|
|
1526
|
+
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, layoutPaths, loadingEntries, treePositionOffset);
|
|
1437
1527
|
}
|
|
1438
1528
|
}
|
|
1439
1529
|
/**
|
|
@@ -1661,4 +1751,4 @@ function computeAppRouteStaticSiblings(allRoutes, matchedRoute) {
|
|
|
1661
1751
|
return Array.from(siblings);
|
|
1662
1752
|
}
|
|
1663
1753
|
//#endregion
|
|
1664
|
-
export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment };
|
|
1754
|
+
export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment, patternsStructurallyEquivalent };
|
|
@@ -11,6 +11,8 @@ import { IncomingMessage, ServerResponse } from "node:http";
|
|
|
11
11
|
declare function handleApiRoute(runner: ModuleImporter, req: IncomingMessage, res: ServerResponse, url: string, apiRoutes: Route[], nextConfig?: {
|
|
12
12
|
basePath?: string;
|
|
13
13
|
i18n?: NextI18nConfig | null;
|
|
14
|
+
trustedRevalidateOrigin?: string;
|
|
15
|
+
allowedRevalidateHeaderKeys?: readonly string[];
|
|
14
16
|
trailingSlash?: boolean;
|
|
15
17
|
}): Promise<boolean>;
|
|
16
18
|
//#endregion
|
|
@@ -160,7 +160,7 @@ async function writeEdgeApiResponseBody(res, body) {
|
|
|
160
160
|
/**
|
|
161
161
|
* Enhance a Node.js req/res pair with Next.js API route helpers.
|
|
162
162
|
*/
|
|
163
|
-
function enhanceApiObjects(req, res, query, body) {
|
|
163
|
+
function enhanceApiObjects(req, res, query, body, trustedRevalidateOrigin, allowedRevalidateHeaderKeys = [], dev = false) {
|
|
164
164
|
const apiReq = Object.assign(req, {
|
|
165
165
|
body,
|
|
166
166
|
query
|
|
@@ -202,7 +202,7 @@ function enhanceApiObjects(req, res, query, body) {
|
|
|
202
202
|
return this;
|
|
203
203
|
},
|
|
204
204
|
async revalidate(urlPath, opts) {
|
|
205
|
-
await performOnDemandRevalidate(req, urlPath, opts);
|
|
205
|
+
await performOnDemandRevalidate(req, urlPath, opts, trustedRevalidateOrigin, allowedRevalidateHeaderKeys, dev);
|
|
206
206
|
}
|
|
207
207
|
});
|
|
208
208
|
attachPagesPreviewApi(apiReq, apiRes);
|
|
@@ -248,7 +248,7 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
|
|
|
248
248
|
}
|
|
249
249
|
const query = mergeRouteParamsIntoQuery(parseQueryString(url), params);
|
|
250
250
|
const bodyParserConfig = resolveBodyParserConfig(apiModule.config);
|
|
251
|
-
const { apiReq, apiRes } = enhanceApiObjects(req, res, query, bodyParserConfig.enabled ? await parseBody(req, bodyParserConfig.sizeLimit) : void 0);
|
|
251
|
+
const { apiReq, apiRes } = enhanceApiObjects(req, res, query, bodyParserConfig.enabled ? await parseBody(req, bodyParserConfig.sizeLimit) : void 0, nextConfig?.trustedRevalidateOrigin, nextConfig?.allowedRevalidateHeaderKeys, true);
|
|
252
252
|
await handler(apiReq, apiRes);
|
|
253
253
|
return true;
|
|
254
254
|
} catch (e) {
|
|
@@ -302,28 +302,29 @@ function createNavigationCommitEffect(options) {
|
|
|
302
302
|
commitClientNavigationState(navId);
|
|
303
303
|
};
|
|
304
304
|
}
|
|
305
|
-
async function renderNavigationPayload(
|
|
305
|
+
async function renderNavigationPayload(options) {
|
|
306
306
|
syncServerActionHttpFallbackHead(null);
|
|
307
307
|
return browserNavigationController.renderNavigationPayload({
|
|
308
|
-
actionType,
|
|
308
|
+
actionType: options.actionType ?? "navigate",
|
|
309
309
|
createNavigationCommitEffect,
|
|
310
|
-
historyUpdateMode,
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
310
|
+
historyUpdateMode: options.historyUpdateMode,
|
|
311
|
+
navigationCommitKind: options.navigationCommitKind,
|
|
312
|
+
navigationInitiationState: options.navigationInitiationState,
|
|
313
|
+
navigationSnapshot: options.navigationSnapshot,
|
|
314
|
+
navId: options.navId,
|
|
315
|
+
nextElements: options.payload,
|
|
316
|
+
onCommittedState: options.onCommittedState,
|
|
317
|
+
operationLane: options.operationLane ?? "navigation",
|
|
318
|
+
params: options.params,
|
|
319
|
+
payloadOrigin: options.payloadOrigin,
|
|
320
|
+
pendingRouterState: options.pendingRouterState,
|
|
321
|
+
previousNextUrl: options.previousNextUrl,
|
|
322
|
+
restoredBfcacheIds: options.restoredBfcacheIds ?? null,
|
|
323
|
+
reuseCurrentBfcacheIds: options.reuseCurrentBfcacheIds ?? true,
|
|
324
|
+
scrollIntent: options.scrollIntent ?? null,
|
|
325
|
+
targetHistoryIndex: options.traversalIntent?.targetHistoryIndex,
|
|
326
|
+
targetHref: options.targetHref,
|
|
327
|
+
visibleCommitMode: options.visibleCommitMode ?? "transition"
|
|
327
328
|
});
|
|
328
329
|
}
|
|
329
330
|
async function commitSameUrlNavigatePayload(nextElements, actionInitiation, returnValue, revalidation = "none") {
|
|
@@ -725,7 +726,21 @@ function registerServerActionCallback() {
|
|
|
725
726
|
const hashIdx = target.href.indexOf("#");
|
|
726
727
|
const actionScrollIntent = beginAppRouterScrollIntent((hashIdx !== -1 ? target.href.slice(hashIdx) : "") || null);
|
|
727
728
|
if (target.type === "push") saveScrollPosition();
|
|
728
|
-
renderNavigationPayload(
|
|
729
|
+
renderNavigationPayload({
|
|
730
|
+
actionType: target.type === "push" ? "navigate" : "replace",
|
|
731
|
+
historyUpdateMode: target.type === "push" ? "push" : "replace",
|
|
732
|
+
navigationInitiationState: actionInitiation.routerState,
|
|
733
|
+
navigationSnapshot: createClientNavigationRenderSnapshot(target.href, actionInitiation.routerState.navigationSnapshot.params),
|
|
734
|
+
navId: actionInitiation.navigationId,
|
|
735
|
+
operationLane: resolveServerActionOperationLane(revalidation),
|
|
736
|
+
params: {},
|
|
737
|
+
payload: Promise.resolve(elements),
|
|
738
|
+
payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
|
|
739
|
+
pendingRouterState: null,
|
|
740
|
+
previousNextUrl: null,
|
|
741
|
+
scrollIntent: actionScrollIntent,
|
|
742
|
+
targetHref: target.href
|
|
743
|
+
}).catch(() => {
|
|
729
744
|
browserNavigationController.performHardNavigation(target.href);
|
|
730
745
|
});
|
|
731
746
|
},
|
|
@@ -870,6 +885,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
870
885
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
871
886
|
if (shouldUsePendingRouterState) pendingRouterState = beginPendingBrowserRouterState();
|
|
872
887
|
}
|
|
888
|
+
const navigationInitiationState = getBrowserRouterState();
|
|
889
|
+
const mountedSlotsHeader = getMountedSlotIdsHeader(navigationInitiationState.elements);
|
|
873
890
|
while (true) {
|
|
874
891
|
const url = new URL(currentHref, window.location.origin);
|
|
875
892
|
const requestState = getRequestState(navigationKind, url.pathname, currentPrevNextUrl, activeTraversalIntent?.historyState);
|
|
@@ -877,12 +894,9 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
877
894
|
const requestPreviousNextUrl = requestState.previousNextUrl;
|
|
878
895
|
if (navigationKind === "refresh") historyController.syncCurrentHistoryStatePreviousNextUrl(requestPreviousNextUrl, getBrowserRouterState().bfcacheIds);
|
|
879
896
|
setPendingPathname(url.pathname, navId);
|
|
880
|
-
const routerStateAtNavStart = getBrowserRouterState();
|
|
881
|
-
const elementsAtNavStart = routerStateAtNavStart.elements;
|
|
882
|
-
const mountedSlotsHeader = getMountedSlotIdsHeader(elementsAtNavStart);
|
|
883
897
|
const earlyIntentDecision = navigationKind === "navigate" ? navigationPlanner.classifyEarlyNavigationIntent({
|
|
884
898
|
basePath: __basePath,
|
|
885
|
-
currentHref: clientNavigationSnapshotHref(
|
|
899
|
+
currentHref: clientNavigationSnapshotHref(navigationInitiationState.navigationSnapshot),
|
|
886
900
|
mode: "push",
|
|
887
901
|
scroll: false,
|
|
888
902
|
targetHref: url.href
|
|
@@ -960,7 +974,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
960
974
|
const cachedNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, cachedParams);
|
|
961
975
|
const cachedPayload = cachedRoute.elements ? Promise.resolve(cachedRoute.elements) : decodeAppElementsPromise(createFromFetch(Promise.resolve(restoreRscResponse(cachedRoute.response))));
|
|
962
976
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
963
|
-
if (await renderNavigationPayload(
|
|
977
|
+
if (await renderNavigationPayload({
|
|
978
|
+
actionType: toActionType(navigationKind),
|
|
979
|
+
historyUpdateMode: currentHistoryMode,
|
|
980
|
+
navigationCommitKind: detachedNavigationCommits ? "authoritative" : void 0,
|
|
981
|
+
navigationInitiationState,
|
|
982
|
+
navigationSnapshot: cachedNavigationSnapshot,
|
|
983
|
+
navId,
|
|
984
|
+
operationLane: toOperationLane(navigationKind),
|
|
985
|
+
params: cachedParams,
|
|
986
|
+
payload: cachedPayload,
|
|
987
|
+
payloadOrigin: cachedRoute.elements ? COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN : VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN,
|
|
988
|
+
pendingRouterState: detachedNavigationCommits ? null : pendingRouterState,
|
|
989
|
+
previousNextUrl: requestPreviousNextUrl,
|
|
990
|
+
restoredBfcacheIds,
|
|
991
|
+
reuseCurrentBfcacheIds,
|
|
992
|
+
scrollIntent,
|
|
993
|
+
targetHref: currentHref,
|
|
994
|
+
traversalIntent: activeTraversalIntent,
|
|
995
|
+
visibleCommitMode
|
|
996
|
+
}) === "no-commit") {
|
|
964
997
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
965
998
|
deleteVisitedResponse(rscUrl, requestInterceptionContext);
|
|
966
999
|
continue;
|
|
@@ -1016,7 +1049,26 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1016
1049
|
if (optimisticPayload !== null) {
|
|
1017
1050
|
detachedNavigationCommits = true;
|
|
1018
1051
|
const optimisticNavigationSnapshot = createClientNavigationRenderSnapshot(currentHref, optimisticPayload.params);
|
|
1019
|
-
renderNavigationPayload(
|
|
1052
|
+
renderNavigationPayload({
|
|
1053
|
+
actionType: toActionType(navigationKind),
|
|
1054
|
+
historyUpdateMode: currentHistoryMode,
|
|
1055
|
+
navigationCommitKind: "detached",
|
|
1056
|
+
navigationInitiationState,
|
|
1057
|
+
navigationSnapshot: optimisticNavigationSnapshot,
|
|
1058
|
+
navId,
|
|
1059
|
+
operationLane: toOperationLane(navigationKind),
|
|
1060
|
+
params: optimisticPayload.params,
|
|
1061
|
+
payload: Promise.resolve(optimisticPayload.elements),
|
|
1062
|
+
payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
|
|
1063
|
+
pendingRouterState: null,
|
|
1064
|
+
previousNextUrl: requestPreviousNextUrl,
|
|
1065
|
+
restoredBfcacheIds,
|
|
1066
|
+
reuseCurrentBfcacheIds,
|
|
1067
|
+
scrollIntent,
|
|
1068
|
+
targetHref: currentHref,
|
|
1069
|
+
traversalIntent: activeTraversalIntent,
|
|
1070
|
+
visibleCommitMode
|
|
1071
|
+
}).catch((error) => {
|
|
1020
1072
|
if (browserNavigationController.isCurrentNavigation(navId)) console.error("[vinext] Optimistic RSC navigation error:", error);
|
|
1021
1073
|
});
|
|
1022
1074
|
}
|
|
@@ -1024,7 +1076,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1024
1076
|
}
|
|
1025
1077
|
if (!navResponse) {
|
|
1026
1078
|
if (navigationKind === "navigate") {
|
|
1027
|
-
const clientReuseManifestHeader = createClientReuseManifestHeaderFromVisibleAppState(
|
|
1079
|
+
const clientReuseManifestHeader = createClientReuseManifestHeaderFromVisibleAppState(navigationInitiationState);
|
|
1028
1080
|
if (clientReuseManifestHeader !== null) requestHeaders.set(VINEXT_CLIENT_REUSE_MANIFEST_HEADER, clientReuseManifestHeader);
|
|
1029
1081
|
}
|
|
1030
1082
|
navResponse = await fetch(rscUrl, {
|
|
@@ -1086,10 +1138,30 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1086
1138
|
const rscPayload = decodeAppElementsPromise(createFromFetch(Promise.resolve(reactResponse)));
|
|
1087
1139
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
1088
1140
|
let committedState = null;
|
|
1089
|
-
if (await renderNavigationPayload(
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1141
|
+
if (await renderNavigationPayload({
|
|
1142
|
+
actionType: toActionType(navigationKind),
|
|
1143
|
+
historyUpdateMode: currentHistoryMode,
|
|
1144
|
+
navigationCommitKind: detachedNavigationCommits ? "authoritative" : void 0,
|
|
1145
|
+
navigationInitiationState,
|
|
1146
|
+
navigationSnapshot,
|
|
1147
|
+
navId,
|
|
1148
|
+
onCommittedState: (state) => {
|
|
1149
|
+
committedState = state;
|
|
1150
|
+
if (activeNavigationAbortController === navigationAbortController) activeNavigationAbortController = null;
|
|
1151
|
+
},
|
|
1152
|
+
operationLane: toOperationLane(navigationKind),
|
|
1153
|
+
params: navParams,
|
|
1154
|
+
payload: rscPayload,
|
|
1155
|
+
payloadOrigin: FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN,
|
|
1156
|
+
pendingRouterState: detachedNavigationCommits ? null : pendingRouterState,
|
|
1157
|
+
previousNextUrl: requestPreviousNextUrl,
|
|
1158
|
+
restoredBfcacheIds,
|
|
1159
|
+
reuseCurrentBfcacheIds,
|
|
1160
|
+
scrollIntent,
|
|
1161
|
+
targetHref: currentHref,
|
|
1162
|
+
traversalIntent: activeTraversalIntent,
|
|
1163
|
+
visibleCommitMode
|
|
1164
|
+
}) !== "committed") return;
|
|
1093
1165
|
try {
|
|
1094
1166
|
const renderedElements = await rscPayload;
|
|
1095
1167
|
if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
|
|
@@ -43,6 +43,28 @@ type BrowserNavigationControllerDeps = {
|
|
|
43
43
|
getRouteManifest?: () => RouteManifest | null;
|
|
44
44
|
syncHistoryStatePreviousNextUrl?: (previousNextUrl: string | null, bfcacheIds?: Readonly<Record<string, string>> | null) => void;
|
|
45
45
|
};
|
|
46
|
+
type BrowserNavigationPayloadOptions = {
|
|
47
|
+
actionType: "navigate" | "replace" | "traverse";
|
|
48
|
+
createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
|
|
49
|
+
historyUpdateMode: HistoryUpdateMode | undefined;
|
|
50
|
+
navigationCommitKind?: "authoritative" | "detached";
|
|
51
|
+
navigationInitiationState: AppRouterState;
|
|
52
|
+
navigationSnapshot: ClientNavigationRenderSnapshot;
|
|
53
|
+
navId: number;
|
|
54
|
+
nextElements: Promise<AppElements>;
|
|
55
|
+
onCommittedState?: (state: AppRouterState) => void;
|
|
56
|
+
operationLane: OperationLane;
|
|
57
|
+
params: Record<string, string | string[]>;
|
|
58
|
+
payloadOrigin: AppNavigationPayloadOrigin;
|
|
59
|
+
pendingRouterState: PendingBrowserRouterState | null;
|
|
60
|
+
previousNextUrl: string | null;
|
|
61
|
+
restoredBfcacheIds?: Readonly<Record<string, string>> | null;
|
|
62
|
+
reuseCurrentBfcacheIds?: boolean;
|
|
63
|
+
scrollIntent?: AppRouterScrollIntent | null;
|
|
64
|
+
targetHistoryIndex?: number | null;
|
|
65
|
+
targetHref: string;
|
|
66
|
+
visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
|
|
67
|
+
};
|
|
46
68
|
type BrowserNavigationController = {
|
|
47
69
|
beginNavigation(): number;
|
|
48
70
|
getActiveNavigationId(): number;
|
|
@@ -60,27 +82,7 @@ type BrowserNavigationController = {
|
|
|
60
82
|
state: AppRouterState;
|
|
61
83
|
targetHref: string;
|
|
62
84
|
}): boolean;
|
|
63
|
-
renderNavigationPayload(options:
|
|
64
|
-
actionType: "navigate" | "replace" | "traverse";
|
|
65
|
-
createNavigationCommitEffect: BrowserNavigationCommitEffectFactory;
|
|
66
|
-
historyUpdateMode: HistoryUpdateMode | undefined;
|
|
67
|
-
navigationSnapshot: ClientNavigationRenderSnapshot;
|
|
68
|
-
nextElements: Promise<AppElements>;
|
|
69
|
-
operationLane: OperationLane;
|
|
70
|
-
payloadOrigin: AppNavigationPayloadOrigin;
|
|
71
|
-
params: Record<string, string | string[]>;
|
|
72
|
-
pendingRouterState: PendingBrowserRouterState | null;
|
|
73
|
-
previousNextUrl: string | null;
|
|
74
|
-
scrollIntent?: AppRouterScrollIntent | null;
|
|
75
|
-
restoredBfcacheIds?: Readonly<Record<string, string>> | null;
|
|
76
|
-
reuseCurrentBfcacheIds?: boolean;
|
|
77
|
-
targetHistoryIndex?: number | null;
|
|
78
|
-
targetHref: string;
|
|
79
|
-
navId: number;
|
|
80
|
-
navigationCommitKind?: "authoritative" | "detached";
|
|
81
|
-
visibleCommitMode?: NavigationRuntimeVisibleCommitMode;
|
|
82
|
-
onCommittedState?: (state: AppRouterState) => void;
|
|
83
|
-
}): Promise<NavigationPayloadOutcome>;
|
|
85
|
+
renderNavigationPayload(options: BrowserNavigationPayloadOptions): Promise<NavigationPayloadOutcome>;
|
|
84
86
|
commitSameUrlNavigatePayload(nextElements: Promise<AppElements>, navigationSnapshot: ClientNavigationRenderSnapshot, returnValue?: {
|
|
85
87
|
ok: boolean;
|
|
86
88
|
data: unknown;
|
|
@@ -349,7 +349,7 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
349
349
|
let snapshotActivated = false;
|
|
350
350
|
try {
|
|
351
351
|
const pending = await createPendingNavigationCommit({
|
|
352
|
-
currentState:
|
|
352
|
+
currentState: options.navigationInitiationState,
|
|
353
353
|
navigationCommitKind: options.navigationCommitKind,
|
|
354
354
|
navigationId: options.navId,
|
|
355
355
|
nextElements: options.nextElements,
|