vinext 1.0.0-beta.1 → 1.0.0-beta.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 +4 -1
- package/dist/build/client-build-config.js +1 -0
- package/dist/build/report.d.ts +8 -1
- package/dist/build/report.js +12 -2
- package/dist/check.js +15 -2
- package/dist/cli.js +3 -2
- package/dist/config/config-matchers.d.ts +13 -37
- package/dist/config/config-matchers.js +31 -121
- package/dist/config/next-config.d.ts +3 -2
- package/dist/config/next-config.js +3 -1
- package/dist/config/request-context.d.ts +14 -0
- package/dist/config/request-context.js +26 -0
- package/dist/entries/app-rsc-entry.js +27 -9
- package/dist/entries/app-rsc-manifest.js +17 -0
- package/dist/entries/pages-server-entry.js +3 -2
- package/dist/index.js +82 -58
- package/dist/init.d.ts +6 -4
- package/dist/init.js +20 -22
- package/dist/plugins/og-assets.js +1 -1
- package/dist/routing/app-route-graph.d.ts +13 -5
- package/dist/routing/app-route-graph.js +59 -13
- package/dist/routing/app-router.js +12 -6
- package/dist/routing/file-matcher.d.ts +11 -22
- package/dist/routing/file-matcher.js +21 -33
- package/dist/routing/pages-router.js +23 -29
- package/dist/routing/route-matching.d.ts +14 -1
- package/dist/routing/route-matching.js +13 -1
- package/dist/routing/route-pattern.d.ts +2 -1
- package/dist/routing/route-pattern.js +7 -4
- package/dist/routing/route-trie.d.ts +5 -1
- package/dist/routing/route-trie.js +5 -2
- package/dist/server/app-browser-entry.js +7 -6
- package/dist/server/app-browser-history-controller.d.ts +1 -1
- package/dist/server/app-browser-history-controller.js +3 -3
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-browser-visible-commit.js +1 -1
- package/dist/server/app-fallback-renderer.d.ts +3 -0
- package/dist/server/app-fallback-renderer.js +4 -2
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-page-boundary-render.d.ts +12 -1
- package/dist/server/app-page-boundary-render.js +84 -2
- package/dist/server/app-page-cache-finalizer.d.ts +1 -1
- package/dist/server/app-page-cache.d.ts +1 -1
- package/dist/server/app-page-dispatch.d.ts +11 -2
- package/dist/server/app-page-dispatch.js +14 -17
- package/dist/server/app-page-element-builder.d.ts +7 -2
- package/dist/server/app-page-element-builder.js +112 -27
- package/dist/server/app-page-head.d.ts +54 -2
- package/dist/server/app-page-head.js +164 -78
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
- package/dist/server/app-page-http-access-fallback-metadata.js +106 -0
- package/dist/server/app-page-render.d.ts +2 -2
- package/dist/server/app-page-render.js +1 -1
- package/dist/server/app-page-request.d.ts +12 -0
- package/dist/server/app-page-request.js +133 -13
- package/dist/server/app-page-route-wiring.d.ts +9 -0
- package/dist/server/app-page-route-wiring.js +43 -3
- package/dist/server/app-post-middleware-context.d.ts +1 -1
- package/dist/server/app-post-middleware-context.js +1 -1
- package/dist/server/app-route-handler-dispatch.js +31 -17
- package/dist/server/app-route-handler-execution.d.ts +6 -1
- package/dist/server/app-route-handler-execution.js +42 -16
- package/dist/server/app-route-handler-policy.d.ts +2 -0
- package/dist/server/app-route-handler-policy.js +3 -3
- package/dist/server/app-route-module-loader.d.ts +3 -1
- package/dist/server/app-route-module-loader.js +1 -0
- package/dist/server/app-rsc-cache-busting.d.ts +1 -1
- package/dist/server/app-rsc-errors.d.ts +1 -1
- package/dist/server/app-rsc-errors.js +12 -2
- package/dist/server/app-rsc-handler.d.ts +10 -5
- package/dist/server/app-rsc-handler.js +52 -24
- package/dist/server/app-rsc-request-normalization.d.ts +3 -2
- package/dist/server/app-rsc-request-normalization.js +8 -2
- package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
- package/dist/server/app-rsc-response-finalizer.js +5 -11
- package/dist/server/app-rsc-route-matching.d.ts +20 -0
- package/dist/server/app-rsc-route-matching.js +87 -11
- package/dist/server/app-segment-config.js +13 -3
- package/dist/server/app-server-action-execution.d.ts +3 -1
- package/dist/server/app-server-action-execution.js +9 -7
- package/dist/server/app-ssr-entry.js +2 -2
- package/dist/server/app-ssr-stream.d.ts +1 -7
- package/dist/server/app-ssr-stream.js +2 -10
- package/dist/server/config-headers.d.ts +24 -0
- package/dist/server/config-headers.js +52 -0
- package/dist/server/dev-initial-server-error.d.ts +1 -1
- package/dist/server/dev-server.js +29 -13
- package/dist/server/isr-cache.d.ts +1 -1
- package/dist/server/middleware-matcher-pattern.d.ts +22 -0
- package/dist/server/middleware-matcher-pattern.js +219 -0
- package/dist/server/middleware-matcher.d.ts +2 -7
- package/dist/server/middleware-matcher.js +13 -60
- package/dist/server/middleware-path-to-regexp.d.ts +14 -0
- package/dist/server/middleware-path-to-regexp.js +228 -0
- package/dist/server/middleware-runtime.js +4 -3
- package/dist/server/middleware.d.ts +1 -1
- package/dist/server/middleware.js +2 -1
- package/dist/server/pages-data-route.d.ts +11 -1
- package/dist/server/pages-data-route.js +16 -1
- package/dist/server/pages-get-initial-props.d.ts +15 -2
- package/dist/server/pages-get-initial-props.js +16 -6
- package/dist/server/pages-page-data.d.ts +3 -1
- package/dist/server/pages-page-data.js +16 -7
- package/dist/server/pages-page-handler.d.ts +4 -2
- package/dist/server/pages-page-handler.js +16 -17
- package/dist/server/pages-page-response.js +1 -1
- package/dist/server/pages-readiness.d.ts +3 -3
- package/dist/server/pages-request-pipeline.d.ts +1 -0
- package/dist/server/pages-request-pipeline.js +25 -11
- package/dist/server/pages-router-entry.js +6 -0
- package/dist/server/prerender-route-params.js +2 -2
- package/dist/server/prod-server.d.ts +1 -4
- package/dist/server/prod-server.js +36 -23
- package/dist/server/request-pipeline.d.ts +15 -29
- package/dist/server/request-pipeline.js +24 -59
- package/dist/server/rsc-stream-hints.js +122 -18
- package/dist/shims/app.d.ts +1 -1
- package/dist/shims/cache-request-state.d.ts +27 -1
- package/dist/shims/cache-request-state.js +60 -1
- package/dist/shims/cache-runtime.d.ts +12 -1
- package/dist/shims/cache-runtime.js +22 -7
- package/dist/shims/cache.d.ts +5 -5
- package/dist/shims/cache.js +26 -10
- package/dist/shims/document.d.ts +42 -83
- package/dist/shims/document.js +62 -53
- package/dist/shims/dynamic.d.ts +5 -25
- package/dist/shims/dynamic.js +29 -8
- package/dist/shims/error.d.ts +1 -1
- package/dist/shims/error.js +1 -1
- package/dist/shims/fetch-cache.js +12 -3
- package/dist/shims/font-google-base.d.ts +11 -4
- package/dist/shims/font-local.d.ts +10 -3
- package/dist/shims/head.d.ts +13 -2
- package/dist/shims/head.js +3 -3
- package/dist/shims/headers.d.ts +28 -31
- package/dist/shims/headers.js +109 -49
- package/dist/shims/image.d.ts +27 -37
- package/dist/shims/image.js +21 -14
- package/dist/shims/internal/cookie-serialize.d.ts +3 -1
- package/dist/shims/internal/cookie-serialize.js +3 -1
- package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
- package/dist/shims/internal/utils.d.ts +3 -3
- package/dist/shims/legacy-image.d.ts +3 -37
- package/dist/shims/legacy-image.js +6 -16
- package/dist/shims/link.d.ts +15 -22
- package/dist/shims/link.js +20 -11
- package/dist/shims/navigation-context-state.d.ts +1 -1
- package/dist/shims/navigation.d.ts +7 -3
- package/dist/shims/navigation.js +24 -13
- package/dist/shims/navigation.react-server.js +1 -1
- package/dist/shims/next-shims-augmentations.d.ts +1 -0
- package/dist/shims/next-shims-public.d.ts +1 -0
- package/dist/shims/public-shim-map.json.js +103 -0
- package/dist/shims/readonly-url-search-params.d.ts +3 -3
- package/dist/shims/readonly-url-search-params.js +3 -3
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/router.d.ts +55 -21
- package/dist/shims/router.js +162 -37
- package/dist/shims/script.d.ts +6 -10
- package/dist/shims/script.js +6 -3
- package/dist/shims/server.d.ts +45 -22
- package/dist/shims/server.js +44 -5
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +2 -0
- package/dist/shims/url-safety.d.ts +1 -1
- package/dist/shims/url-safety.js +2 -2
- package/dist/typegen.d.ts +8 -2
- package/dist/typegen.js +76 -15
- package/dist/utils/domain-locale.d.ts +2 -2
- package/dist/utils/external-url.d.ts +5 -0
- package/dist/utils/external-url.js +7 -0
- package/dist/utils/regex-safety.d.ts +8 -0
- package/dist/utils/regex-safety.js +737 -0
- package/package.json +13 -2
|
@@ -25,7 +25,10 @@ type InterceptingRoute = {
|
|
|
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
27
|
layoutSegments?: string[][]; /** Full normalized interception branch segments through the page. */
|
|
28
|
-
branchSegments?: string[]; /**
|
|
28
|
+
branchSegments?: string[]; /** Nearest not-found convention inside the interception branch. */
|
|
29
|
+
notFoundPath?: string | null; /** Slot-root-relative branch segments used to scope not-found params. */
|
|
30
|
+
notFoundBranchSegments?: string[]; /** Tree position relative to the normalized interception branch. */
|
|
31
|
+
notFoundTreePosition?: number | null; /** Parameter names for dynamic segments */
|
|
29
32
|
params: string[];
|
|
30
33
|
/**
|
|
31
34
|
* Synthetic page-carrier slot id for sibling (slot-less) interception.
|
|
@@ -46,7 +49,9 @@ type ParallelSlot = {
|
|
|
46
49
|
configLayoutPaths?: string[]; /** Tree positions of configLayoutPaths relative to the slot root. */
|
|
47
50
|
configLayoutTreePositions?: number[]; /** Absolute path to the slot's loading component */
|
|
48
51
|
loadingPath: string | null; /** Absolute path to the slot's error component */
|
|
49
|
-
errorPath: string | null; /**
|
|
52
|
+
errorPath: string | null; /** Nearest not-found convention for the slot's active branch. */
|
|
53
|
+
notFoundPath?: string | null; /** Tree position of that convention relative to the slot root. */
|
|
54
|
+
notFoundTreePosition?: number | null; /** Intercepting routes within this slot */
|
|
50
55
|
interceptingRoutes: InterceptingRoute[];
|
|
51
56
|
/**
|
|
52
57
|
* The layout index (0-based, in route.layouts[]) that this slot belongs to.
|
|
@@ -105,7 +110,8 @@ type AppRoute = {
|
|
|
105
110
|
layoutErrorPaths: (string | null)[]; /** Per-segment error boundary paths, aligned with errorTreePositions. */
|
|
106
111
|
errorPaths?: string[]; /** Tree position (directory depth from app/ root) for each error boundary. */
|
|
107
112
|
errorTreePositions?: number[]; /** Not-found component path (nearest, walking up from page dir) */
|
|
108
|
-
notFoundPath: string | null;
|
|
113
|
+
notFoundPath: string | null; /** Tree position of the nearest not-found component's owning segment. */
|
|
114
|
+
notFoundTreePosition?: number | null;
|
|
109
115
|
/**
|
|
110
116
|
* Not-found component paths per layout level (aligned with layouts array).
|
|
111
117
|
* Each entry is the not-found.tsx at that layout's directory, or null.
|
|
@@ -119,8 +125,10 @@ type AppRoute = {
|
|
|
119
125
|
* Used to create per-layout ForbiddenBoundary.
|
|
120
126
|
*/
|
|
121
127
|
forbiddenPaths: (string | null)[]; /** Forbidden component path (403) at the route's directory level */
|
|
122
|
-
forbiddenPath: string | null; /**
|
|
123
|
-
|
|
128
|
+
forbiddenPath: string | null; /** Tree position of the nearest forbidden component's owning segment. */
|
|
129
|
+
forbiddenTreePosition?: number | null; /** Unauthorized component path (401) at the route's directory level */
|
|
130
|
+
unauthorizedPath: string | null; /** Tree position of the nearest unauthorized component's owning segment. */
|
|
131
|
+
unauthorizedTreePosition?: number | null; /** Unauthorized component paths per layout level (aligned with layouts array). */
|
|
124
132
|
unauthorizedPaths: (string | null)[];
|
|
125
133
|
/**
|
|
126
134
|
* Filesystem segments from app/ root to the route's directory.
|
|
@@ -459,11 +459,14 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
459
459
|
const subPage = slotPages.get(slot.key);
|
|
460
460
|
if (subPage !== void 0) {
|
|
461
461
|
const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage, matcher);
|
|
462
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage, matcher);
|
|
462
463
|
return {
|
|
463
464
|
...slot,
|
|
464
465
|
pagePath: subPage,
|
|
465
466
|
configLayoutPaths,
|
|
466
467
|
configLayoutTreePositions: findSlotConfigLayoutTreePositions(slot.ownerDir, configLayoutPaths),
|
|
468
|
+
notFoundPath: notFoundBoundary.path,
|
|
469
|
+
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
467
470
|
routeSegments: rawSegments
|
|
468
471
|
};
|
|
469
472
|
}
|
|
@@ -545,11 +548,14 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
545
548
|
const subSlots = parentRoute.parallelSlots.map((slot) => {
|
|
546
549
|
const subPage = slotPages.get(slot.key);
|
|
547
550
|
const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage ?? null, matcher);
|
|
551
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage ?? null, matcher);
|
|
548
552
|
return {
|
|
549
553
|
...slot,
|
|
550
554
|
pagePath: subPage || null,
|
|
551
555
|
configLayoutPaths,
|
|
552
556
|
configLayoutTreePositions: findSlotConfigLayoutTreePositions(slot.ownerDir, configLayoutPaths),
|
|
557
|
+
notFoundPath: notFoundBoundary.path,
|
|
558
|
+
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
553
559
|
routeSegments: subPage ? rawSegments : null
|
|
554
560
|
};
|
|
555
561
|
});
|
|
@@ -578,10 +584,13 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
578
584
|
errorPath: parentRoute.errorPath,
|
|
579
585
|
layoutErrorPaths: parentRoute.layoutErrorPaths,
|
|
580
586
|
notFoundPath: parentRoute.notFoundPath,
|
|
587
|
+
notFoundTreePosition: parentRoute.notFoundTreePosition,
|
|
581
588
|
notFoundPaths: parentRoute.notFoundPaths,
|
|
582
589
|
forbiddenPaths: parentRoute.forbiddenPaths,
|
|
583
590
|
forbiddenPath: parentRoute.forbiddenPath,
|
|
591
|
+
forbiddenTreePosition: parentRoute.forbiddenTreePosition,
|
|
584
592
|
unauthorizedPath: parentRoute.unauthorizedPath,
|
|
593
|
+
unauthorizedTreePosition: parentRoute.unauthorizedTreePosition,
|
|
585
594
|
unauthorizedPaths: parentRoute.unauthorizedPaths,
|
|
586
595
|
routeSegments: [...parentRoute.routeSegments, ...rawSegments],
|
|
587
596
|
childrenRouteSegments: childrenDefault ? parentRoute.routeSegments : void 0,
|
|
@@ -669,6 +678,25 @@ function findSlotConfigLayoutTreePositions(slotDir, layoutPaths) {
|
|
|
669
678
|
return relativeDir ? relativeDir.split(path.sep).filter(Boolean).length : 0;
|
|
670
679
|
});
|
|
671
680
|
}
|
|
681
|
+
function findSlotNotFoundBoundary(slotDir, pagePath, matcher) {
|
|
682
|
+
let dir = pagePath ? path.dirname(pagePath) : slotDir;
|
|
683
|
+
while (dir === slotDir || dir.startsWith(`${slotDir}${path.sep}`)) {
|
|
684
|
+
const boundaryPath = findFile(dir, "not-found", matcher);
|
|
685
|
+
if (boundaryPath) {
|
|
686
|
+
const relativeDir = path.relative(slotDir, dir);
|
|
687
|
+
return {
|
|
688
|
+
path: boundaryPath,
|
|
689
|
+
treePosition: relativeDir ? relativeDir.split(path.sep).filter(Boolean).length : 0
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
if (dir === slotDir) break;
|
|
693
|
+
dir = path.dirname(dir);
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
path: null,
|
|
697
|
+
treePosition: null
|
|
698
|
+
};
|
|
699
|
+
}
|
|
672
700
|
/**
|
|
673
701
|
* Find a sibling catch-all page directly under `dir`, i.e. a `[...slug]` or
|
|
674
702
|
* `[[...slug]]` directory that contains a `page` file. Returns the absolute
|
|
@@ -730,9 +758,12 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
730
758
|
const effectivePagePath = pagePath ?? (routePath ? null : findFile(routeDir, "default", matcher));
|
|
731
759
|
const loadingPath = findFile(routeDir, "loading", matcher);
|
|
732
760
|
const errorPath = findFile(routeDir, "error", matcher);
|
|
733
|
-
const
|
|
734
|
-
const
|
|
735
|
-
const
|
|
761
|
+
const notFoundEntry = discoverBoundaryFileEntry(segments, appDir, "not-found", matcher);
|
|
762
|
+
const notFoundPath = notFoundEntry?.path ?? null;
|
|
763
|
+
const forbiddenEntry = discoverBoundaryFileEntry(segments, appDir, "forbidden", matcher);
|
|
764
|
+
const forbiddenPath = forbiddenEntry?.path ?? null;
|
|
765
|
+
const unauthorizedEntry = discoverBoundaryFileEntry(segments, appDir, "unauthorized", matcher);
|
|
766
|
+
const unauthorizedPath = unauthorizedEntry?.path ?? null;
|
|
736
767
|
const notFoundPaths = discoverBoundaryFilePerLayout(layouts, "not-found", matcher);
|
|
737
768
|
const forbiddenPaths = discoverBoundaryFilePerLayout(layouts, "forbidden", matcher);
|
|
738
769
|
const unauthorizedPaths = discoverBoundaryFilePerLayout(layouts, "unauthorized", matcher);
|
|
@@ -759,10 +790,13 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
759
790
|
errorPaths,
|
|
760
791
|
errorTreePositions,
|
|
761
792
|
notFoundPath,
|
|
793
|
+
notFoundTreePosition: notFoundEntry?.treePosition ?? null,
|
|
762
794
|
notFoundPaths,
|
|
763
795
|
forbiddenPaths,
|
|
764
796
|
forbiddenPath,
|
|
797
|
+
forbiddenTreePosition: forbiddenEntry?.treePosition ?? null,
|
|
765
798
|
unauthorizedPath,
|
|
799
|
+
unauthorizedTreePosition: unauthorizedEntry?.treePosition ?? null,
|
|
766
800
|
unauthorizedPaths,
|
|
767
801
|
routeSegments: segments,
|
|
768
802
|
templateTreePositions,
|
|
@@ -921,7 +955,7 @@ function discoverLayoutAlignedErrors(segments, appDir, matcher) {
|
|
|
921
955
|
* by walking from the route's directory up to the app root.
|
|
922
956
|
* Returns the first (closest) file found, or null.
|
|
923
957
|
*/
|
|
924
|
-
function
|
|
958
|
+
function discoverBoundaryFileEntry(segments, appDir, fileName, matcher) {
|
|
925
959
|
const dirs = [];
|
|
926
960
|
let dir = appDir;
|
|
927
961
|
dirs.push(dir);
|
|
@@ -931,7 +965,10 @@ function discoverBoundaryFile(segments, appDir, fileName, matcher) {
|
|
|
931
965
|
}
|
|
932
966
|
for (let i = dirs.length - 1; i >= 0; i--) {
|
|
933
967
|
const f = findFile(dirs[i], fileName, matcher);
|
|
934
|
-
if (f) return
|
|
968
|
+
if (f) return {
|
|
969
|
+
path: f,
|
|
970
|
+
treePosition: i
|
|
971
|
+
};
|
|
935
972
|
}
|
|
936
973
|
return null;
|
|
937
974
|
}
|
|
@@ -1172,6 +1209,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1172
1209
|
const ownerSegments = path.relative(appDir, dir).split(path.sep).filter((segment) => segment.length > 0);
|
|
1173
1210
|
const ownerTreePath = createAppRouteGraphTreePath(ownerSegments, ownerSegments.length);
|
|
1174
1211
|
const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
|
|
1212
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slotDir, pagePath, matcher);
|
|
1175
1213
|
slots.push({
|
|
1176
1214
|
id: createAppRouteGraphSlotId(slotName, ownerTreePath),
|
|
1177
1215
|
key: `${slotName}@${path.relative(appDir, slotDir)}`,
|
|
@@ -1186,6 +1224,8 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1186
1224
|
configLayoutTreePositions: findSlotConfigLayoutTreePositions(slotDir, configLayoutPaths),
|
|
1187
1225
|
loadingPath: findFile(slotDir, "loading", matcher),
|
|
1188
1226
|
errorPath: findFile(slotDir, "error", matcher),
|
|
1227
|
+
notFoundPath: notFoundBoundary.path,
|
|
1228
|
+
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
1189
1229
|
interceptingRoutes,
|
|
1190
1230
|
layoutIndex: -1,
|
|
1191
1231
|
routeSegments: pagePath ? [] : null
|
|
@@ -1240,7 +1280,7 @@ function isInterceptionMarkerDir(name) {
|
|
|
1240
1280
|
function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
|
|
1241
1281
|
if (!fs.existsSync(slotDir)) return [];
|
|
1242
1282
|
const results = [];
|
|
1243
|
-
scanForInterceptingPages(slotDir, routeDir, appDir, results, matcher);
|
|
1283
|
+
scanForInterceptingPages(slotDir, slotDir, routeDir, appDir, results, matcher);
|
|
1244
1284
|
return results;
|
|
1245
1285
|
}
|
|
1246
1286
|
/**
|
|
@@ -1277,7 +1317,7 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1277
1317
|
const restOfName = entry.name.slice(marker.prefix.length);
|
|
1278
1318
|
const parentDir = dir;
|
|
1279
1319
|
const results = [];
|
|
1280
|
-
collectInterceptingPages(childDir, childDir, marker.convention, restOfName, parentDir, appDir, parentDir, results, matcher);
|
|
1320
|
+
collectInterceptingPages(childDir, childDir, marker.convention, restOfName, parentDir, appDir, parentDir, null, results, matcher);
|
|
1281
1321
|
for (const ir of results) {
|
|
1282
1322
|
ir.slotId = createAppRouteGraphSiblingInterceptSlotId(ir.sourceMatchPattern);
|
|
1283
1323
|
const owner = findOwnerRouteForDir(parentDir, appDir, routes, routesByDir);
|
|
@@ -1334,7 +1374,7 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1334
1374
|
* Recursively scan a directory tree for page.tsx files that are inside
|
|
1335
1375
|
* intercepting route directories.
|
|
1336
1376
|
*/
|
|
1337
|
-
function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
|
|
1377
|
+
function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, results, matcher) {
|
|
1338
1378
|
if (!fs.existsSync(currentDir)) return;
|
|
1339
1379
|
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
1340
1380
|
for (const entry of entries) {
|
|
@@ -1344,8 +1384,8 @@ function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher
|
|
|
1344
1384
|
const interceptDir = path.join(currentDir, entry.name);
|
|
1345
1385
|
if (interceptMatch) {
|
|
1346
1386
|
const restOfName = entry.name.slice(interceptMatch.prefix.length);
|
|
1347
|
-
collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, results, matcher);
|
|
1348
|
-
} else scanForInterceptingPages(interceptDir, routeDir, appDir, results, matcher);
|
|
1387
|
+
collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, slotRootDir, results, matcher);
|
|
1388
|
+
} else scanForInterceptingPages(interceptDir, slotRootDir, routeDir, appDir, results, matcher);
|
|
1349
1389
|
}
|
|
1350
1390
|
}
|
|
1351
1391
|
/**
|
|
@@ -1359,7 +1399,7 @@ function matchInterceptConvention(name) {
|
|
|
1359
1399
|
* Collect page.tsx files inside an intercepting route directory tree
|
|
1360
1400
|
* and compute their target URL patterns.
|
|
1361
1401
|
*/
|
|
1362
|
-
function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, parentLayoutPaths = []) {
|
|
1402
|
+
function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, parentLayoutPaths = []) {
|
|
1363
1403
|
const currentLayoutPath = findFile(currentDir, "layout", matcher);
|
|
1364
1404
|
const layoutPaths = currentLayoutPath ? [...parentLayoutPaths, currentLayoutPath] : parentLayoutPaths;
|
|
1365
1405
|
const page = findFile(currentDir, "page", matcher);
|
|
@@ -1367,13 +1407,19 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1367
1407
|
const targetPattern = computeInterceptTarget(convention, interceptSegment, currentDir, interceptRoot, routeDir, appDir);
|
|
1368
1408
|
if (targetPattern) {
|
|
1369
1409
|
const sourceMatchPattern = computeInterceptSourceMatchPattern(interceptParentDir, appDir);
|
|
1410
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slotRootDir ?? interceptRoot, page, matcher);
|
|
1411
|
+
const branchSegments = [interceptSegment, ...path.relative(interceptRoot, path.dirname(page)).split(path.sep).filter(Boolean)];
|
|
1412
|
+
const slotParentSegments = slotRootDir ? path.relative(slotRootDir, interceptParentDir).split(path.sep).filter(Boolean) : [];
|
|
1370
1413
|
results.push({
|
|
1371
|
-
branchSegments
|
|
1414
|
+
branchSegments,
|
|
1372
1415
|
convention,
|
|
1373
1416
|
layoutPaths: [...layoutPaths],
|
|
1374
1417
|
layoutSegments: layoutPaths.map((layoutPath) => {
|
|
1375
1418
|
return [interceptSegment, ...path.relative(interceptRoot, path.dirname(layoutPath)).split(path.sep).filter(Boolean)];
|
|
1376
1419
|
}),
|
|
1420
|
+
notFoundBranchSegments: [...slotParentSegments, ...branchSegments],
|
|
1421
|
+
notFoundPath: notFoundBoundary.path,
|
|
1422
|
+
notFoundTreePosition: notFoundBoundary.treePosition === null ? null : slotRootDir ? notFoundBoundary.treePosition : notFoundBoundary.treePosition + 1,
|
|
1377
1423
|
targetPattern: targetPattern.pattern,
|
|
1378
1424
|
sourceMatchPattern,
|
|
1379
1425
|
pagePath: page,
|
|
@@ -1387,7 +1433,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1387
1433
|
for (const entry of entries) {
|
|
1388
1434
|
if (!entry.isDirectory()) continue;
|
|
1389
1435
|
if (entry.name.startsWith("_")) continue;
|
|
1390
|
-
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, layoutPaths);
|
|
1436
|
+
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, layoutPaths);
|
|
1391
1437
|
}
|
|
1392
1438
|
}
|
|
1393
1439
|
/**
|
|
@@ -20,7 +20,9 @@ import { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamName
|
|
|
20
20
|
let cachedGraph = null;
|
|
21
21
|
let cachedAppDir = null;
|
|
22
22
|
let cachedPageExtensionsKey = null;
|
|
23
|
+
let cacheGeneration = 0;
|
|
23
24
|
function invalidateAppRouteCache() {
|
|
25
|
+
cacheGeneration++;
|
|
24
26
|
cachedGraph = null;
|
|
25
27
|
cachedAppDir = null;
|
|
26
28
|
cachedPageExtensionsKey = null;
|
|
@@ -35,12 +37,16 @@ function invalidateAppRouteCache() {
|
|
|
35
37
|
async function appRouteGraph(appDir, pageExtensions, matcher) {
|
|
36
38
|
matcher ??= createValidFileMatcher(pageExtensions);
|
|
37
39
|
const pageExtensionsKey = JSON.stringify(matcher.extensions);
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
while (true) {
|
|
41
|
+
if (cachedGraph && cachedAppDir === appDir && cachedPageExtensionsKey === pageExtensionsKey) return cachedGraph;
|
|
42
|
+
const scanGeneration = cacheGeneration;
|
|
43
|
+
const graph = await buildAppRouteGraph(appDir, matcher);
|
|
44
|
+
if (scanGeneration !== cacheGeneration) continue;
|
|
45
|
+
cachedGraph = graph;
|
|
46
|
+
cachedAppDir = appDir;
|
|
47
|
+
cachedPageExtensionsKey = pageExtensionsKey;
|
|
48
|
+
return graph;
|
|
49
|
+
}
|
|
44
50
|
}
|
|
45
51
|
/**
|
|
46
52
|
* Scan the app/ directory and return a list of routes.
|
|
@@ -24,32 +24,21 @@ declare function findFileWithExtensions(basePath: string, matcher: ValidFileMatc
|
|
|
24
24
|
*/
|
|
25
25
|
declare function findFileWithExts(dir: string, name: string, matcher: ValidFileMatcher): string | null;
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
* `.mjs/.mts`). When the user configures `pageExtensions` with values Vite
|
|
29
|
-
* does not know about — e.g. `["platform.tsx", "tsx", "mdx"]` from the
|
|
30
|
-
* Next.js `resolve-extensions` fixture — extensionless imports of those
|
|
31
|
-
* files fail to resolve, and the build crashes with "Custom deploy script
|
|
32
|
-
* failed: undefined (1)".
|
|
27
|
+
* Add the config extensions produced by `vinext init` to vinext's resolver.
|
|
33
28
|
*
|
|
34
|
-
*
|
|
29
|
+
* `pageExtensions` is intentionally not part of module resolution. Next.js
|
|
30
|
+
* uses it to discover route files; custom module extensions are configured
|
|
31
|
+
* separately through `turbopack.resolveExtensions` or webpack
|
|
32
|
+
* `resolve.extensions`.
|
|
35
33
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* 3. `.cjs`/`.cts` go last (lowest priority). Neither Vite's defaults nor the
|
|
40
|
-
* user's pageExtensions include them, but `vinext init` renames CJS config
|
|
41
|
-
* files (e.g. `tailwind.config.js` → `tailwind.config.cjs`) when it adds
|
|
42
|
-
* `"type": "module"`, and app code imports those extensionlessly
|
|
43
|
-
* (`import cfg from "../tailwind.config"`). Without these, the bundle fails
|
|
44
|
-
* with "[UNRESOLVED_IMPORT] Could not resolve '../tailwind.config'".
|
|
34
|
+
* The default order preserves vinext's existing module-resolution behavior
|
|
35
|
+
* and matches Next.js Turbopack for overlapping JavaScript and TypeScript
|
|
36
|
+
* extensions.
|
|
45
37
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* See: cloudflare/vinext#1502 for page-extension ordering, and
|
|
50
|
-
* cloudflare/vinext#2435 for extensionless `.cjs` config imports.
|
|
38
|
+
* `.cjs`/`.cts` go last because `vinext init` renames CJS config files when it
|
|
39
|
+
* adds `"type": "module"`, and app code may import those files extensionlessly.
|
|
51
40
|
*/
|
|
52
|
-
declare function buildViteResolveExtensions(
|
|
41
|
+
declare function buildViteResolveExtensions(viteExtensions?: readonly string[]): string[];
|
|
53
42
|
/**
|
|
54
43
|
* Normalize an explicit Next.js resolver extension list for Vite.
|
|
55
44
|
*
|
|
@@ -9,6 +9,15 @@ const DEFAULT_PAGE_EXTENSIONS = [
|
|
|
9
9
|
"jsx",
|
|
10
10
|
"js"
|
|
11
11
|
];
|
|
12
|
+
const DEFAULT_VINEXT_RESOLVE_EXTENSIONS = [
|
|
13
|
+
".tsx",
|
|
14
|
+
".ts",
|
|
15
|
+
".jsx",
|
|
16
|
+
".js",
|
|
17
|
+
".mjs",
|
|
18
|
+
".mts",
|
|
19
|
+
".json"
|
|
20
|
+
];
|
|
12
21
|
function normalizePageExtensions(pageExtensions) {
|
|
13
22
|
if (!Array.isArray(pageExtensions) || pageExtensions.length === 0) return [...DEFAULT_PAGE_EXTENSIONS];
|
|
14
23
|
const filtered = pageExtensions.filter((ext) => typeof ext === "string").map((ext) => ext.trim().replace(/^\.+/, "")).filter((ext) => ext.length > 0);
|
|
@@ -79,46 +88,25 @@ function findFileWithExts(dir, name, matcher) {
|
|
|
79
88
|
return null;
|
|
80
89
|
}
|
|
81
90
|
/**
|
|
82
|
-
*
|
|
83
|
-
* `.mjs/.mts`). When the user configures `pageExtensions` with values Vite
|
|
84
|
-
* does not know about — e.g. `["platform.tsx", "tsx", "mdx"]` from the
|
|
85
|
-
* Next.js `resolve-extensions` fixture — extensionless imports of those
|
|
86
|
-
* files fail to resolve, and the build crashes with "Custom deploy script
|
|
87
|
-
* failed: undefined (1)".
|
|
91
|
+
* Add the config extensions produced by `vinext init` to vinext's resolver.
|
|
88
92
|
*
|
|
89
|
-
*
|
|
93
|
+
* `pageExtensions` is intentionally not part of module resolution. Next.js
|
|
94
|
+
* uses it to discover route files; custom module extensions are configured
|
|
95
|
+
* separately through `turbopack.resolveExtensions` or webpack
|
|
96
|
+
* `resolve.extensions`.
|
|
90
97
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
* 3. `.cjs`/`.cts` go last (lowest priority). Neither Vite's defaults nor the
|
|
95
|
-
* user's pageExtensions include them, but `vinext init` renames CJS config
|
|
96
|
-
* files (e.g. `tailwind.config.js` → `tailwind.config.cjs`) when it adds
|
|
97
|
-
* `"type": "module"`, and app code imports those extensionlessly
|
|
98
|
-
* (`import cfg from "../tailwind.config"`). Without these, the bundle fails
|
|
99
|
-
* with "[UNRESOLVED_IMPORT] Could not resolve '../tailwind.config'".
|
|
98
|
+
* The default order preserves vinext's existing module-resolution behavior
|
|
99
|
+
* and matches Next.js Turbopack for overlapping JavaScript and TypeScript
|
|
100
|
+
* extensions.
|
|
100
101
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
* See: cloudflare/vinext#1502 for page-extension ordering, and
|
|
105
|
-
* cloudflare/vinext#2435 for extensionless `.cjs` config imports.
|
|
102
|
+
* `.cjs`/`.cts` go last because `vinext init` renames CJS config files when it
|
|
103
|
+
* adds `"type": "module"`, and app code may import those files extensionlessly.
|
|
106
104
|
*/
|
|
107
|
-
function buildViteResolveExtensions(
|
|
108
|
-
".mjs",
|
|
109
|
-
".js",
|
|
110
|
-
".mts",
|
|
111
|
-
".ts",
|
|
112
|
-
".jsx",
|
|
113
|
-
".tsx",
|
|
114
|
-
".json"
|
|
115
|
-
]) {
|
|
116
|
-
const dotted = normalizePageExtensions(pageExtensions).map((ext) => `.${ext}`);
|
|
105
|
+
function buildViteResolveExtensions(viteExtensions = DEFAULT_VINEXT_RESOLVE_EXTENSIONS) {
|
|
117
106
|
const seen = /* @__PURE__ */ new Set();
|
|
118
107
|
const result = [];
|
|
119
108
|
for (const ext of [
|
|
120
|
-
...
|
|
121
|
-
...viteDefaults,
|
|
109
|
+
...viteExtensions,
|
|
122
110
|
".cjs",
|
|
123
111
|
".cts"
|
|
124
112
|
]) {
|
|
@@ -2,7 +2,7 @@ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
|
2
2
|
import { decodeRouteSegment, sortRoutes } from "./utils.js";
|
|
3
3
|
import { createValidFileMatcher, scanWithExtensions } from "./file-matcher.js";
|
|
4
4
|
import { patternToNextFormat, validateRoutePatterns } from "./route-validation.js";
|
|
5
|
-
import { createRouteTrieCache,
|
|
5
|
+
import { createRouteTrieCache, matchRouteWithTrieRawPathname } from "./route-matching.js";
|
|
6
6
|
//#region src/routing/pages-router.ts
|
|
7
7
|
/** Next.js special pages that should not produce routes. */
|
|
8
8
|
const RESERVED_PAGE_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -11,11 +11,13 @@ const RESERVED_PAGE_NAMES = /* @__PURE__ */ new Set([
|
|
|
11
11
|
"_error"
|
|
12
12
|
]);
|
|
13
13
|
const routeCache = /* @__PURE__ */ new Map();
|
|
14
|
+
let routeCacheGeneration = 0;
|
|
14
15
|
/**
|
|
15
16
|
* Invalidate cached routes for a given pages directory.
|
|
16
17
|
* Called by the file watcher when pages are added/removed.
|
|
17
18
|
*/
|
|
18
19
|
function invalidateRouteCache(pagesDir) {
|
|
20
|
+
routeCacheGeneration++;
|
|
19
21
|
for (const key of routeCache.keys()) if (key.startsWith(`pages:${pagesDir}:`) || key.startsWith(`api:${pagesDir}:`)) routeCache.delete(key);
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
@@ -32,20 +34,7 @@ function invalidateRouteCache(pagesDir) {
|
|
|
32
34
|
*/
|
|
33
35
|
async function pagesRouter(pagesDir, pageExtensions, matcher) {
|
|
34
36
|
matcher ??= createValidFileMatcher(pageExtensions);
|
|
35
|
-
|
|
36
|
-
const cached = routeCache.get(cacheKey);
|
|
37
|
-
if (cached) return cached.promise;
|
|
38
|
-
const promise = scanPageRoutes(pagesDir, matcher);
|
|
39
|
-
routeCache.set(cacheKey, {
|
|
40
|
-
routes: [],
|
|
41
|
-
promise
|
|
42
|
-
});
|
|
43
|
-
const routes = await promise;
|
|
44
|
-
routeCache.set(cacheKey, {
|
|
45
|
-
routes,
|
|
46
|
-
promise
|
|
47
|
-
});
|
|
48
|
-
return routes;
|
|
37
|
+
return getCachedRoutes(`pages:${pagesDir}:${JSON.stringify(matcher.extensions)}`, () => scanPageRoutes(pagesDir, matcher));
|
|
49
38
|
}
|
|
50
39
|
async function scanPageRoutes(pagesDir, matcher) {
|
|
51
40
|
const routes = [];
|
|
@@ -114,7 +103,7 @@ const trieCache = createRouteTrieCache();
|
|
|
114
103
|
* Returns the matched params or null if no match.
|
|
115
104
|
*/
|
|
116
105
|
function matchRoute(url, routes) {
|
|
117
|
-
return
|
|
106
|
+
return matchRouteWithTrieRawPathname(url, routes, trieCache);
|
|
118
107
|
}
|
|
119
108
|
/**
|
|
120
109
|
* Scan the pages/api/ directory and return API routes.
|
|
@@ -126,20 +115,25 @@ function matchRoute(url, routes) {
|
|
|
126
115
|
*/
|
|
127
116
|
async function apiRouter(pagesDir, pageExtensions, matcher) {
|
|
128
117
|
matcher ??= createValidFileMatcher(pageExtensions);
|
|
129
|
-
|
|
118
|
+
return getCachedRoutes(`api:${pagesDir}:${JSON.stringify(matcher.extensions)}`, () => scanApiRoutes(pagesDir, matcher));
|
|
119
|
+
}
|
|
120
|
+
async function getCachedRoutes(cacheKey, scan) {
|
|
130
121
|
const cached = routeCache.get(cacheKey);
|
|
131
|
-
if (cached) return cached
|
|
132
|
-
const promise =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
122
|
+
if (cached) return cached;
|
|
123
|
+
const promise = (async () => {
|
|
124
|
+
while (true) {
|
|
125
|
+
const scanGeneration = routeCacheGeneration;
|
|
126
|
+
const routes = await scan();
|
|
127
|
+
if (scanGeneration === routeCacheGeneration) return routes;
|
|
128
|
+
}
|
|
129
|
+
})();
|
|
130
|
+
routeCache.set(cacheKey, promise);
|
|
131
|
+
try {
|
|
132
|
+
return await promise;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
if (routeCache.get(cacheKey) === promise) routeCache.delete(cacheKey);
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
143
137
|
}
|
|
144
138
|
async function scanApiRoutes(pagesDir, matcher) {
|
|
145
139
|
const apiDir = path.join(pagesDir, "api");
|
|
@@ -23,5 +23,18 @@ declare function matchRouteWithTrie<R extends {
|
|
|
23
23
|
route: R;
|
|
24
24
|
params: Record<string, string | string[]>;
|
|
25
25
|
} | null;
|
|
26
|
+
/**
|
|
27
|
+
* Match a filesystem route against the request's raw encoded pathname.
|
|
28
|
+
*
|
|
29
|
+
* Next.js compares static route identity before decoding, so `/%61bout` does
|
|
30
|
+
* not select the filesystem route `/about`. Dynamic captures are still decoded
|
|
31
|
+
* exactly once by `trieMatch`, matching `getRouteMatcher` upstream.
|
|
32
|
+
*/
|
|
33
|
+
declare function matchRouteWithTrieRawPathname<R extends {
|
|
34
|
+
patternParts: string[];
|
|
35
|
+
}>(url: string, routes: R[], cache: RouteTrieCache<R>): {
|
|
36
|
+
route: R;
|
|
37
|
+
params: Record<string, string | string[]>;
|
|
38
|
+
} | null;
|
|
26
39
|
//#endregion
|
|
27
|
-
export { createRouteTrieCache, matchRouteWithTrie };
|
|
40
|
+
export { createRouteTrieCache, matchRouteWithTrie, matchRouteWithTrieRawPathname };
|
|
@@ -38,5 +38,17 @@ function matchRouteWithTrie(url, routes, cache) {
|
|
|
38
38
|
const urlParts = normalizedUrl.split("/").filter(Boolean);
|
|
39
39
|
return trieMatch(getOrBuildTrie(cache, routes), urlParts);
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Match a filesystem route against the request's raw encoded pathname.
|
|
43
|
+
*
|
|
44
|
+
* Next.js compares static route identity before decoding, so `/%61bout` does
|
|
45
|
+
* not select the filesystem route `/about`. Dynamic captures are still decoded
|
|
46
|
+
* exactly once by `trieMatch`, matching `getRouteMatcher` upstream.
|
|
47
|
+
*/
|
|
48
|
+
function matchRouteWithTrieRawPathname(url, routes, cache) {
|
|
49
|
+
const pathname = url.split("?")[0];
|
|
50
|
+
const urlParts = (pathname === "/" ? "/" : pathname.replace(/\/$/, "")).split("/").filter(Boolean);
|
|
51
|
+
return trieMatch(getOrBuildTrie(cache, routes), urlParts);
|
|
52
|
+
}
|
|
41
53
|
//#endregion
|
|
42
|
-
export { createRouteTrieCache, matchRouteWithTrie };
|
|
54
|
+
export { createRouteTrieCache, matchRouteWithTrie, matchRouteWithTrieRawPathname };
|
|
@@ -4,6 +4,7 @@ declare function routePatternParts(pathname: string): string[];
|
|
|
4
4
|
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
|
+
declare function matchRoutePatternRaw(urlParts: readonly string[], patternParts: readonly string[]): RoutePatternParams | null;
|
|
7
8
|
declare function matchRoutePatternPrefix(pathParts: readonly string[], patternParts: readonly string[]): boolean;
|
|
8
9
|
declare function matchRoutePatternWithOptionalDynamicSegments(pathParts: readonly string[], patternParts: readonly string[]): boolean;
|
|
9
10
|
/**
|
|
@@ -61,4 +62,4 @@ declare function normalizeStaticPathname(pathname: string): string;
|
|
|
61
62
|
*/
|
|
62
63
|
declare function normalizeStaticPathsEntry(entry: StaticPathsEntry, routePattern: string): NormalizedStaticPathsEntry;
|
|
63
64
|
//#endregion
|
|
64
|
-
export { RoutePatternParams, StaticPathsEntry, fillRoutePatternSegments, matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments, normalizeStaticPathname, normalizeStaticPathsEntry, routePattern, routePatternParts };
|
|
65
|
+
export { RoutePatternParams, StaticPathsEntry, fillRoutePatternSegments, matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternRaw, matchRoutePatternWithOptionalDynamicSegments, normalizeStaticPathname, normalizeStaticPathsEntry, routePattern, routePatternParts };
|
|
@@ -56,6 +56,11 @@ function fillRoutePatternSegments(pathname, params) {
|
|
|
56
56
|
return resolvedSegments.length > 0 ? `/${resolvedSegments.join("/")}` : "/";
|
|
57
57
|
}
|
|
58
58
|
function matchRoutePattern(urlParts, patternParts) {
|
|
59
|
+
const params = matchRoutePatternRaw(urlParts, patternParts);
|
|
60
|
+
if (params) decodeMatchedParams(params);
|
|
61
|
+
return params;
|
|
62
|
+
}
|
|
63
|
+
function matchRoutePatternRaw(urlParts, patternParts) {
|
|
59
64
|
const params = Object.create(null);
|
|
60
65
|
function matchFrom(urlIndex, patternIndex) {
|
|
61
66
|
if (patternIndex === patternParts.length) return urlIndex === urlParts.length;
|
|
@@ -83,9 +88,7 @@ function matchRoutePattern(urlParts, patternParts) {
|
|
|
83
88
|
if (urlIndex >= urlParts.length || urlParts[urlIndex] !== patternPart) return false;
|
|
84
89
|
return matchFrom(urlIndex + 1, patternIndex + 1);
|
|
85
90
|
}
|
|
86
|
-
|
|
87
|
-
decodeMatchedParams(params);
|
|
88
|
-
return params;
|
|
91
|
+
return matchFrom(0, 0) ? params : null;
|
|
89
92
|
}
|
|
90
93
|
function matchRoutePatternPrefix(pathParts, patternParts) {
|
|
91
94
|
let pathIndex = 0;
|
|
@@ -162,4 +165,4 @@ function normalizeStaticPathsEntry(entry, routePattern) {
|
|
|
162
165
|
return { params };
|
|
163
166
|
}
|
|
164
167
|
//#endregion
|
|
165
|
-
export { fillRoutePatternSegments, matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternWithOptionalDynamicSegments, normalizeStaticPathname, normalizeStaticPathsEntry, routePattern, routePatternParts };
|
|
168
|
+
export { fillRoutePatternSegments, matchRoutePattern, matchRoutePatternPrefix, matchRoutePatternRaw, matchRoutePatternWithOptionalDynamicSegments, normalizeStaticPathname, normalizeStaticPathsEntry, routePattern, routePatternParts };
|
|
@@ -63,5 +63,9 @@ declare function trieMatch<R>(root: TrieNode<R>, urlParts: string[]): {
|
|
|
63
63
|
route: R;
|
|
64
64
|
params: Record<string, string | string[]>;
|
|
65
65
|
} | null;
|
|
66
|
+
declare function trieMatchRaw<R>(root: TrieNode<R>, urlParts: string[]): {
|
|
67
|
+
route: R;
|
|
68
|
+
params: Record<string, string | string[]>;
|
|
69
|
+
} | null;
|
|
66
70
|
//#endregion
|
|
67
|
-
export { TrieNode, buildRouteTrie, trieMatch };
|
|
71
|
+
export { TrieNode, buildRouteTrie, trieMatch, trieMatchRaw };
|
|
@@ -92,10 +92,13 @@ function buildRouteTrie(routes) {
|
|
|
92
92
|
* @returns Match result with route and extracted params, or null
|
|
93
93
|
*/
|
|
94
94
|
function trieMatch(root, urlParts) {
|
|
95
|
-
const result =
|
|
95
|
+
const result = trieMatchRaw(root, urlParts);
|
|
96
96
|
if (result) decodeMatchedParams(result.params);
|
|
97
97
|
return result;
|
|
98
98
|
}
|
|
99
|
+
function trieMatchRaw(root, urlParts) {
|
|
100
|
+
return match(root, urlParts, 0, []);
|
|
101
|
+
}
|
|
99
102
|
function match(node, urlParts, index, entries) {
|
|
100
103
|
if (index === urlParts.length) {
|
|
101
104
|
if (node.route !== null) return {
|
|
@@ -140,4 +143,4 @@ function match(node, urlParts, index, entries) {
|
|
|
140
143
|
return null;
|
|
141
144
|
}
|
|
142
145
|
//#endregion
|
|
143
|
-
export { buildRouteTrie, trieMatch };
|
|
146
|
+
export { buildRouteTrie, trieMatch, trieMatchRaw };
|