vinext 0.1.0 → 0.1.2
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 +2 -5
- package/dist/build/assets-ignore.d.ts +32 -0
- package/dist/build/assets-ignore.js +48 -0
- package/dist/build/client-build-config.d.ts +33 -1
- package/dist/build/client-build-config.js +66 -1
- package/dist/check.js +4 -3
- package/dist/cli.js +2 -0
- package/dist/client/navigation-runtime.d.ts +11 -2
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/client/vinext-next-data.d.ts +2 -1
- package/dist/client/window-next.d.ts +6 -4
- package/dist/config/config-matchers.d.ts +31 -5
- package/dist/config/config-matchers.js +50 -3
- package/dist/config/next-config.d.ts +29 -3
- package/dist/config/next-config.js +32 -2
- package/dist/deploy.js +47 -304
- package/dist/entries/app-rsc-entry.d.ts +8 -2
- package/dist/entries/app-rsc-entry.js +61 -5
- package/dist/entries/app-rsc-manifest.js +20 -2
- package/dist/entries/pages-client-entry.js +1 -1
- package/dist/entries/pages-server-entry.js +16 -7
- package/dist/index.d.ts +0 -2
- package/dist/index.js +233 -280
- package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
- package/dist/plugins/dynamic-preload-metadata.js +415 -0
- package/dist/plugins/og-assets.js +2 -2
- package/dist/plugins/optimize-imports.d.ts +8 -4
- package/dist/plugins/optimize-imports.js +16 -12
- package/dist/plugins/postcss.js +18 -14
- package/dist/plugins/require-context.d.ts +6 -0
- package/dist/plugins/require-context.js +184 -0
- package/dist/plugins/sass.d.ts +53 -24
- package/dist/plugins/sass.js +249 -1
- package/dist/plugins/wasm-module-import.d.ts +15 -0
- package/dist/plugins/wasm-module-import.js +50 -0
- package/dist/routing/app-route-graph.d.ts +35 -2
- package/dist/routing/app-route-graph.js +179 -8
- package/dist/routing/file-matcher.js +1 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +16 -1
- package/dist/server/api-handler.js +4 -0
- package/dist/server/app-browser-entry.js +155 -215
- package/dist/server/app-browser-error.d.ts +4 -1
- package/dist/server/app-browser-error.js +7 -1
- package/dist/server/app-browser-history-controller.d.ts +104 -0
- package/dist/server/app-browser-history-controller.js +210 -0
- package/dist/server/app-browser-interception-context.d.ts +2 -1
- package/dist/server/app-browser-interception-context.js +15 -2
- package/dist/server/app-browser-navigation-controller.d.ts +13 -2
- package/dist/server/app-browser-navigation-controller.js +83 -4
- package/dist/server/app-browser-popstate.d.ts +12 -3
- package/dist/server/app-browser-popstate.js +19 -4
- package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
- package/dist/server/app-browser-rsc-redirect.js +30 -8
- package/dist/server/app-browser-state.d.ts +3 -0
- package/dist/server/app-browser-state.js +10 -10
- package/dist/server/app-browser-visible-commit.js +10 -8
- package/dist/server/app-fallback-renderer.d.ts +2 -1
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-history-state.d.ts +45 -1
- package/dist/server/app-history-state.js +109 -1
- package/dist/server/app-middleware.js +1 -0
- package/dist/server/app-optimistic-routing.js +22 -1
- package/dist/server/app-page-boundary-render.d.ts +2 -1
- package/dist/server/app-page-boundary-render.js +45 -21
- package/dist/server/app-page-cache.js +9 -7
- package/dist/server/app-page-dispatch.d.ts +14 -0
- package/dist/server/app-page-dispatch.js +21 -6
- package/dist/server/app-page-element-builder.d.ts +23 -2
- package/dist/server/app-page-element-builder.js +58 -17
- package/dist/server/app-page-execution.d.ts +1 -1
- package/dist/server/app-page-execution.js +32 -17
- package/dist/server/app-page-render.d.ts +7 -1
- package/dist/server/app-page-render.js +11 -16
- package/dist/server/app-page-request.d.ts +9 -6
- package/dist/server/app-page-request.js +14 -10
- package/dist/server/app-page-response.d.ts +2 -2
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +10 -8
- package/dist/server/app-page-stream.d.ts +37 -7
- package/dist/server/app-page-stream.js +36 -6
- package/dist/server/app-pages-bridge.d.ts +16 -0
- package/dist/server/app-pages-bridge.js +23 -3
- package/dist/server/app-route-handler-cache.d.ts +1 -0
- package/dist/server/app-route-handler-cache.js +1 -0
- package/dist/server/app-route-handler-dispatch.d.ts +1 -0
- package/dist/server/app-route-handler-dispatch.js +2 -0
- package/dist/server/app-route-handler-execution.d.ts +1 -0
- package/dist/server/app-route-handler-execution.js +1 -0
- package/dist/server/app-route-handler-response.js +11 -10
- package/dist/server/app-route-handler-runtime.d.ts +1 -0
- package/dist/server/app-route-handler-runtime.js +15 -3
- package/dist/server/app-rsc-handler.d.ts +1 -0
- package/dist/server/app-rsc-handler.js +5 -4
- package/dist/server/app-rsc-response-finalizer.js +1 -1
- package/dist/server/app-rsc-route-matching.d.ts +20 -1
- package/dist/server/app-rsc-route-matching.js +29 -4
- package/dist/server/app-server-action-execution.d.ts +22 -1
- package/dist/server/app-server-action-execution.js +73 -12
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +19 -3
- package/dist/server/app-ssr-stream.js +9 -1
- package/dist/server/dev-lockfile.js +2 -1
- package/dist/server/dev-server.d.ts +1 -1
- package/dist/server/dev-server.js +97 -43
- package/dist/server/headers.d.ts +8 -1
- package/dist/server/headers.js +8 -1
- package/dist/server/instrumentation-runtime.d.ts +6 -0
- package/dist/server/instrumentation-runtime.js +8 -0
- package/dist/server/isr-cache.d.ts +37 -1
- package/dist/server/isr-cache.js +85 -1
- package/dist/server/isr-decision.d.ts +79 -0
- package/dist/server/isr-decision.js +70 -0
- package/dist/server/metadata-route-response.js +5 -3
- package/dist/server/middleware-runtime.d.ts +13 -0
- package/dist/server/middleware-runtime.js +11 -7
- package/dist/server/middleware.js +1 -0
- package/dist/server/navigation-planner.d.ts +62 -1
- package/dist/server/navigation-planner.js +193 -3
- package/dist/server/navigation-trace.d.ts +12 -2
- package/dist/server/navigation-trace.js +11 -1
- package/dist/server/normalize-path.d.ts +0 -8
- package/dist/server/normalize-path.js +3 -1
- package/dist/server/otel-tracer-extension.d.ts +45 -0
- package/dist/server/otel-tracer-extension.js +89 -0
- package/dist/server/pages-api-route.d.ts +14 -3
- package/dist/server/pages-api-route.js +6 -1
- package/dist/server/pages-asset-tags.d.ts +15 -4
- package/dist/server/pages-asset-tags.js +18 -12
- package/dist/server/pages-data-route.js +5 -1
- package/dist/server/pages-node-compat.d.ts +5 -11
- package/dist/server/pages-node-compat.js +175 -118
- package/dist/server/pages-page-data.d.ts +38 -7
- package/dist/server/pages-page-data.js +64 -18
- package/dist/server/pages-page-handler.d.ts +10 -2
- package/dist/server/pages-page-handler.js +49 -20
- package/dist/server/pages-page-response.d.ts +55 -2
- package/dist/server/pages-page-response.js +74 -6
- package/dist/server/pages-readiness.d.ts +36 -0
- package/dist/server/pages-readiness.js +21 -0
- package/dist/server/pages-request-pipeline.d.ts +113 -0
- package/dist/server/pages-request-pipeline.js +230 -0
- package/dist/server/pages-revalidate.d.ts +15 -0
- package/dist/server/pages-revalidate.js +19 -0
- package/dist/server/prod-server.d.ts +45 -3
- package/dist/server/prod-server.js +182 -234
- package/dist/server/socket-error-backstop.d.ts +19 -1
- package/dist/server/socket-error-backstop.js +77 -4
- package/dist/shims/app-router-scroll.js +22 -4
- package/dist/shims/cache-runtime.js +39 -2
- package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
- package/dist/shims/dynamic-preload-chunks.js +77 -0
- package/dist/shims/dynamic.d.ts +4 -0
- package/dist/shims/dynamic.js +4 -2
- package/dist/shims/error-boundary.d.ts +17 -7
- package/dist/shims/error-boundary.js +8 -1
- package/dist/shims/error.js +37 -11
- package/dist/shims/fetch-cache.d.ts +22 -1
- package/dist/shims/fetch-cache.js +28 -1
- package/dist/shims/hash-scroll.d.ts +1 -0
- package/dist/shims/hash-scroll.js +3 -1
- package/dist/shims/head.js +6 -1
- package/dist/shims/headers.d.ts +16 -2
- package/dist/shims/headers.js +37 -1
- package/dist/shims/image-config.js +7 -1
- package/dist/shims/internal/app-route-detection.d.ts +6 -3
- package/dist/shims/internal/app-route-detection.js +10 -6
- package/dist/shims/internal/app-router-context.d.ts +5 -0
- package/dist/shims/internal/link-status-registry.d.ts +43 -0
- package/dist/shims/internal/link-status-registry.js +42 -0
- package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
- package/dist/shims/internal/route-pattern-for-warning.js +40 -0
- package/dist/shims/internal/utils.d.ts +1 -0
- package/dist/shims/link.js +20 -6
- package/dist/shims/metadata.d.ts +6 -2
- package/dist/shims/metadata.js +32 -14
- package/dist/shims/navigation.d.ts +9 -18
- package/dist/shims/navigation.js +96 -23
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router-state.js +2 -0
- package/dist/shims/router.d.ts +6 -3
- package/dist/shims/router.js +156 -22
- package/dist/shims/script-nonce-context.d.ts +1 -1
- package/dist/shims/script-nonce-context.js +11 -3
- package/dist/shims/server.d.ts +17 -1
- package/dist/shims/server.js +31 -6
- package/dist/shims/slot.js +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -0
- package/dist/utils/client-build-manifest.d.ts +8 -1
- package/dist/utils/client-build-manifest.js +41 -6
- package/dist/utils/client-entry-manifest.d.ts +11 -0
- package/dist/utils/client-entry-manifest.js +29 -0
- package/dist/utils/client-runtime-metadata.d.ts +45 -0
- package/dist/utils/client-runtime-metadata.js +63 -0
- package/dist/utils/hash.d.ts +17 -1
- package/dist/utils/hash.js +36 -1
- package/dist/utils/lazy-chunks.d.ts +27 -1
- package/dist/utils/lazy-chunks.js +65 -1
- package/dist/utils/manifest-paths.d.ts +20 -2
- package/dist/utils/manifest-paths.js +38 -3
- package/dist/utils/path.d.ts +2 -1
- package/dist/utils/path.js +5 -1
- package/package.json +6 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizePathSeparators } from "../utils/path.js";
|
|
1
2
|
import { compareRoutes, decodeRouteSegment, isInvisibleSegment } from "./utils.js";
|
|
2
3
|
import { findFileWithExts, scanWithExtensions } from "./file-matcher.js";
|
|
3
4
|
import { validateRoutePatterns } from "./route-validation.js";
|
|
@@ -33,6 +34,10 @@ function createAppRouteGraphSlotId(slotName, ownerTreePath) {
|
|
|
33
34
|
function createAppRouteGraphDefaultId(slotId) {
|
|
34
35
|
return `default:${slotId}`;
|
|
35
36
|
}
|
|
37
|
+
const SIBLING_INTERCEPT_SLOT_NAME = "__vinext_sibling_intercept";
|
|
38
|
+
function createAppRouteGraphSiblingInterceptSlotId(sourcePattern) {
|
|
39
|
+
return createAppRouteGraphSlotId(SIBLING_INTERCEPT_SLOT_NAME, sourcePattern);
|
|
40
|
+
}
|
|
36
41
|
function createAppRouteGraphInterceptionId(slotId, sourcePattern, targetPattern) {
|
|
37
42
|
return `interception:${slotId}:${sourcePattern}->${targetPattern}`;
|
|
38
43
|
}
|
|
@@ -182,6 +187,21 @@ function createStaticSegmentGraph(routes) {
|
|
|
182
187
|
slot
|
|
183
188
|
});
|
|
184
189
|
}
|
|
190
|
+
for (const ir of route.siblingIntercepts) {
|
|
191
|
+
if (!ir.slotId) continue;
|
|
192
|
+
const id = createAppRouteGraphInterceptionId(ir.slotId, ir.sourceMatchPattern, ir.targetPattern);
|
|
193
|
+
interceptions.set(id, {
|
|
194
|
+
id,
|
|
195
|
+
sourcePattern: ir.sourceMatchPattern,
|
|
196
|
+
sourcePatternParts: splitRouteManifestPatternParts(ir.sourceMatchPattern),
|
|
197
|
+
targetPattern: ir.targetPattern,
|
|
198
|
+
targetPatternParts: splitRouteManifestPatternParts(ir.targetPattern),
|
|
199
|
+
slotId: ir.slotId,
|
|
200
|
+
ownerLayoutId: null,
|
|
201
|
+
interceptingRouteId: routeIdByPattern.get(ir.sourceMatchPattern) ?? null,
|
|
202
|
+
targetRouteId: routeIdByPattern.get(ir.targetPattern) ?? null
|
|
203
|
+
});
|
|
204
|
+
}
|
|
185
205
|
}
|
|
186
206
|
return {
|
|
187
207
|
routes: routeEntries,
|
|
@@ -353,9 +373,10 @@ async function buildAppRouteGraph(appDir, matcher) {
|
|
|
353
373
|
}
|
|
354
374
|
const slotSubRoutes = discoverSlotSubRoutes(routes, matcher, ghostParentRoutes);
|
|
355
375
|
routes.push(...slotSubRoutes);
|
|
376
|
+
discoverSiblingInterceptingRoutes(routes, appDir, matcher);
|
|
356
377
|
validatePageRouteConflicts(routes, appDir);
|
|
357
378
|
validateRoutePatterns(routes.map((route) => route.pattern));
|
|
358
|
-
validateRoutePatterns([...new Set(routes.flatMap((route) => route.parallelSlots.flatMap((slot) => slot.interceptingRoutes.map((intercept) => intercept.targetPattern))))]);
|
|
379
|
+
validateRoutePatterns([...new Set(routes.flatMap((route) => [...route.parallelSlots.flatMap((slot) => slot.interceptingRoutes.map((intercept) => intercept.targetPattern)), ...route.siblingIntercepts.map((intercept) => intercept.targetPattern)]))]);
|
|
359
380
|
routes.sort(compareRoutes);
|
|
360
381
|
return {
|
|
361
382
|
routes,
|
|
@@ -456,6 +477,8 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
456
477
|
if (subPathMap.size === 0) continue;
|
|
457
478
|
const childrenDefault = findFile(parentPageDir, "default", matcher);
|
|
458
479
|
if (parentRoute.pagePath && !childrenDefault) continue;
|
|
480
|
+
const childrenCatchAll = childrenDefault ? null : findCatchAllPage(parentPageDir, matcher);
|
|
481
|
+
const childrenFallback = childrenDefault ?? childrenCatchAll;
|
|
459
482
|
for (const { rawSegments, converted: convertedSubRoute, slotPages } of subPathMap.values()) {
|
|
460
483
|
const { urlSegments: urlParts, params: subParams, isDynamic: subIsDynamic } = convertedSubRoute;
|
|
461
484
|
const subUrlPath = urlParts.join("/");
|
|
@@ -463,7 +486,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
463
486
|
const existingRoute = routesByPattern.get(pattern);
|
|
464
487
|
if (existingRoute) {
|
|
465
488
|
if (existingRoute.routePath && !existingRoute.pagePath) throw new Error(`You cannot have two routes that resolve to the same path ("${pattern}").`);
|
|
466
|
-
applySlotSubPages(existingRoute, slotPages, rawSegments);
|
|
489
|
+
if (urlParts.length > 0) applySlotSubPages(existingRoute, slotPages, rawSegments);
|
|
467
490
|
continue;
|
|
468
491
|
}
|
|
469
492
|
const syntheticParts = [...parentRoute.patternParts, ...urlParts];
|
|
@@ -479,7 +502,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
479
502
|
const newRoute = {
|
|
480
503
|
ids: createAppRouteSemanticIds({
|
|
481
504
|
pattern,
|
|
482
|
-
pagePath:
|
|
505
|
+
pagePath: childrenFallback,
|
|
483
506
|
routePath: null,
|
|
484
507
|
routeSegments: [...parentRoute.routeSegments, ...rawSegments],
|
|
485
508
|
layoutTreePositions: parentRoute.layoutTreePositions,
|
|
@@ -487,7 +510,7 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
487
510
|
slots: subSlots
|
|
488
511
|
}),
|
|
489
512
|
pattern,
|
|
490
|
-
pagePath:
|
|
513
|
+
pagePath: childrenFallback,
|
|
491
514
|
routePath: null,
|
|
492
515
|
layouts: parentRoute.layouts,
|
|
493
516
|
templates: parentRoute.templates,
|
|
@@ -507,7 +530,8 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
507
530
|
isDynamic: parentRoute.isDynamic || subIsDynamic,
|
|
508
531
|
params: [...parentRoute.params, ...subParams],
|
|
509
532
|
rootParamNames: parentRoute.rootParamNames,
|
|
510
|
-
patternParts: [...parentRoute.patternParts, ...urlParts]
|
|
533
|
+
patternParts: [...parentRoute.patternParts, ...urlParts],
|
|
534
|
+
siblingIntercepts: []
|
|
511
535
|
};
|
|
512
536
|
syntheticRoutes.push(newRoute);
|
|
513
537
|
routesByPattern.set(pattern, newRoute);
|
|
@@ -549,6 +573,34 @@ function findSlotSubPages(slotDir, matcher) {
|
|
|
549
573
|
return results;
|
|
550
574
|
}
|
|
551
575
|
/**
|
|
576
|
+
* Find a sibling catch-all page directly under `dir`, i.e. a `[...slug]` or
|
|
577
|
+
* `[[...slug]]` directory that contains a `page` file. Returns the absolute
|
|
578
|
+
* page path, or null when no catch-all sibling exists.
|
|
579
|
+
*
|
|
580
|
+
* Used as the children fallback for slot-only sub-routes (an explicit `@slot`
|
|
581
|
+
* sub-page with no corresponding children page or `default.tsx`): Next.js
|
|
582
|
+
* serves the children prop from the nearest catch-all, so `/baz` renders
|
|
583
|
+
* `[...catchAll]/page.tsx` for children while `@slot/baz/page.tsx` fills the
|
|
584
|
+
* slot. Optional catch-alls (`[[...slug]]`) qualify because they also match a
|
|
585
|
+
* single extra segment.
|
|
586
|
+
*/
|
|
587
|
+
function findCatchAllPage(dir, matcher) {
|
|
588
|
+
let entries;
|
|
589
|
+
try {
|
|
590
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
591
|
+
} catch {
|
|
592
|
+
return null;
|
|
593
|
+
}
|
|
594
|
+
for (const entry of entries) {
|
|
595
|
+
if (!entry.isDirectory()) continue;
|
|
596
|
+
const name = entry.name;
|
|
597
|
+
if (!(name.startsWith("[...") && name.endsWith("]") || name.startsWith("[[...") && name.endsWith("]]"))) continue;
|
|
598
|
+
const page = findFile(path.join(dir, name), "page", matcher);
|
|
599
|
+
if (page) return page;
|
|
600
|
+
}
|
|
601
|
+
return null;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
552
604
|
* Convert a file path relative to app/ into an AppRoute.
|
|
553
605
|
*/
|
|
554
606
|
function fileToAppRoute(file, appDir, type, matcher) {
|
|
@@ -620,7 +672,8 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath) {
|
|
|
620
672
|
isDynamic,
|
|
621
673
|
params,
|
|
622
674
|
rootParamNames: computeRootParamNames(segments, layoutTreePositions),
|
|
623
|
-
patternParts: urlSegments
|
|
675
|
+
patternParts: urlSegments,
|
|
676
|
+
siblingIntercepts: []
|
|
624
677
|
};
|
|
625
678
|
}
|
|
626
679
|
function dynamicParamNameFromSegment(segment) {
|
|
@@ -974,6 +1027,34 @@ function patternsStructurallyEquivalent(a, b) {
|
|
|
974
1027
|
return true;
|
|
975
1028
|
}
|
|
976
1029
|
/**
|
|
1030
|
+
* Find a page file at the root URL level of a parallel slot directory, including
|
|
1031
|
+
* through transparent route-group subdirectories (e.g. `@slot/(group)/page.tsx`
|
|
1032
|
+
* is equivalent to `@slot/page.tsx` since `(group)` is invisible in the URL).
|
|
1033
|
+
*
|
|
1034
|
+
* Returns the absolute page path, or null if no root-level page is found.
|
|
1035
|
+
*
|
|
1036
|
+
* Only descends into route-group directories (those whose name starts with `(`
|
|
1037
|
+
* and ends with `)`). Dynamic segments, regular named dirs, and `@slot` dirs
|
|
1038
|
+
* are not transparent and are therefore not searched.
|
|
1039
|
+
*/
|
|
1040
|
+
function findSlotRootPage(slotDir, matcher) {
|
|
1041
|
+
const directPage = findFile(slotDir, "page", matcher);
|
|
1042
|
+
if (directPage) return directPage;
|
|
1043
|
+
let entries;
|
|
1044
|
+
try {
|
|
1045
|
+
entries = fs.readdirSync(slotDir, { withFileTypes: true });
|
|
1046
|
+
} catch {
|
|
1047
|
+
return null;
|
|
1048
|
+
}
|
|
1049
|
+
for (const entry of entries) {
|
|
1050
|
+
if (!entry.isDirectory()) continue;
|
|
1051
|
+
if (!entry.name.startsWith("(") || !entry.name.endsWith(")")) continue;
|
|
1052
|
+
const found = findSlotRootPage(path.join(slotDir, entry.name), matcher);
|
|
1053
|
+
if (found) return found;
|
|
1054
|
+
}
|
|
1055
|
+
return null;
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
977
1058
|
* Discover parallel route slots (@team, @analytics, etc.) in a directory.
|
|
978
1059
|
* Returns a ParallelSlot for each @-prefixed subdirectory that has a page or default component.
|
|
979
1060
|
*/
|
|
@@ -986,7 +1067,7 @@ function discoverParallelSlots(dir, appDir, matcher) {
|
|
|
986
1067
|
if (entry.name === "@children") continue;
|
|
987
1068
|
const slotName = entry.name.slice(1);
|
|
988
1069
|
const slotDir = path.join(dir, entry.name);
|
|
989
|
-
const pagePath =
|
|
1070
|
+
const pagePath = findSlotRootPage(slotDir, matcher);
|
|
990
1071
|
const defaultPath = findFile(slotDir, "default", matcher);
|
|
991
1072
|
const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
|
|
992
1073
|
if (!pagePath && !defaultPath && interceptingRoutes.length === 0) continue;
|
|
@@ -1062,6 +1143,96 @@ function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
|
|
|
1062
1143
|
return results;
|
|
1063
1144
|
}
|
|
1064
1145
|
/**
|
|
1146
|
+
* Discover sibling-style interception markers — interception marker directories
|
|
1147
|
+
* (e.g. `(..)showcase`, `(..)(..)hoge`) that are NOT wrapped inside an `@slot`
|
|
1148
|
+
* directory. Mutates each matching route's `siblingIntercepts` array.
|
|
1149
|
+
*
|
|
1150
|
+
* Sibling intercepts use the same conventions and target-computation logic as
|
|
1151
|
+
* slot intercepts, but their intercepting page replaces the full page response
|
|
1152
|
+
* (not a slot) during soft navigation.
|
|
1153
|
+
*/
|
|
1154
|
+
function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
1155
|
+
const routesByDir = /* @__PURE__ */ new Map();
|
|
1156
|
+
for (const route of routes) {
|
|
1157
|
+
const filePath = route.pagePath ?? route.routePath;
|
|
1158
|
+
if (!filePath) continue;
|
|
1159
|
+
const routeDir = normalizePathSeparators(path.dirname(filePath));
|
|
1160
|
+
if (!routesByDir.has(routeDir)) routesByDir.set(routeDir, route);
|
|
1161
|
+
}
|
|
1162
|
+
function walk(dir) {
|
|
1163
|
+
let entries;
|
|
1164
|
+
try {
|
|
1165
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1166
|
+
} catch {
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
for (const entry of entries) {
|
|
1170
|
+
if (!entry.isDirectory()) continue;
|
|
1171
|
+
if (entry.name.startsWith("_")) continue;
|
|
1172
|
+
if (entry.name.startsWith("@")) continue;
|
|
1173
|
+
const childDir = path.join(dir, entry.name);
|
|
1174
|
+
const marker = matchInterceptConvention(entry.name);
|
|
1175
|
+
if (marker) {
|
|
1176
|
+
const restOfName = entry.name.slice(marker.prefix.length);
|
|
1177
|
+
const parentDir = dir;
|
|
1178
|
+
const results = [];
|
|
1179
|
+
collectInterceptingPages(childDir, childDir, marker.convention, restOfName, parentDir, appDir, parentDir, results, matcher);
|
|
1180
|
+
for (const ir of results) {
|
|
1181
|
+
ir.slotId = createAppRouteGraphSiblingInterceptSlotId(ir.sourceMatchPattern);
|
|
1182
|
+
const owner = findOwnerRouteForDir(parentDir, appDir, routes, routesByDir);
|
|
1183
|
+
if (owner) owner.siblingIntercepts.push(ir);
|
|
1184
|
+
}
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
walk(childDir);
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
walk(appDir);
|
|
1191
|
+
}
|
|
1192
|
+
/**
|
|
1193
|
+
* Find the best route to attach a sibling intercept to, given the directory
|
|
1194
|
+
* that contains the interception marker.
|
|
1195
|
+
*
|
|
1196
|
+
* 1. Exact hit: a route whose page/handler lives directly in `dir`.
|
|
1197
|
+
* 2. Subtree hit: shallowest route whose page lives anywhere under `dir`
|
|
1198
|
+
* (handles catch-all routes like `/templates/:catchAll+`).
|
|
1199
|
+
* 3. Ancestor walk: walk up the directory tree toward `appDir` looking for
|
|
1200
|
+
* any of the above. This handles the case where the marker directory has
|
|
1201
|
+
* no sibling pages at all (e.g. `deep/path/(...)target` with no
|
|
1202
|
+
* `deep/path/page.tsx`).
|
|
1203
|
+
*
|
|
1204
|
+
* All comparisons happen in forward-slash space: `appDir` is forward-slash
|
|
1205
|
+
* (normalized once in the config hook), but `dir` and route file paths
|
|
1206
|
+
* descend through native `path.join`/`path.dirname`, which reintroduce
|
|
1207
|
+
* backslashes on Windows. Without normalizing, the `current === appDir`
|
|
1208
|
+
* termination never fires there and the walk overshoots the app root.
|
|
1209
|
+
* `routesByDir` keys must be forward-slash dirnames of the route file paths.
|
|
1210
|
+
*
|
|
1211
|
+
* Exported for tests.
|
|
1212
|
+
*/
|
|
1213
|
+
function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
1214
|
+
const appRoot = normalizePathSeparators(appDir);
|
|
1215
|
+
let current = normalizePathSeparators(dir);
|
|
1216
|
+
while (true) {
|
|
1217
|
+
const exact = routesByDir.get(current);
|
|
1218
|
+
if (exact) return exact;
|
|
1219
|
+
const currentWithSep = current + "/";
|
|
1220
|
+
let best = null;
|
|
1221
|
+
for (const route of routes) {
|
|
1222
|
+
const filePath = route.pagePath ?? route.routePath;
|
|
1223
|
+
if (!filePath) continue;
|
|
1224
|
+
if (!normalizePathSeparators(filePath).startsWith(currentWithSep)) continue;
|
|
1225
|
+
if (!best || route.patternParts.length < best.patternParts.length) best = route;
|
|
1226
|
+
}
|
|
1227
|
+
if (best) return best;
|
|
1228
|
+
if (current === appRoot) break;
|
|
1229
|
+
const parent = path.dirname(current);
|
|
1230
|
+
if (parent === current) break;
|
|
1231
|
+
current = parent;
|
|
1232
|
+
}
|
|
1233
|
+
return null;
|
|
1234
|
+
}
|
|
1235
|
+
/**
|
|
1065
1236
|
* Recursively scan a directory tree for page.tsx files that are inside
|
|
1066
1237
|
* intercepting route directories.
|
|
1067
1238
|
*/
|
|
@@ -1314,4 +1485,4 @@ function computeAppRouteStaticSiblings(allRoutes, matchedRoute) {
|
|
|
1314
1485
|
return Array.from(siblings);
|
|
1315
1486
|
}
|
|
1316
1487
|
//#endregion
|
|
1317
|
-
export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, isInvisibleSegment };
|
|
1488
|
+
export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment };
|
|
@@ -69,7 +69,7 @@ function findFileWithExtensions(basePath, matcher) {
|
|
|
69
69
|
*/
|
|
70
70
|
function findFileWithExts(dir, name, matcher) {
|
|
71
71
|
for (const ext of matcher.dottedExtensions) {
|
|
72
|
-
const filePath = path.join(dir, name + ext);
|
|
72
|
+
const filePath = path.posix.join(dir, name + ext);
|
|
73
73
|
if (existsSync(filePath)) return filePath;
|
|
74
74
|
}
|
|
75
75
|
return null;
|
|
@@ -5,6 +5,7 @@ declare function routePattern(pathname: string): string;
|
|
|
5
5
|
declare function fillRoutePatternSegments(pathname: string, params: RoutePatternParams): string | null;
|
|
6
6
|
declare function matchRoutePattern(urlParts: readonly string[], patternParts: readonly string[]): RoutePatternParams | null;
|
|
7
7
|
declare function matchRoutePatternPrefix(pathParts: readonly string[], patternParts: readonly string[]): boolean;
|
|
8
|
+
declare function matchRoutePatternWithOptionalDynamicSegments(pathParts: readonly string[], patternParts: readonly string[]): boolean;
|
|
8
9
|
/**
|
|
9
10
|
* A single entry from `getStaticPaths().paths`.
|
|
10
11
|
*
|
|
@@ -60,4 +61,4 @@ declare function normalizeStaticPathname(pathname: string): string;
|
|
|
60
61
|
*/
|
|
61
62
|
declare function normalizeStaticPathsEntry(entry: StaticPathsEntry, routePattern: string): NormalizedStaticPathsEntry;
|
|
62
63
|
//#endregion
|
|
63
|
-
export { RoutePatternParams, StaticPathsEntry, fillRoutePatternSegments, matchRoutePattern, matchRoutePatternPrefix, normalizeStaticPathname, normalizeStaticPathsEntry, routePattern, routePatternParts };
|
|
64
|
+
export { RoutePatternParams, StaticPathsEntry, fillRoutePatternSegments, matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments, normalizeStaticPathname, normalizeStaticPathsEntry, routePattern, routePatternParts };
|
|
@@ -104,6 +104,21 @@ function matchRoutePatternPrefix(pathParts, patternParts) {
|
|
|
104
104
|
}
|
|
105
105
|
return true;
|
|
106
106
|
}
|
|
107
|
+
function matchRoutePatternWithOptionalDynamicSegments(pathParts, patternParts) {
|
|
108
|
+
function matchFrom(pathIndex, patternIndex) {
|
|
109
|
+
if (patternIndex === patternParts.length) return pathIndex === pathParts.length;
|
|
110
|
+
const patternPart = patternParts[patternIndex];
|
|
111
|
+
if (patternPart.startsWith(":") && (patternPart.endsWith("+") || patternPart.endsWith("*"))) {
|
|
112
|
+
const minLength = patternPart.endsWith("+") ? 1 : 0;
|
|
113
|
+
for (let endIndex = pathIndex + minLength; endIndex <= pathParts.length; endIndex++) if (matchFrom(endIndex, patternIndex + 1)) return true;
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (patternPart.startsWith(":")) return matchFrom(pathIndex, patternIndex + 1) || pathIndex < pathParts.length && matchFrom(pathIndex + 1, patternIndex + 1);
|
|
117
|
+
if (pathIndex >= pathParts.length || pathParts[pathIndex] !== patternPart) return false;
|
|
118
|
+
return matchFrom(pathIndex + 1, patternIndex + 1);
|
|
119
|
+
}
|
|
120
|
+
return matchFrom(0, 0);
|
|
121
|
+
}
|
|
107
122
|
/**
|
|
108
123
|
* Strip query string and a single trailing slash from a pathname.
|
|
109
124
|
*
|
|
@@ -147,4 +162,4 @@ function normalizeStaticPathsEntry(entry, routePattern) {
|
|
|
147
162
|
return { params };
|
|
148
163
|
}
|
|
149
164
|
//#endregion
|
|
150
|
-
export { fillRoutePatternSegments, matchRoutePattern, matchRoutePatternPrefix, normalizeStaticPathname, normalizeStaticPathsEntry, routePattern, routePatternParts };
|
|
165
|
+
export { fillRoutePatternSegments, matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments, normalizeStaticPathname, normalizeStaticPathsEntry, routePattern, routePatternParts };
|
|
@@ -7,6 +7,7 @@ import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-medi
|
|
|
7
7
|
import { isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
8
8
|
import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT, resolveBodyParserConfig } from "./pages-body-parser-config.js";
|
|
9
9
|
import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
|
|
10
|
+
import { performOnDemandRevalidate } from "./pages-revalidate.js";
|
|
10
11
|
import { decode } from "node:querystring";
|
|
11
12
|
import { Buffer } from "node:buffer";
|
|
12
13
|
//#region src/server/api-handler.ts
|
|
@@ -198,6 +199,9 @@ function enhanceApiObjects(req, res, query, body) {
|
|
|
198
199
|
if (typeof statusOrUrl === "string") this.writeHead(307, { Location: statusOrUrl });
|
|
199
200
|
else this.writeHead(statusOrUrl, { Location: url ?? "" });
|
|
200
201
|
this.end();
|
|
202
|
+
},
|
|
203
|
+
async revalidate(urlPath, opts) {
|
|
204
|
+
await performOnDemandRevalidate(req, urlPath, opts);
|
|
201
205
|
}
|
|
202
206
|
})
|
|
203
207
|
};
|