vinext 1.0.0-beta.2 → 1.0.0-beta.4
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/build/precompress.js +1 -1
- package/dist/build/prerender.d.ts +2 -1
- package/dist/build/prerender.js +12 -2
- package/dist/check.js +4 -0
- package/dist/cli-args.d.ts +2 -1
- package/dist/cli-args.js +10 -0
- package/dist/cli.js +14 -8
- package/dist/client/navigation-runtime.d.ts +6 -0
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/config/config-matchers.js +4 -1
- package/dist/config/dotenv.d.ts +3 -4
- package/dist/config/dotenv.js +30 -22
- package/dist/config/next-config.d.ts +16 -5
- package/dist/config/next-config.js +7 -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 +21 -3
- 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 +13 -3
- package/dist/index.js +122 -49
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/plugins/client-reference-dedup.js +7 -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/sass.js +3 -4
- 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 +7 -6
- package/dist/server/app-bfcache-identity.js +28 -2
- package/dist/server/app-browser-entry.js +175 -75
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +30 -5
- package/dist/server/app-browser-prefetch-response.d.ts +9 -0
- package/dist/server/app-browser-prefetch-response.js +32 -0
- package/dist/server/app-browser-state.d.ts +8 -3
- package/dist/server/app-browser-state.js +9 -3
- 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 +9 -1
- package/dist/server/app-page-dispatch.js +37 -8
- package/dist/server/app-page-element-builder.d.ts +4 -1
- package/dist/server/app-page-element-builder.js +34 -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-render.d.ts +1 -1
- package/dist/server/app-page-render.js +13 -3
- package/dist/server/app-page-request.d.ts +3 -1
- package/dist/server/app-page-request.js +4 -13
- package/dist/server/app-page-response.d.ts +2 -0
- package/dist/server/app-page-response.js +7 -2
- package/dist/server/app-page-route-wiring.d.ts +10 -2
- package/dist/server/app-page-route-wiring.js +269 -59
- package/dist/server/app-pages-bridge.d.ts +2 -1
- package/dist/server/app-pages-bridge.js +3 -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-cache-busting.d.ts +7 -0
- package/dist/server/app-rsc-cache-busting.js +6 -0
- package/dist/server/app-rsc-errors.js +10 -0
- package/dist/server/app-rsc-handler.d.ts +2 -0
- package/dist/server/app-rsc-handler.js +30 -13
- package/dist/server/app-rsc-render-mode.d.ts +3 -2
- package/dist/server/app-rsc-render-mode.js +4 -1
- package/dist/server/app-rsc-request-normalization.js +65 -4
- 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-server-action-execution.d.ts +1 -1
- package/dist/server/app-ssr-entry.js +4 -4
- package/dist/server/app-visited-response-cache.d.ts +6 -1
- package/dist/server/app-visited-response-cache.js +49 -1
- 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 +204 -348
- package/dist/server/edge-api-runtime.d.ts +10 -1
- package/dist/server/edge-api-runtime.js +29 -1
- package/dist/server/headers.d.ts +4 -2
- package/dist/server/headers.js +6 -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 +4 -0
- package/dist/server/pages-api-route.js +6 -3
- package/dist/server/pages-asset-tags.d.ts +1 -0
- package/dist/server/pages-asset-tags.js +6 -4
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-document-asset-props.d.ts +26 -0
- package/dist/server/pages-document-asset-props.js +69 -0
- 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 +10 -6
- 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 +6 -1
- package/dist/server/pages-page-handler.js +119 -20
- package/dist/server/pages-page-response.d.ts +7 -3
- package/dist/server/pages-page-response.js +41 -33
- 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/prerender-manifest.d.ts +1 -0
- package/dist/server/prod-server.d.ts +2 -1
- package/dist/server/prod-server.js +38 -13
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/seed-cache.js +1 -1
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +30 -0
- package/dist/shims/app-router-scroll-state.d.ts +2 -1
- package/dist/shims/app-router-scroll-state.js +7 -2
- 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/document.js +15 -2
- 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/hash-scroll.d.ts +1 -1
- package/dist/shims/hash-scroll.js +2 -1
- 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.d.ts +2 -0
- package/dist/shims/link.js +77 -24
- package/dist/shims/metadata.d.ts +18 -34
- package/dist/shims/metadata.js +98 -56
- package/dist/shims/navigation.d.ts +18 -2
- package/dist/shims/navigation.js +70 -21
- package/dist/shims/request-context.d.ts +6 -2
- 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/slot.d.ts +2 -1
- package/dist/shims/slot.js +6 -2
- package/dist/shims/streamed-icons.d.ts +9 -0
- package/dist/shims/streamed-icons.js +33 -0
- 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
|
@@ -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
|
|
@@ -9,7 +9,7 @@ import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-medi
|
|
|
9
9
|
import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
|
|
10
10
|
import { performOnDemandRevalidate } from "./pages-revalidate.js";
|
|
11
11
|
import { attachPagesPreviewApi, attachPagesRequestCookies } from "./pages-node-compat.js";
|
|
12
|
-
import { isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
12
|
+
import { finalizeEdgeApiResponse, isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
13
13
|
import { decode } from "node:querystring";
|
|
14
14
|
import { Buffer } from "node:buffer";
|
|
15
15
|
//#region src/server/api-handler.ts
|
|
@@ -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);
|
|
@@ -227,8 +227,9 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
|
|
|
227
227
|
i18n: nextConfig.i18n ?? void 0,
|
|
228
228
|
trailingSlash: nextConfig.trailingSlash
|
|
229
229
|
} } : void 0);
|
|
230
|
-
const
|
|
231
|
-
if (!(
|
|
230
|
+
const handlerResponse = await apiModule.default(nextRequest);
|
|
231
|
+
if (!(handlerResponse instanceof Response)) throw new Error("Edge API route did not return a Response");
|
|
232
|
+
const response = finalizeEdgeApiResponse(handlerResponse, "node");
|
|
232
233
|
res.statusCode = response.status;
|
|
233
234
|
res.statusMessage = response.statusText;
|
|
234
235
|
const setCookieHeaders = response.headers.getSetCookie();
|
|
@@ -248,7 +249,7 @@ async function handleApiRoute(runner, req, res, url, apiRoutes, nextConfig) {
|
|
|
248
249
|
}
|
|
249
250
|
const query = mergeRouteParamsIntoQuery(parseQueryString(url), params);
|
|
250
251
|
const bodyParserConfig = resolveBodyParserConfig(apiModule.config);
|
|
251
|
-
const { apiReq, apiRes } = enhanceApiObjects(req, res, query, bodyParserConfig.enabled ? await parseBody(req, bodyParserConfig.sizeLimit) : void 0);
|
|
252
|
+
const { apiReq, apiRes } = enhanceApiObjects(req, res, query, bodyParserConfig.enabled ? await parseBody(req, bodyParserConfig.sizeLimit) : void 0, nextConfig?.trustedRevalidateOrigin, nextConfig?.allowedRevalidateHeaderKeys, true);
|
|
252
253
|
await handler(apiReq, apiRes);
|
|
253
254
|
return true;
|
|
254
255
|
} catch (e) {
|
|
@@ -51,19 +51,45 @@ function createActiveSlotIdentity(id, parsed) {
|
|
|
51
51
|
if (interception?.slotId !== id) return null;
|
|
52
52
|
return `${id}@${interception.targetRouteId}`;
|
|
53
53
|
}
|
|
54
|
+
function getDirectMatchedRoutePathname(metadata) {
|
|
55
|
+
if (metadata?.metadata.interception != null || metadata?.metadata.sourcePage == null) return null;
|
|
56
|
+
const route = AppElementsWire.parseElementKey(metadata.metadata.routeId);
|
|
57
|
+
return route?.kind === "route" ? route.path : null;
|
|
58
|
+
}
|
|
59
|
+
function getPageMatchedUrl(parsed, metadata) {
|
|
60
|
+
const interception = metadata?.metadata.interception;
|
|
61
|
+
if (interception === null || interception === void 0) {
|
|
62
|
+
const matchedRoutePathname = getDirectMatchedRoutePathname(metadata);
|
|
63
|
+
return matchedRoutePathname === parsed.path ? matchedRoutePathname : null;
|
|
64
|
+
}
|
|
65
|
+
for (const [routeId, matchedUrl] of [[interception.sourceRouteId, interception.sourceMatchedUrl], [interception.targetRouteId, interception.targetMatchedUrl]]) {
|
|
66
|
+
const route = AppElementsWire.parseElementKey(routeId);
|
|
67
|
+
if (route?.kind === "route" && route.path === parsed.path) return matchedUrl;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function getTreePathname(treePath, metadata) {
|
|
72
|
+
const interception = metadata?.metadata.interception;
|
|
73
|
+
if (interception === null || interception === void 0) return getDirectMatchedRoutePathname(metadata);
|
|
74
|
+
const slot = AppElementsWire.parseElementKey(interception.slotId);
|
|
75
|
+
if (slot?.kind !== "slot") return interception.sourceMatchedUrl;
|
|
76
|
+
const treeSegments = splitPathSegments(treePath);
|
|
77
|
+
const ownerSegments = splitPathSegments(slot.treePath);
|
|
78
|
+
return treeSegments.length > ownerSegments.length && ownerSegments.every((segment, index) => treeSegments[index] === segment) && treeSegments[ownerSegments.length] === `@${slot.name}` ? interception.targetMatchedUrl : interception.sourceMatchedUrl;
|
|
79
|
+
}
|
|
54
80
|
/**
|
|
55
81
|
* Derive BFCache identity from AppElements wire keys. Keep wire-key parsing
|
|
56
82
|
* contained here until vinext has a route-manifest authority equivalent to
|
|
57
83
|
* Next.js CacheNode or segment-cache state.
|
|
58
84
|
*/
|
|
59
85
|
function createBfcacheSegmentIdentity(id, parsed, options) {
|
|
60
|
-
if (parsed.kind === "page") return `${id}@${options.pathname}`;
|
|
86
|
+
if (parsed.kind === "page") return `${id}@${getPageMatchedUrl(parsed, options.metadata) ?? options.pathname}`;
|
|
61
87
|
if (parsed.kind === "slot") {
|
|
62
88
|
const activeSlotIdentity = createActiveSlotIdentity(id, options.metadata);
|
|
63
89
|
if (activeSlotIdentity !== null) return activeSlotIdentity;
|
|
64
90
|
return `${id}@${getTreePathIdentityPrefix(options.pathname, parsed.treePath)}`;
|
|
65
91
|
}
|
|
66
|
-
if (parsed.kind === "layout" || parsed.kind === "template") return `${id}@${getTreePathIdentityPrefix(options.pathname, parsed.treePath)}`;
|
|
92
|
+
if (parsed.kind === "layout" || parsed.kind === "template") return `${id}@${getTreePathIdentityPrefix(getTreePathname(parsed.treePath, options.metadata) ?? options.pathname, parsed.treePath)}`;
|
|
67
93
|
return null;
|
|
68
94
|
}
|
|
69
95
|
function collectBfcacheSegmentIdCandidates(elements, metadata = readAppElementsMetadata(elements)) {
|