vinext 1.0.0-beta.1 → 1.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -10
- package/dist/build/client-build-config.js +8 -1
- package/dist/build/report.d.ts +8 -1
- package/dist/build/report.js +12 -2
- package/dist/check.js +19 -2
- package/dist/cli.js +3 -2
- package/dist/config/config-matchers.d.ts +13 -37
- package/dist/config/config-matchers.js +35 -122
- package/dist/config/next-config.d.ts +15 -5
- package/dist/config/next-config.js +7 -1
- package/dist/config/request-context.d.ts +14 -0
- package/dist/config/request-context.js +26 -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 +30 -9
- package/dist/entries/app-rsc-manifest.js +36 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +13 -5
- package/dist/index.js +194 -101
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/init.d.ts +6 -4
- package/dist/init.js +20 -22
- 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/og-assets.js +1 -1
- package/dist/plugins/require-context.js +20 -15
- package/dist/plugins/transform-cache.d.ts +19 -0
- package/dist/plugins/transform-cache.js +36 -0
- package/dist/routing/app-route-graph.d.ts +27 -11
- package/dist/routing/app-route-graph.js +158 -22
- 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/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +3 -3
- package/dist/server/app-browser-entry.js +110 -37
- 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.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +2 -2
- 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-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +2 -2
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +19 -2
- package/dist/server/app-page-dispatch.js +45 -23
- package/dist/server/app-page-element-builder.d.ts +9 -2
- package/dist/server/app-page-element-builder.js +144 -33
- package/dist/server/app-page-head.d.ts +56 -4
- package/dist/server/app-page-head.js +216 -85
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
- package/dist/server/app-page-http-access-fallback-metadata.js +114 -0
- 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 +2 -2
- package/dist/server/app-page-render.js +1 -1
- package/dist/server/app-page-request.d.ts +15 -0
- package/dist/server/app-page-request.js +134 -14
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +16 -0
- package/dist/server/app-page-route-wiring.js +254 -52
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -1
- 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-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +40 -20
- 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-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +9 -1
- package/dist/server/app-route-module-loader.js +4 -0
- package/dist/server/app-router-entry.js +6 -3
- 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 +22 -2
- package/dist/server/app-rsc-handler.d.ts +10 -5
- package/dist/server/app-rsc-handler.js +73 -33
- 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 +29 -0
- package/dist/server/app-rsc-route-matching.js +93 -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 +6 -6
- package/dist/server/app-ssr-stream.d.ts +1 -7
- package/dist/server/app-ssr-stream.js +2 -10
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- 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.d.ts +3 -1
- package/dist/server/dev-server.js +170 -321
- package/dist/server/headers.d.ts +2 -2
- package/dist/server/headers.js +4 -3
- package/dist/server/isr-cache.d.ts +11 -34
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- 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-api-route.d.ts +2 -0
- package/dist/server/pages-api-route.js +4 -1
- package/dist/server/pages-data-route.d.ts +11 -1
- package/dist/server/pages-data-route.js +16 -1
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-get-initial-props.d.ts +16 -3
- package/dist/server/pages-get-initial-props.js +17 -10
- package/dist/server/pages-i18n.js +64 -16
- package/dist/server/pages-node-compat.d.ts +2 -0
- package/dist/server/pages-node-compat.js +7 -3
- package/dist/server/pages-page-data.d.ts +64 -10
- package/dist/server/pages-page-data.js +349 -80
- package/dist/server/pages-page-handler.d.ts +9 -3
- package/dist/server/pages-page-handler.js +131 -36
- package/dist/server/pages-page-response.d.ts +5 -3
- package/dist/server/pages-page-response.js +24 -27
- package/dist/server/pages-readiness.d.ts +3 -3
- package/dist/server/pages-request-pipeline.d.ts +8 -0
- package/dist/server/pages-request-pipeline.js +30 -12
- 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 +16 -4
- package/dist/server/prerender-route-params.js +2 -2
- package/dist/server/prod-server.d.ts +3 -5
- package/dist/server/prod-server.js +72 -35
- package/dist/server/request-pipeline.d.ts +15 -29
- package/dist/server/request-pipeline.js +24 -59
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/rsc-stream-hints.js +122 -18
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +29 -0
- package/dist/shims/app.d.ts +1 -1
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- 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 +18 -1
- package/dist/shims/cache-runtime.js +26 -8
- package/dist/shims/cache.d.ts +5 -5
- package/dist/shims/cache.js +27 -11
- 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 +2 -2
- package/dist/shims/fetch-cache.js +13 -4
- package/dist/shims/font-google-base.d.ts +11 -4
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.d.ts +10 -3
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/head.d.ts +13 -2
- package/dist/shims/head.js +3 -3
- package/dist/shims/headers.d.ts +37 -43
- package/dist/shims/headers.js +183 -74
- package/dist/shims/image.d.ts +27 -37
- package/dist/shims/image.js +21 -14
- package/dist/shims/internal/cookie-serialize.d.ts +12 -9
- package/dist/shims/internal/cookie-serialize.js +10 -7
- package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- 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 +34 -13
- package/dist/shims/metadata.d.ts +7 -17
- package/dist/shims/metadata.js +74 -24
- package/dist/shims/navigation-context-state.d.ts +1 -1
- package/dist/shims/navigation.d.ts +7 -3
- package/dist/shims/navigation.js +25 -14
- 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-context.d.ts +4 -1
- package/dist/shims/request-state-types.d.ts +1 -1
- package/dist/shims/router.d.ts +55 -21
- package/dist/shims/router.js +180 -44
- package/dist/shims/script.d.ts +6 -10
- package/dist/shims/script.js +7 -4
- package/dist/shims/server.d.ts +50 -26
- package/dist/shims/server.js +193 -55
- package/dist/shims/unified-request-context.d.ts +22 -3
- package/dist/shims/unified-request-context.js +84 -1
- 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/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- 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/html-limited-bots.js +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/dist/utils/regex-safety.d.ts +8 -0
- package/dist/utils/regex-safety.js +737 -0
- package/package.json +13 -2
|
@@ -459,13 +459,16 @@ 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
|
-
|
|
462
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage, matcher);
|
|
463
|
+
return withSlotLoadingEntries({
|
|
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
|
+
}, subPage, matcher);
|
|
469
472
|
}
|
|
470
473
|
return slot;
|
|
471
474
|
});
|
|
@@ -545,13 +548,16 @@ 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);
|
|
548
|
-
|
|
551
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage ?? null, matcher);
|
|
552
|
+
return withSlotLoadingEntries({
|
|
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
|
+
}, subPage ?? null, matcher);
|
|
555
561
|
});
|
|
556
562
|
const newRoute = {
|
|
557
563
|
ids: createAppRouteSemanticIds({
|
|
@@ -574,14 +580,19 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
574
580
|
ownerTreePath: childrenOwnerTreePath,
|
|
575
581
|
state: childrenDefault ? "default" : childrenCatchAll ? "active" : "unmatched"
|
|
576
582
|
},
|
|
577
|
-
loadingPath:
|
|
583
|
+
loadingPath: null,
|
|
584
|
+
loadingPaths: parentRoute.loadingPaths,
|
|
585
|
+
loadingTreePositions: parentRoute.loadingTreePositions,
|
|
578
586
|
errorPath: parentRoute.errorPath,
|
|
579
587
|
layoutErrorPaths: parentRoute.layoutErrorPaths,
|
|
580
588
|
notFoundPath: parentRoute.notFoundPath,
|
|
589
|
+
notFoundTreePosition: parentRoute.notFoundTreePosition,
|
|
581
590
|
notFoundPaths: parentRoute.notFoundPaths,
|
|
582
591
|
forbiddenPaths: parentRoute.forbiddenPaths,
|
|
583
592
|
forbiddenPath: parentRoute.forbiddenPath,
|
|
593
|
+
forbiddenTreePosition: parentRoute.forbiddenTreePosition,
|
|
584
594
|
unauthorizedPath: parentRoute.unauthorizedPath,
|
|
595
|
+
unauthorizedTreePosition: parentRoute.unauthorizedTreePosition,
|
|
585
596
|
unauthorizedPaths: parentRoute.unauthorizedPaths,
|
|
586
597
|
routeSegments: [...parentRoute.routeSegments, ...rawSegments],
|
|
587
598
|
childrenRouteSegments: childrenDefault ? parentRoute.routeSegments : void 0,
|
|
@@ -669,6 +680,55 @@ function findSlotConfigLayoutTreePositions(slotDir, layoutPaths) {
|
|
|
669
680
|
return relativeDir ? relativeDir.split(path.sep).filter(Boolean).length : 0;
|
|
670
681
|
});
|
|
671
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
|
+
}
|
|
713
|
+
function findSlotNotFoundBoundary(slotDir, pagePath, matcher) {
|
|
714
|
+
let dir = pagePath ? path.dirname(pagePath) : slotDir;
|
|
715
|
+
while (dir === slotDir || dir.startsWith(`${slotDir}${path.sep}`)) {
|
|
716
|
+
const boundaryPath = findFile(dir, "not-found", matcher);
|
|
717
|
+
if (boundaryPath) {
|
|
718
|
+
const relativeDir = path.relative(slotDir, dir);
|
|
719
|
+
return {
|
|
720
|
+
path: boundaryPath,
|
|
721
|
+
treePosition: relativeDir ? relativeDir.split(path.sep).filter(Boolean).length : 0
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
if (dir === slotDir) break;
|
|
725
|
+
dir = path.dirname(dir);
|
|
726
|
+
}
|
|
727
|
+
return {
|
|
728
|
+
path: null,
|
|
729
|
+
treePosition: null
|
|
730
|
+
};
|
|
731
|
+
}
|
|
672
732
|
/**
|
|
673
733
|
* Find a sibling catch-all page directly under `dir`, i.e. a `[...slug]` or
|
|
674
734
|
* `[[...slug]]` directory that contains a `page` file. Returns the absolute
|
|
@@ -723,6 +783,9 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
723
783
|
const templateTreePositions = computeLayoutTreePositions(appDir, templates);
|
|
724
784
|
const layoutTreePositions = computeLayoutTreePositions(appDir, layouts);
|
|
725
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);
|
|
726
789
|
const errorEntries = discoverSegmentErrors(segments, appDir, matcher);
|
|
727
790
|
const errorPaths = errorEntries.map((entry) => entry.path);
|
|
728
791
|
const errorTreePositions = errorEntries.map((entry) => entry.treePosition);
|
|
@@ -730,9 +793,12 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
730
793
|
const effectivePagePath = pagePath ?? (routePath ? null : findFile(routeDir, "default", matcher));
|
|
731
794
|
const loadingPath = findFile(routeDir, "loading", matcher);
|
|
732
795
|
const errorPath = findFile(routeDir, "error", matcher);
|
|
733
|
-
const
|
|
734
|
-
const
|
|
735
|
-
const
|
|
796
|
+
const notFoundEntry = discoverBoundaryFileEntry(segments, appDir, "not-found", matcher);
|
|
797
|
+
const notFoundPath = notFoundEntry?.path ?? null;
|
|
798
|
+
const forbiddenEntry = discoverBoundaryFileEntry(segments, appDir, "forbidden", matcher);
|
|
799
|
+
const forbiddenPath = forbiddenEntry?.path ?? null;
|
|
800
|
+
const unauthorizedEntry = discoverBoundaryFileEntry(segments, appDir, "unauthorized", matcher);
|
|
801
|
+
const unauthorizedPath = unauthorizedEntry?.path ?? null;
|
|
736
802
|
const notFoundPaths = discoverBoundaryFilePerLayout(layouts, "not-found", matcher);
|
|
737
803
|
const forbiddenPaths = discoverBoundaryFilePerLayout(layouts, "forbidden", matcher);
|
|
738
804
|
const unauthorizedPaths = discoverBoundaryFilePerLayout(layouts, "unauthorized", matcher);
|
|
@@ -754,15 +820,20 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
754
820
|
templates,
|
|
755
821
|
parallelSlots,
|
|
756
822
|
loadingPath,
|
|
823
|
+
loadingPaths,
|
|
824
|
+
loadingTreePositions,
|
|
757
825
|
errorPath,
|
|
758
826
|
layoutErrorPaths,
|
|
759
827
|
errorPaths,
|
|
760
828
|
errorTreePositions,
|
|
761
829
|
notFoundPath,
|
|
830
|
+
notFoundTreePosition: notFoundEntry?.treePosition ?? null,
|
|
762
831
|
notFoundPaths,
|
|
763
832
|
forbiddenPaths,
|
|
764
833
|
forbiddenPath,
|
|
834
|
+
forbiddenTreePosition: forbiddenEntry?.treePosition ?? null,
|
|
765
835
|
unauthorizedPath,
|
|
836
|
+
unauthorizedTreePosition: unauthorizedEntry?.treePosition ?? null,
|
|
766
837
|
unauthorizedPaths,
|
|
767
838
|
routeSegments: segments,
|
|
768
839
|
templateTreePositions,
|
|
@@ -899,6 +970,31 @@ function discoverSegmentErrors(segments, appDir, matcher) {
|
|
|
899
970
|
return errors;
|
|
900
971
|
}
|
|
901
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
|
+
/**
|
|
902
998
|
* Discover error.tsx files aligned with the layouts array.
|
|
903
999
|
*
|
|
904
1000
|
* Route manifests still model layout-owned boundary facts by layout index.
|
|
@@ -921,7 +1017,7 @@ function discoverLayoutAlignedErrors(segments, appDir, matcher) {
|
|
|
921
1017
|
* by walking from the route's directory up to the app root.
|
|
922
1018
|
* Returns the first (closest) file found, or null.
|
|
923
1019
|
*/
|
|
924
|
-
function
|
|
1020
|
+
function discoverBoundaryFileEntry(segments, appDir, fileName, matcher) {
|
|
925
1021
|
const dirs = [];
|
|
926
1022
|
let dir = appDir;
|
|
927
1023
|
dirs.push(dir);
|
|
@@ -931,7 +1027,10 @@ function discoverBoundaryFile(segments, appDir, fileName, matcher) {
|
|
|
931
1027
|
}
|
|
932
1028
|
for (let i = dirs.length - 1; i >= 0; i--) {
|
|
933
1029
|
const f = findFile(dirs[i], fileName, matcher);
|
|
934
|
-
if (f) return
|
|
1030
|
+
if (f) return {
|
|
1031
|
+
path: f,
|
|
1032
|
+
treePosition: i
|
|
1033
|
+
};
|
|
935
1034
|
}
|
|
936
1035
|
return null;
|
|
937
1036
|
}
|
|
@@ -1000,7 +1099,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
|
|
|
1000
1099
|
slotParamNames = [...ownerUrl?.params ?? [], ...mirror.slotParamNames];
|
|
1001
1100
|
}
|
|
1002
1101
|
const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, mirror?.pagePath ?? null, matcher);
|
|
1003
|
-
const inheritedSlot = {
|
|
1102
|
+
const inheritedSlot = withSlotLoadingEntries({
|
|
1004
1103
|
...slot,
|
|
1005
1104
|
pagePath: mirror?.pagePath ?? null,
|
|
1006
1105
|
configLayoutPaths,
|
|
@@ -1009,7 +1108,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
|
|
|
1009
1108
|
routeSegments: mirror?.segments ?? null,
|
|
1010
1109
|
slotPatternParts,
|
|
1011
1110
|
slotParamNames
|
|
1012
|
-
};
|
|
1111
|
+
}, mirror?.pagePath ?? null, matcher);
|
|
1013
1112
|
slotMap.set(slot.key, inheritedSlot);
|
|
1014
1113
|
}
|
|
1015
1114
|
}
|
|
@@ -1172,12 +1271,15 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1172
1271
|
const ownerSegments = path.relative(appDir, dir).split(path.sep).filter((segment) => segment.length > 0);
|
|
1173
1272
|
const ownerTreePath = createAppRouteGraphTreePath(ownerSegments, ownerSegments.length);
|
|
1174
1273
|
const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
|
|
1274
|
+
const loadingEntries = findSlotLoadingEntries(slotDir, pagePath, matcher);
|
|
1275
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slotDir, pagePath, matcher);
|
|
1175
1276
|
slots.push({
|
|
1176
1277
|
id: createAppRouteGraphSlotId(slotName, ownerTreePath),
|
|
1177
1278
|
key: `${slotName}@${path.relative(appDir, slotDir)}`,
|
|
1178
1279
|
name: slotName,
|
|
1179
1280
|
ownerDir: slotDir,
|
|
1180
1281
|
ownerTreePath,
|
|
1282
|
+
ownerTreePosition: ownerSegments.length,
|
|
1181
1283
|
hasPage: pagePath !== null,
|
|
1182
1284
|
pagePath,
|
|
1183
1285
|
defaultPath,
|
|
@@ -1185,7 +1287,11 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1185
1287
|
configLayoutPaths,
|
|
1186
1288
|
configLayoutTreePositions: findSlotConfigLayoutTreePositions(slotDir, configLayoutPaths),
|
|
1187
1289
|
loadingPath: findFile(slotDir, "loading", matcher),
|
|
1290
|
+
loadingPaths: loadingEntries.map((loading) => loading.path),
|
|
1291
|
+
loadingTreePositions: loadingEntries.map((loading) => loading.treePosition),
|
|
1188
1292
|
errorPath: findFile(slotDir, "error", matcher),
|
|
1293
|
+
notFoundPath: notFoundBoundary.path,
|
|
1294
|
+
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
1189
1295
|
interceptingRoutes,
|
|
1190
1296
|
layoutIndex: -1,
|
|
1191
1297
|
routeSegments: pagePath ? [] : null
|
|
@@ -1240,7 +1346,7 @@ function isInterceptionMarkerDir(name) {
|
|
|
1240
1346
|
function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
|
|
1241
1347
|
if (!fs.existsSync(slotDir)) return [];
|
|
1242
1348
|
const results = [];
|
|
1243
|
-
scanForInterceptingPages(slotDir, routeDir, appDir, results, matcher);
|
|
1349
|
+
scanForInterceptingPages(slotDir, slotDir, routeDir, appDir, results, matcher);
|
|
1244
1350
|
return results;
|
|
1245
1351
|
}
|
|
1246
1352
|
/**
|
|
@@ -1277,7 +1383,7 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1277
1383
|
const restOfName = entry.name.slice(marker.prefix.length);
|
|
1278
1384
|
const parentDir = dir;
|
|
1279
1385
|
const results = [];
|
|
1280
|
-
collectInterceptingPages(childDir, childDir, marker.convention, restOfName, parentDir, appDir, parentDir, results, matcher);
|
|
1386
|
+
collectInterceptingPages(childDir, childDir, marker.convention, restOfName, parentDir, appDir, parentDir, null, results, matcher);
|
|
1281
1387
|
for (const ir of results) {
|
|
1282
1388
|
ir.slotId = createAppRouteGraphSiblingInterceptSlotId(ir.sourceMatchPattern);
|
|
1283
1389
|
const owner = findOwnerRouteForDir(parentDir, appDir, routes, routesByDir);
|
|
@@ -1334,7 +1440,7 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1334
1440
|
* Recursively scan a directory tree for page.tsx files that are inside
|
|
1335
1441
|
* intercepting route directories.
|
|
1336
1442
|
*/
|
|
1337
|
-
function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
|
|
1443
|
+
function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, results, matcher, parentLoadingEntries = []) {
|
|
1338
1444
|
if (!fs.existsSync(currentDir)) return;
|
|
1339
1445
|
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
1340
1446
|
for (const entry of entries) {
|
|
@@ -1344,8 +1450,14 @@ function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher
|
|
|
1344
1450
|
const interceptDir = path.join(currentDir, entry.name);
|
|
1345
1451
|
if (interceptMatch) {
|
|
1346
1452
|
const restOfName = entry.name.slice(interceptMatch.prefix.length);
|
|
1347
|
-
collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, results, matcher);
|
|
1348
|
-
} 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
|
+
}
|
|
1349
1461
|
}
|
|
1350
1462
|
}
|
|
1351
1463
|
/**
|
|
@@ -1359,21 +1471,45 @@ function matchInterceptConvention(name) {
|
|
|
1359
1471
|
* Collect page.tsx files inside an intercepting route directory tree
|
|
1360
1472
|
* and compute their target URL patterns.
|
|
1361
1473
|
*/
|
|
1362
|
-
function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, parentLayoutPaths = []) {
|
|
1474
|
+
function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, parentLayoutPaths = [], parentLoadingEntries = [], treePositionOffset = 0) {
|
|
1363
1475
|
const currentLayoutPath = findFile(currentDir, "layout", matcher);
|
|
1364
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;
|
|
1365
1484
|
const page = findFile(currentDir, "page", matcher);
|
|
1366
1485
|
if (page) {
|
|
1367
1486
|
const targetPattern = computeInterceptTarget(convention, interceptSegment, currentDir, interceptRoot, routeDir, appDir);
|
|
1368
1487
|
if (targetPattern) {
|
|
1369
1488
|
const sourceMatchPattern = computeInterceptSourceMatchPattern(interceptParentDir, appDir);
|
|
1489
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slotRootDir ?? interceptRoot, page, matcher);
|
|
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
|
+
];
|
|
1370
1496
|
results.push({
|
|
1371
|
-
branchSegments
|
|
1497
|
+
branchSegments,
|
|
1372
1498
|
convention,
|
|
1373
1499
|
layoutPaths: [...layoutPaths],
|
|
1374
1500
|
layoutSegments: layoutPaths.map((layoutPath) => {
|
|
1375
|
-
|
|
1501
|
+
const relativeDir = path.relative(interceptRoot, path.dirname(layoutPath));
|
|
1502
|
+
return [
|
|
1503
|
+
...slotParentSegments,
|
|
1504
|
+
interceptSegment,
|
|
1505
|
+
...relativeDir.split(path.sep).filter(Boolean)
|
|
1506
|
+
];
|
|
1376
1507
|
}),
|
|
1508
|
+
loadingPaths: loadingEntries.map((loading) => loading.path),
|
|
1509
|
+
loadingTreePositions: loadingEntries.map((loading) => loading.treePosition),
|
|
1510
|
+
notFoundBranchSegments: branchSegments,
|
|
1511
|
+
notFoundPath: notFoundBoundary.path,
|
|
1512
|
+
notFoundTreePosition: notFoundBoundary.treePosition === null ? null : slotRootDir ? notFoundBoundary.treePosition : notFoundBoundary.treePosition + 1,
|
|
1377
1513
|
targetPattern: targetPattern.pattern,
|
|
1378
1514
|
sourceMatchPattern,
|
|
1379
1515
|
pagePath: page,
|
|
@@ -1387,7 +1523,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1387
1523
|
for (const entry of entries) {
|
|
1388
1524
|
if (!entry.isDirectory()) continue;
|
|
1389
1525
|
if (entry.name.startsWith("_")) continue;
|
|
1390
|
-
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, layoutPaths);
|
|
1526
|
+
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, layoutPaths, loadingEntries, treePositionOffset);
|
|
1391
1527
|
}
|
|
1392
1528
|
}
|
|
1393
1529
|
/**
|
|
@@ -1615,4 +1751,4 @@ function computeAppRouteStaticSiblings(allRoutes, matchedRoute) {
|
|
|
1615
1751
|
return Array.from(siblings);
|
|
1616
1752
|
}
|
|
1617
1753
|
//#endregion
|
|
1618
|
-
export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment };
|
|
1754
|
+
export { buildAppRouteGraph, computeAppRouteStaticSiblings, computeRootParamNames, convertSegmentsToRouteParts, findOwnerRouteForDir, isInvisibleSegment, patternsStructurallyEquivalent };
|
|
@@ -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 };
|