vinext 1.0.0-beta.0 → 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 +21 -2
- package/dist/build/assets-ignore.js +1 -1
- package/dist/build/clean-output.js +1 -1
- package/dist/build/client-build-config.js +7 -4
- package/dist/build/css-url-assets.js +3 -3
- package/dist/build/google-fonts/fallback-metrics.js +1 -1
- package/dist/build/google-fonts/font-metadata.js +1 -1
- package/dist/build/inject-pregenerated-paths.js +1 -1
- package/dist/build/inline-css.js +2 -5
- package/dist/build/module-dependency-cache.d.ts +4 -0
- package/dist/build/module-dependency-cache.js +13 -0
- package/dist/build/next-client-runtime-manifests.js +1 -1
- package/dist/build/pages-client-assets-module.js +1 -1
- package/dist/build/precompress.js +7 -7
- package/dist/build/prerender-paths.d.ts +2 -2
- package/dist/build/prerender-paths.js +10 -15
- package/dist/build/prerender-server-pool.js +2 -2
- package/dist/build/prerender.js +1 -1
- package/dist/build/preview-credentials.d.ts +11 -0
- package/dist/build/preview-credentials.js +19 -0
- package/dist/build/report.d.ts +8 -4
- package/dist/build/report.js +14 -6
- package/dist/build/run-prerender.d.ts +2 -11
- package/dist/build/run-prerender.js +2 -10
- package/dist/build/server-manifest.js +1 -1
- package/dist/build/ssr-manifest.d.ts +4 -0
- package/dist/build/ssr-manifest.js +10 -6
- package/dist/build/standalone.js +37 -37
- package/dist/cache/cache-adapters-virtual.d.ts +1 -17
- package/dist/cache/cache-adapters-virtual.js +20 -11
- package/dist/check.d.ts +0 -16
- package/dist/check.js +27 -36
- package/dist/cli.js +61 -55
- package/dist/client/instrumentation-client-inject.js +1 -1
- package/dist/client/pages-router-link-navigation.js +1 -1
- package/dist/config/config-matchers.d.ts +13 -37
- package/dist/config/config-matchers.js +33 -123
- package/dist/config/dotenv.js +1 -1
- package/dist/config/next-config.d.ts +7 -3
- package/dist/config/next-config.js +84 -15
- package/dist/config/prerender.d.ts +2 -2
- package/dist/config/prerender.js +7 -15
- package/dist/config/request-context.d.ts +14 -0
- package/dist/config/request-context.js +26 -0
- package/dist/config/server-external-packages.js +1 -1
- package/dist/config/tsconfig-paths.js +2 -2
- package/dist/{node_modules/.pnpm/am-i-vibing@0.5.0/node_modules → deps/.pnpm/am-i-vibing@0.5.0/deps}/am-i-vibing/dist/detector-1yx2Hoe0.js +2 -2
- package/dist/deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js +50 -0
- package/dist/entries/app-rsc-entry.js +39 -14
- package/dist/entries/app-rsc-manifest.js +21 -4
- package/dist/entries/pages-client-entry.js +6 -5
- package/dist/entries/pages-server-entry.js +18 -28
- package/dist/entries/runtime-entry-module.js +2 -2
- package/dist/index.js +314 -230
- package/dist/init-cloudflare.js +1 -1
- package/dist/init-platform.js +1 -1
- package/dist/init.d.ts +6 -4
- package/dist/init.js +25 -27
- package/dist/plugins/ast-utils.js +1 -1
- package/dist/plugins/dynamic-preload-metadata.js +7 -7
- package/dist/plugins/extensionless-dynamic-import.js +1 -1
- package/dist/plugins/fonts.js +3 -3
- package/dist/plugins/ignore-dynamic-requests.js +13 -8
- package/dist/plugins/import-meta-url.js +16 -17
- package/dist/plugins/middleware-export-validation.js +1 -1
- package/dist/plugins/middleware-server-only.js +2 -2
- package/dist/plugins/og-asset-ownership.js +7 -6
- package/dist/plugins/og-assets.js +5 -6
- package/dist/plugins/optimize-imports.js +12 -13
- package/dist/plugins/postcss.js +1 -1
- package/dist/plugins/remove-console.js +1 -1
- package/dist/plugins/require-context.js +1 -1
- package/dist/plugins/sass.js +2 -2
- package/dist/plugins/server-externals-manifest.js +1 -1
- package/dist/plugins/strip-server-exports.js +1 -1
- package/dist/plugins/styled-jsx.js +3 -2
- package/dist/plugins/typeof-window.js +1 -1
- package/dist/routing/app-route-graph.d.ts +16 -15
- package/dist/routing/app-route-graph.js +86 -88
- package/dist/routing/app-router.d.ts +0 -5
- package/dist/routing/app-router.js +12 -11
- package/dist/routing/file-matcher.d.ts +13 -28
- package/dist/routing/file-matcher.js +31 -44
- package/dist/routing/pages-router.js +26 -32
- 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.js +41 -33
- package/dist/server/app-browser-entry.js +18 -11
- 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 +4 -2
- package/dist/server/app-browser-server-action-client.js +1 -1
- package/dist/server/app-elements-wire.d.ts +2 -1
- package/dist/server/app-fallback-renderer.d.ts +11 -0
- package/dist/server/app-fallback-renderer.js +16 -2
- package/dist/server/app-middleware.js +2 -1
- package/dist/server/app-page-boundary-render.d.ts +26 -2
- package/dist/server/app-page-boundary-render.js +190 -55
- 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 +12 -2
- package/dist/server/app-page-dispatch.js +37 -51
- package/dist/server/app-page-element-builder.d.ts +8 -2
- package/dist/server/app-page-element-builder.js +112 -27
- package/dist/server/app-page-execution.d.ts +3 -1
- package/dist/server/app-page-execution.js +31 -39
- 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 +134 -14
- package/dist/server/app-page-route-wiring.d.ts +9 -0
- package/dist/server/app-page-route-wiring.js +44 -4
- 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 +41 -15
- 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-route-tree-prefetch.js +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +1 -1
- package/dist/server/app-rsc-cache-busting.js +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 +51 -23
- package/dist/server/app-rsc-redirect-flight.d.ts +66 -0
- package/dist/server/app-rsc-redirect-flight.js +71 -0
- 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 +15 -5
- package/dist/server/app-server-action-execution.d.ts +3 -1
- package/dist/server/app-server-action-execution.js +8 -6
- package/dist/server/app-ssr-entry.js +4 -1
- package/dist/server/app-ssr-stream.d.ts +2 -7
- package/dist/server/app-ssr-stream.js +5 -10
- package/dist/server/cache-proof.js +1 -1
- package/dist/server/config-headers.d.ts +24 -0
- package/dist/server/config-headers.js +52 -0
- package/dist/server/default-global-error-module.d.ts +3 -1
- package/dist/server/default-not-found-module.d.ts +3 -1
- package/dist/server/dev-initial-server-error.d.ts +1 -1
- package/dist/server/dev-lockfile.js +2 -3
- package/dist/server/dev-route-files.js +1 -1
- package/dist/server/dev-server.js +281 -173
- package/dist/server/dev-stack-sourcemap.js +4 -3
- package/dist/server/headers.d.ts +3 -1
- package/dist/server/headers.js +3 -1
- package/dist/server/image-optimization.js +2 -2
- package/dist/server/implicit-tags.js +2 -1
- package/dist/server/instrumentation.js +2 -2
- package/dist/server/isr-cache.d.ts +1 -1
- package/dist/server/isr-cache.js +1 -1
- package/dist/server/metadata-routes.js +4 -4
- 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-response-headers.js +1 -1
- package/dist/server/middleware-runtime.js +5 -4
- package/dist/server/middleware.d.ts +2 -1
- package/dist/server/middleware.js +3 -2
- package/dist/server/navigation-planner.d.ts +2 -0
- package/dist/server/navigation-planner.js +8 -2
- package/dist/server/next-error-digest.d.ts +9 -24
- package/dist/server/next-error-digest.js +23 -15
- package/dist/server/operation-token.js +1 -1
- package/dist/server/pages-body-parser-config.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-dev-hydration.d.ts +14 -0
- package/dist/server/pages-dev-hydration.js +83 -0
- package/dist/server/pages-dev-module-url.js +2 -2
- 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-node-compat.d.ts +13 -6
- package/dist/server/pages-node-compat.js +44 -45
- package/dist/server/pages-page-data.d.ts +4 -2
- package/dist/server/pages-page-data.js +20 -10
- package/dist/server/pages-page-handler.d.ts +4 -2
- package/dist/server/pages-page-handler.js +74 -43
- package/dist/server/pages-page-response.d.ts +2 -2
- package/dist/server/pages-page-response.js +1 -1
- package/dist/server/pages-preview.d.ts +26 -0
- package/dist/server/pages-preview.js +165 -0
- package/dist/server/pages-readiness.d.ts +5 -4
- package/dist/server/pages-readiness.js +4 -2
- 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 +44 -30
- package/dist/server/request-pipeline.d.ts +15 -29
- package/dist/server/request-pipeline.js +25 -60
- package/dist/server/rsc-stream-hints.js +122 -18
- package/dist/server/seed-cache.js +1 -1
- package/dist/server/socket-error-backstop.js +1 -1
- package/dist/server/static-file-cache.js +7 -8
- package/dist/server/worker-utils.js +1 -1
- 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 +28 -11
- package/dist/shims/constants.js +1 -1
- 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-boundary.d.ts +4 -4
- package/dist/shims/error.d.ts +22 -6
- package/dist/shims/error.js +53 -15
- package/dist/shims/fetch-cache.js +13 -4
- package/dist/shims/font-google-base.d.ts +11 -4
- package/dist/shims/font-local.d.ts +10 -3
- package/dist/shims/font-utils.js +1 -1
- package/dist/shims/form.d.ts +1 -1
- package/dist/shims/head.d.ts +13 -2
- package/dist/shims/head.js +6 -6
- package/dist/shims/headers.d.ts +28 -31
- package/dist/shims/headers.js +115 -53
- 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/pages-data-fetch-dedup.js +5 -3
- package/dist/shims/internal/utils.d.ts +3 -3
- package/dist/shims/layout-segment-context.d.ts +1 -1
- package/dist/shims/legacy-image.d.ts +3 -37
- package/dist/shims/legacy-image.js +6 -16
- package/dist/shims/link.d.ts +16 -21
- package/dist/shims/link.js +25 -13
- package/dist/shims/navigation-context-state.d.ts +1 -1
- package/dist/shims/navigation-errors.d.ts +2 -1
- package/dist/shims/navigation-errors.js +9 -13
- package/dist/shims/navigation.d.ts +8 -4
- package/dist/shims/navigation.js +29 -18
- 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-state.d.ts +1 -0
- package/dist/shims/router.d.ts +57 -22
- package/dist/shims/router.js +178 -46
- package/dist/shims/script.d.ts +6 -10
- package/dist/shims/script.js +7 -4
- package/dist/shims/server.d.ts +45 -22
- package/dist/shims/server.js +46 -7
- package/dist/shims/slot.d.ts +1 -1
- package/dist/shims/thenable-params.js +1 -1
- 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 +84 -25
- package/dist/utils/client-build-manifest.js +2 -2
- package/dist/utils/client-entry-manifest.js +1 -1
- package/dist/utils/client-runtime-metadata.js +1 -1
- package/dist/utils/commonjs-loader.js +3 -3
- 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/mdx-scan.js +1 -1
- package/dist/utils/path.d.ts +1 -13
- package/dist/utils/path.js +1 -15
- package/dist/utils/plugin-options.d.ts +4 -0
- package/dist/utils/plugin-options.js +8 -0
- package/dist/utils/project.d.ts +0 -6
- package/dist/utils/project.js +2 -8
- package/dist/utils/public-routes.js +2 -2
- package/dist/utils/react-version.js +1 -1
- package/dist/utils/redirect-digest.d.ts +9 -0
- package/dist/utils/redirect-digest.js +25 -0
- package/dist/utils/regex-safety.d.ts +8 -0
- package/dist/utils/regex-safety.js +737 -0
- package/dist/utils/vinext-root.js +1 -1
- package/dist/utils/vite-version.d.ts +5 -2
- package/dist/utils/vite-version.js +43 -17
- package/package.json +16 -4
- /package/dist/build/google-fonts/{fallback-metrics-data.js → fallback-metrics-data.json.js} +0 -0
- /package/dist/build/google-fonts/{font-data.js → font-data.json.js} +0 -0
- /package/dist/{node_modules/.pnpm/process-ancestry@0.1.0/node_modules → deps/.pnpm/process-ancestry@0.1.0/deps}/process-ancestry/dist/index.js +0 -0
package/dist/plugins/sass.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
1
2
|
import { markCssUrlAssetReferences, rebaseCssUrlAssetReferences } from "../build/css-url-assets.js";
|
|
2
3
|
import { createRequire } from "node:module";
|
|
3
4
|
import fs from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
5
|
import { preprocessCSS } from "vite";
|
|
6
6
|
import { pathToFileURL } from "node:url";
|
|
7
7
|
//#region src/plugins/sass.ts
|
|
@@ -98,7 +98,7 @@ function sassStylesheetCandidates(importPath) {
|
|
|
98
98
|
return candidates;
|
|
99
99
|
}
|
|
100
100
|
function deriveSassNamespace(importUrl) {
|
|
101
|
-
const parts = importUrl
|
|
101
|
+
const parts = toSlash(importUrl).replace(/\/$/, "").split("/");
|
|
102
102
|
let basename = parts.pop() ?? "stylesheet";
|
|
103
103
|
basename = basename.replace(/^_/, "").replace(/\..*$/, "");
|
|
104
104
|
if (basename === "index" && parts.length > 0) basename = (parts.pop() ?? basename).replace(/^_/, "").replace(/\..*$/, "");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
1
2
|
import { builtinModules } from "node:module";
|
|
2
3
|
import fs from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
4
|
//#region src/plugins/server-externals-manifest.ts
|
|
5
5
|
const BUILTIN_MODULES = new Set(builtinModules.flatMap((name) => name.startsWith("node:") ? [name, name.slice(5)] : [name, `node:${name}`]));
|
|
6
6
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parseAst } from "vite";
|
|
2
2
|
import MagicString from "magic-string";
|
|
3
3
|
//#region src/plugins/strip-server-exports.ts
|
|
4
|
-
const SERVER_EXPORTS = new Set([
|
|
4
|
+
const SERVER_EXPORTS = /* @__PURE__ */ new Set([
|
|
5
5
|
"getServerSideProps",
|
|
6
6
|
"getStaticProps",
|
|
7
7
|
"getStaticPaths",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
|
-
import path from "node:path";
|
|
3
3
|
import { parseAst } from "vite";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
//#region src/plugins/styled-jsx.ts
|
|
@@ -75,7 +75,8 @@ function createStyledJsxPlugin(initialProjectRoot, options = {}) {
|
|
|
75
75
|
if (!compilerPromise) {
|
|
76
76
|
const requireFromNext = getNextRequire();
|
|
77
77
|
if (!requireFromNext) throw new Error("[vinext] styled-jsx requires an installed next package so vinext can use its matching compiler.");
|
|
78
|
-
|
|
78
|
+
const compilerPath = requireFromNext.resolve("next/dist/build/swc");
|
|
79
|
+
compilerPromise = importModule(pathToFileURL(compilerPath).href).then(async (compiler) => {
|
|
79
80
|
await compiler.loadBindings();
|
|
80
81
|
return compiler;
|
|
81
82
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
1
2
|
import { collectBindingNames, forEachAstChild, hasRange, isAstRecord, isIdentifierNamed, nodeArray } from "./ast-utils.js";
|
|
2
3
|
import { collectDirectScopeBindings, collectLoopScopeBindings, collectSwitchScopeBindings, collectVarScopeBindings, createAstScope, hasAstBinding, isFunctionNode } from "./ast-scope.js";
|
|
3
|
-
import path from "node:path";
|
|
4
4
|
import { parseAst } from "vite";
|
|
5
5
|
import MagicString from "magic-string";
|
|
6
6
|
//#region src/plugins/typeof-window.ts
|
|
@@ -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.
|
|
@@ -292,10 +300,6 @@ type RouteManifest = {
|
|
|
292
300
|
};
|
|
293
301
|
/**
|
|
294
302
|
* Build the App Router route graph by scanning `appDir`.
|
|
295
|
-
*
|
|
296
|
-
* `appDir` must be forward-slash. Every path in the graph is derived from it
|
|
297
|
-
* with `path.posix.*` and `findFile`, so a native appDir would produce mixed
|
|
298
|
-
* separators on Windows. Production callers normalize it at their entry.
|
|
299
303
|
*/
|
|
300
304
|
declare function buildAppRouteGraph(appDir: string, matcher: ValidFileMatcher): Promise<{
|
|
301
305
|
routes: AppRouteGraphRoute[];
|
|
@@ -314,12 +318,9 @@ declare function computeRootParamNames(routeSegments: readonly string[], layoutT
|
|
|
314
318
|
* no sibling pages at all (e.g. `deep/path/(...)target` with no
|
|
315
319
|
* `deep/path/page.tsx`).
|
|
316
320
|
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* backslashes on Windows. Without normalizing, the `current === appDir`
|
|
321
|
-
* termination never fires there and the walk overshoots the app root.
|
|
322
|
-
* `routesByDir` keys must be forward-slash dirnames of the route file paths.
|
|
321
|
+
* Comparisons happen in forward-slash space — inputs go through `toSlash` so
|
|
322
|
+
* callers still holding native-separator paths hit the `current === appDir`
|
|
323
|
+
* termination instead of overshooting the app root.
|
|
323
324
|
*
|
|
324
325
|
* Exported for tests.
|
|
325
326
|
*/
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
import { decodeRouteSegment, isInvisibleSegment, sortRoutes } from "./utils.js";
|
|
3
3
|
import { findFileWithExts, scanWithExtensions } from "./file-matcher.js";
|
|
4
4
|
import { validateRoutePatterns } from "./route-validation.js";
|
|
5
5
|
import { compareStrings } from "../utils/compare.js";
|
|
6
6
|
import fs from "node:fs";
|
|
7
|
-
import path from "node:path";
|
|
8
7
|
import { createHash } from "node:crypto";
|
|
9
8
|
//#region src/routing/app-route-graph.ts
|
|
10
9
|
/**
|
|
@@ -363,10 +362,6 @@ function createRouteManifestGraphVersion(segmentGraph) {
|
|
|
363
362
|
}
|
|
364
363
|
/**
|
|
365
364
|
* Build the App Router route graph by scanning `appDir`.
|
|
366
|
-
*
|
|
367
|
-
* `appDir` must be forward-slash. Every path in the graph is derived from it
|
|
368
|
-
* with `path.posix.*` and `findFile`, so a native appDir would produce mixed
|
|
369
|
-
* separators on Windows. Production callers normalize it at their entry.
|
|
370
365
|
*/
|
|
371
366
|
async function buildAppRouteGraph(appDir, matcher) {
|
|
372
367
|
const routes = [];
|
|
@@ -384,8 +379,8 @@ async function buildAppRouteGraph(appDir, matcher) {
|
|
|
384
379
|
const routePatterns = new Set(routes.map((route) => route.pattern));
|
|
385
380
|
const ghostParentRoutes = [];
|
|
386
381
|
for await (const file of scanWithExtensions("**/layout", appDir, scanMatcher.extensions, excludeDir)) {
|
|
387
|
-
const dir = path.
|
|
388
|
-
const routeDir = dir === "." ? appDir : path.
|
|
382
|
+
const dir = path.dirname(file);
|
|
383
|
+
const routeDir = dir === "." ? appDir : path.join(appDir, dir);
|
|
389
384
|
if (!hasParallelSlotDirectory(routeDir)) continue;
|
|
390
385
|
if (discoverParallelSlots(routeDir, appDir, scanMatcher, true).length === 0) continue;
|
|
391
386
|
const route = directoryToAppRoute(dir, appDir, scanMatcher, null, null, true);
|
|
@@ -440,9 +435,9 @@ function validatePageRouteConflicts(routes, appDir) {
|
|
|
440
435
|
}
|
|
441
436
|
}
|
|
442
437
|
function formatAppFilePath(filePath, appDir) {
|
|
443
|
-
const relativePath =
|
|
438
|
+
const relativePath = path.relative(appDir, filePath);
|
|
444
439
|
const parsedPath = path.parse(relativePath);
|
|
445
|
-
const withoutExtension =
|
|
440
|
+
const withoutExtension = path.join(parsedPath.dir, parsedPath.name);
|
|
446
441
|
return withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`;
|
|
447
442
|
}
|
|
448
443
|
/**
|
|
@@ -464,11 +459,14 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
464
459
|
const subPage = slotPages.get(slot.key);
|
|
465
460
|
if (subPage !== void 0) {
|
|
466
461
|
const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage, matcher);
|
|
462
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage, matcher);
|
|
467
463
|
return {
|
|
468
464
|
...slot,
|
|
469
465
|
pagePath: subPage,
|
|
470
466
|
configLayoutPaths,
|
|
471
467
|
configLayoutTreePositions: findSlotConfigLayoutTreePositions(slot.ownerDir, configLayoutPaths),
|
|
468
|
+
notFoundPath: notFoundBoundary.path,
|
|
469
|
+
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
472
470
|
routeSegments: rawSegments
|
|
473
471
|
};
|
|
474
472
|
}
|
|
@@ -550,11 +548,14 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
550
548
|
const subSlots = parentRoute.parallelSlots.map((slot) => {
|
|
551
549
|
const subPage = slotPages.get(slot.key);
|
|
552
550
|
const configLayoutPaths = findSlotConfigLayoutPaths(slot.ownerDir, subPage ?? null, matcher);
|
|
551
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slot.ownerDir, subPage ?? null, matcher);
|
|
553
552
|
return {
|
|
554
553
|
...slot,
|
|
555
554
|
pagePath: subPage || null,
|
|
556
555
|
configLayoutPaths,
|
|
557
556
|
configLayoutTreePositions: findSlotConfigLayoutTreePositions(slot.ownerDir, configLayoutPaths),
|
|
557
|
+
notFoundPath: notFoundBoundary.path,
|
|
558
|
+
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
558
559
|
routeSegments: subPage ? rawSegments : null
|
|
559
560
|
};
|
|
560
561
|
});
|
|
@@ -583,10 +584,13 @@ function discoverSlotSubRoutes(routes, matcher, ghostParents = []) {
|
|
|
583
584
|
errorPath: parentRoute.errorPath,
|
|
584
585
|
layoutErrorPaths: parentRoute.layoutErrorPaths,
|
|
585
586
|
notFoundPath: parentRoute.notFoundPath,
|
|
587
|
+
notFoundTreePosition: parentRoute.notFoundTreePosition,
|
|
586
588
|
notFoundPaths: parentRoute.notFoundPaths,
|
|
587
589
|
forbiddenPaths: parentRoute.forbiddenPaths,
|
|
588
590
|
forbiddenPath: parentRoute.forbiddenPath,
|
|
591
|
+
forbiddenTreePosition: parentRoute.forbiddenTreePosition,
|
|
589
592
|
unauthorizedPath: parentRoute.unauthorizedPath,
|
|
593
|
+
unauthorizedTreePosition: parentRoute.unauthorizedTreePosition,
|
|
590
594
|
unauthorizedPaths: parentRoute.unauthorizedPaths,
|
|
591
595
|
routeSegments: [...parentRoute.routeSegments, ...rawSegments],
|
|
592
596
|
childrenRouteSegments: childrenDefault ? parentRoute.routeSegments : void 0,
|
|
@@ -674,6 +678,25 @@ function findSlotConfigLayoutTreePositions(slotDir, layoutPaths) {
|
|
|
674
678
|
return relativeDir ? relativeDir.split(path.sep).filter(Boolean).length : 0;
|
|
675
679
|
});
|
|
676
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
|
+
}
|
|
677
700
|
/**
|
|
678
701
|
* Find a sibling catch-all page directly under `dir`, i.e. a `[...slug]` or
|
|
679
702
|
* `[[...slug]]` directory that contains a `page` file. Returns the absolute
|
|
@@ -704,27 +727,15 @@ function findCatchAllPage(dir, matcher) {
|
|
|
704
727
|
}
|
|
705
728
|
/**
|
|
706
729
|
* Convert a file path relative to app/ into an AppRoute.
|
|
707
|
-
*
|
|
708
|
-
* `file` and `appDir` must be forward-slash. `file` comes from
|
|
709
|
-
* `scanWithExtensions` (already forward-slash) and is joined onto `appDir` with
|
|
710
|
-
* `path.posix.join` to form the page/route path, so a native input would
|
|
711
|
-
* produce a mixed separator on Windows.
|
|
712
730
|
*/
|
|
713
731
|
function fileToAppRoute(file, appDir, type, matcher) {
|
|
714
|
-
let dir = path.
|
|
715
|
-
if (type === "page" && dir !== "." && path.
|
|
716
|
-
const parent = path.
|
|
732
|
+
let dir = path.dirname(file);
|
|
733
|
+
if (type === "page" && dir !== "." && path.basename(dir) === "@children") {
|
|
734
|
+
const parent = path.dirname(dir);
|
|
717
735
|
dir = parent === "" || parent === "." ? "." : parent;
|
|
718
736
|
}
|
|
719
|
-
return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.
|
|
737
|
+
return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.join(appDir, file) : null, type === "route" ? path.join(appDir, file) : null);
|
|
720
738
|
}
|
|
721
|
-
/**
|
|
722
|
-
* `dir`, `appDir`, `pagePath`, and `routePath` must all be forward-slash. `dir`
|
|
723
|
-
* is split on `path.posix.sep` and joined onto `appDir` with `path.posix.join`.
|
|
724
|
-
* `appDir` is threaded to the layout/slot/boundary discovery below, which builds
|
|
725
|
-
* paths the same way. `pagePath` and `routePath` are stored on the route node as
|
|
726
|
-
* canonical ids that get compared and re-joined downstream.
|
|
727
|
-
*/
|
|
728
739
|
function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeNestedOnlySlots = false) {
|
|
729
740
|
const segments = dir === "." ? [] : dir.split("/");
|
|
730
741
|
const params = [];
|
|
@@ -743,13 +754,16 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
743
754
|
const errorEntries = discoverSegmentErrors(segments, appDir, matcher);
|
|
744
755
|
const errorPaths = errorEntries.map((entry) => entry.path);
|
|
745
756
|
const errorTreePositions = errorEntries.map((entry) => entry.treePosition);
|
|
746
|
-
const routeDir = dir === "." ? appDir : path.
|
|
757
|
+
const routeDir = dir === "." ? appDir : path.join(appDir, dir);
|
|
747
758
|
const effectivePagePath = pagePath ?? (routePath ? null : findFile(routeDir, "default", matcher));
|
|
748
759
|
const loadingPath = findFile(routeDir, "loading", matcher);
|
|
749
760
|
const errorPath = findFile(routeDir, "error", matcher);
|
|
750
|
-
const
|
|
751
|
-
const
|
|
752
|
-
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;
|
|
753
767
|
const notFoundPaths = discoverBoundaryFilePerLayout(layouts, "not-found", matcher);
|
|
754
768
|
const forbiddenPaths = discoverBoundaryFilePerLayout(layouts, "forbidden", matcher);
|
|
755
769
|
const unauthorizedPaths = discoverBoundaryFilePerLayout(layouts, "unauthorized", matcher);
|
|
@@ -776,10 +790,13 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
776
790
|
errorPaths,
|
|
777
791
|
errorTreePositions,
|
|
778
792
|
notFoundPath,
|
|
793
|
+
notFoundTreePosition: notFoundEntry?.treePosition ?? null,
|
|
779
794
|
notFoundPaths,
|
|
780
795
|
forbiddenPaths,
|
|
781
796
|
forbiddenPath,
|
|
797
|
+
forbiddenTreePosition: forbiddenEntry?.treePosition ?? null,
|
|
782
798
|
unauthorizedPath,
|
|
799
|
+
unauthorizedTreePosition: unauthorizedEntry?.treePosition ?? null,
|
|
783
800
|
unauthorizedPaths,
|
|
784
801
|
routeSegments: segments,
|
|
785
802
|
templateTreePositions,
|
|
@@ -850,8 +867,9 @@ function convertTreePathToRouteParts(treePath) {
|
|
|
850
867
|
function computeLayoutTreePositions(appDir, layouts) {
|
|
851
868
|
return layouts.map((layoutPath) => {
|
|
852
869
|
const layoutDir = path.dirname(layoutPath);
|
|
853
|
-
|
|
854
|
-
|
|
870
|
+
const relative = path.relative(appDir, layoutDir);
|
|
871
|
+
if (relative === "") return 0;
|
|
872
|
+
return relative.split(path.sep).length;
|
|
855
873
|
});
|
|
856
874
|
}
|
|
857
875
|
/**
|
|
@@ -937,7 +955,7 @@ function discoverLayoutAlignedErrors(segments, appDir, matcher) {
|
|
|
937
955
|
* by walking from the route's directory up to the app root.
|
|
938
956
|
* Returns the first (closest) file found, or null.
|
|
939
957
|
*/
|
|
940
|
-
function
|
|
958
|
+
function discoverBoundaryFileEntry(segments, appDir, fileName, matcher) {
|
|
941
959
|
const dirs = [];
|
|
942
960
|
let dir = appDir;
|
|
943
961
|
dirs.push(dir);
|
|
@@ -947,7 +965,10 @@ function discoverBoundaryFile(segments, appDir, fileName, matcher) {
|
|
|
947
965
|
}
|
|
948
966
|
for (let i = dirs.length - 1; i >= 0; i--) {
|
|
949
967
|
const f = findFile(dirs[i], fileName, matcher);
|
|
950
|
-
if (f) return
|
|
968
|
+
if (f) return {
|
|
969
|
+
path: f,
|
|
970
|
+
treePosition: i
|
|
971
|
+
};
|
|
951
972
|
}
|
|
952
973
|
return null;
|
|
953
974
|
}
|
|
@@ -976,10 +997,6 @@ function discoverBoundaryFilePerLayout(layouts, fileName, matcher) {
|
|
|
976
997
|
* Walk from appDir through each segment to the route's directory. At each level
|
|
977
998
|
* that has @slot dirs, collect them. Slots at the route's own directory level
|
|
978
999
|
* use page.tsx; slots at ancestor levels use default.tsx only.
|
|
979
|
-
*
|
|
980
|
-
* `appDir` and `routeDir` must be forward-slash — `currentDir` descends from
|
|
981
|
-
* `appDir` via `path.posix.join`, and the `dir === routeDir` active-level test
|
|
982
|
-
* below only matches when both share the canonical separator.
|
|
983
1000
|
*/
|
|
984
1001
|
function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, includeNestedOnlySlots = false) {
|
|
985
1002
|
const slotMap = /* @__PURE__ */ new Map();
|
|
@@ -992,7 +1009,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
|
|
|
992
1009
|
segmentIndex: 0
|
|
993
1010
|
});
|
|
994
1011
|
for (let i = 0; i < segments.length; i++) {
|
|
995
|
-
currentDir = path.
|
|
1012
|
+
currentDir = path.join(currentDir, segments[i]);
|
|
996
1013
|
if (findFile(currentDir, "layout", matcher)) layoutIdx++;
|
|
997
1014
|
dirsToCheck.push({
|
|
998
1015
|
dir: currentDir,
|
|
@@ -1155,9 +1172,6 @@ function patternsStructurallyEquivalent(a, b) {
|
|
|
1155
1172
|
*
|
|
1156
1173
|
* Returns the absolute page path, or null if no root-level page is found.
|
|
1157
1174
|
*
|
|
1158
|
-
* `slotDir` must be forward-slash: the `path.posix.join` descent stays a
|
|
1159
|
-
* canonical id only when the base already is.
|
|
1160
|
-
*
|
|
1161
1175
|
* Only descends into route-group directories (those whose name starts with `(`
|
|
1162
1176
|
* and ends with `)`). Dynamic segments, regular named dirs, and `@slot` dirs
|
|
1163
1177
|
* are not transparent and are therefore not searched.
|
|
@@ -1169,7 +1183,7 @@ function findSlotRootPage(slotDir, matcher) {
|
|
|
1169
1183
|
for (const entry of entries) {
|
|
1170
1184
|
if (!entry.isDirectory()) continue;
|
|
1171
1185
|
if (!entry.name.startsWith("(") || !entry.name.endsWith(")")) continue;
|
|
1172
|
-
const found = findSlotRootPage(path.
|
|
1186
|
+
const found = findSlotRootPage(path.join(slotDir, entry.name), matcher);
|
|
1173
1187
|
if (found) return found;
|
|
1174
1188
|
}
|
|
1175
1189
|
return null;
|
|
@@ -1178,9 +1192,6 @@ function findSlotRootPage(slotDir, matcher) {
|
|
|
1178
1192
|
* Discover parallel route slots (@team, @analytics, etc.) in a directory.
|
|
1179
1193
|
* Returns a ParallelSlot for each @-prefixed subdirectory that has a page,
|
|
1180
1194
|
* default component, intercepting route, or nested page-backed sub-route.
|
|
1181
|
-
*
|
|
1182
|
-
* `dir` and `appDir` must be forward-slash. The slot directory is built from
|
|
1183
|
-
* `dir` with `path.posix.join`.
|
|
1184
1195
|
*/
|
|
1185
1196
|
function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = false) {
|
|
1186
1197
|
const entries = readDirEntriesCached(dir, matcher);
|
|
@@ -1189,7 +1200,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1189
1200
|
if (!entry.isDirectory() || !entry.name.startsWith("@")) continue;
|
|
1190
1201
|
if (entry.name === "@children") continue;
|
|
1191
1202
|
const slotName = entry.name.slice(1);
|
|
1192
|
-
const slotDir = path.
|
|
1203
|
+
const slotDir = path.join(dir, entry.name);
|
|
1193
1204
|
const pagePath = findSlotRootPage(slotDir, matcher);
|
|
1194
1205
|
const defaultPath = findFile(slotDir, "default", matcher);
|
|
1195
1206
|
const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
|
|
@@ -1198,9 +1209,10 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1198
1209
|
const ownerSegments = path.relative(appDir, dir).split(path.sep).filter((segment) => segment.length > 0);
|
|
1199
1210
|
const ownerTreePath = createAppRouteGraphTreePath(ownerSegments, ownerSegments.length);
|
|
1200
1211
|
const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
|
|
1212
|
+
const notFoundBoundary = findSlotNotFoundBoundary(slotDir, pagePath, matcher);
|
|
1201
1213
|
slots.push({
|
|
1202
1214
|
id: createAppRouteGraphSlotId(slotName, ownerTreePath),
|
|
1203
|
-
key: `${slotName}@${
|
|
1215
|
+
key: `${slotName}@${path.relative(appDir, slotDir)}`,
|
|
1204
1216
|
name: slotName,
|
|
1205
1217
|
ownerDir: slotDir,
|
|
1206
1218
|
ownerTreePath,
|
|
@@ -1212,6 +1224,8 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1212
1224
|
configLayoutTreePositions: findSlotConfigLayoutTreePositions(slotDir, configLayoutPaths),
|
|
1213
1225
|
loadingPath: findFile(slotDir, "loading", matcher),
|
|
1214
1226
|
errorPath: findFile(slotDir, "error", matcher),
|
|
1227
|
+
notFoundPath: notFoundBoundary.path,
|
|
1228
|
+
notFoundTreePosition: notFoundBoundary.treePosition,
|
|
1215
1229
|
interceptingRoutes,
|
|
1216
1230
|
layoutIndex: -1,
|
|
1217
1231
|
routeSegments: pagePath ? [] : null
|
|
@@ -1259,9 +1273,6 @@ function isInterceptionMarkerDir(name) {
|
|
|
1259
1273
|
* Intercepting routes use conventions like (.)photo, (..)feed, (...), etc.
|
|
1260
1274
|
* They intercept navigation to another route and render within the slot instead.
|
|
1261
1275
|
*
|
|
1262
|
-
* `slotDir`, `routeDir`, and `appDir` must be forward-slash. They are passed
|
|
1263
|
-
* down to `path.posix.join` when building the intercept page paths.
|
|
1264
|
-
*
|
|
1265
1276
|
* @param slotDir - The parallel slot directory (e.g. app/feed/@modal)
|
|
1266
1277
|
* @param routeDir - The directory of the route that owns this slot (e.g. app/feed)
|
|
1267
1278
|
* @param appDir - The root app directory
|
|
@@ -1269,7 +1280,7 @@ function isInterceptionMarkerDir(name) {
|
|
|
1269
1280
|
function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
|
|
1270
1281
|
if (!fs.existsSync(slotDir)) return [];
|
|
1271
1282
|
const results = [];
|
|
1272
|
-
scanForInterceptingPages(slotDir, routeDir, appDir, results, matcher);
|
|
1283
|
+
scanForInterceptingPages(slotDir, slotDir, routeDir, appDir, results, matcher);
|
|
1273
1284
|
return results;
|
|
1274
1285
|
}
|
|
1275
1286
|
/**
|
|
@@ -1280,17 +1291,13 @@ function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
|
|
|
1280
1291
|
* Sibling intercepts use the same conventions and target-computation logic as
|
|
1281
1292
|
* slot intercepts, but their intercepting page replaces the full page response
|
|
1282
1293
|
* (not a slot) during soft navigation.
|
|
1283
|
-
*
|
|
1284
|
-
* `appDir` and each route's `pagePath` / `routePath` must be forward-slash. The
|
|
1285
|
-
* owner directories are derived from `pagePath` / `routePath` and matched
|
|
1286
|
-
* against `appDir`-relative paths built with `path.posix.*`.
|
|
1287
1294
|
*/
|
|
1288
1295
|
function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
1289
1296
|
const routesByDir = /* @__PURE__ */ new Map();
|
|
1290
1297
|
for (const route of routes) {
|
|
1291
1298
|
const filePath = route.pagePath ?? route.routePath;
|
|
1292
1299
|
if (!filePath) continue;
|
|
1293
|
-
const routeDir = path.
|
|
1300
|
+
const routeDir = path.dirname(filePath);
|
|
1294
1301
|
if (!routesByDir.has(routeDir)) routesByDir.set(routeDir, route);
|
|
1295
1302
|
}
|
|
1296
1303
|
function walk(dir) {
|
|
@@ -1304,13 +1311,13 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1304
1311
|
if (!entry.isDirectory()) continue;
|
|
1305
1312
|
if (entry.name.startsWith("_")) continue;
|
|
1306
1313
|
if (entry.name.startsWith("@")) continue;
|
|
1307
|
-
const childDir = path.
|
|
1314
|
+
const childDir = path.join(dir, entry.name);
|
|
1308
1315
|
const marker = matchInterceptConvention(entry.name);
|
|
1309
1316
|
if (marker) {
|
|
1310
1317
|
const restOfName = entry.name.slice(marker.prefix.length);
|
|
1311
1318
|
const parentDir = dir;
|
|
1312
1319
|
const results = [];
|
|
1313
|
-
collectInterceptingPages(childDir, childDir, marker.convention, restOfName, parentDir, appDir, parentDir, results, matcher);
|
|
1320
|
+
collectInterceptingPages(childDir, childDir, marker.convention, restOfName, parentDir, appDir, parentDir, null, results, matcher);
|
|
1314
1321
|
for (const ir of results) {
|
|
1315
1322
|
ir.slotId = createAppRouteGraphSiblingInterceptSlotId(ir.sourceMatchPattern);
|
|
1316
1323
|
const owner = findOwnerRouteForDir(parentDir, appDir, routes, routesByDir);
|
|
@@ -1335,18 +1342,15 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1335
1342
|
* no sibling pages at all (e.g. `deep/path/(...)target` with no
|
|
1336
1343
|
* `deep/path/page.tsx`).
|
|
1337
1344
|
*
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
* backslashes on Windows. Without normalizing, the `current === appDir`
|
|
1342
|
-
* termination never fires there and the walk overshoots the app root.
|
|
1343
|
-
* `routesByDir` keys must be forward-slash dirnames of the route file paths.
|
|
1345
|
+
* Comparisons happen in forward-slash space — inputs go through `toSlash` so
|
|
1346
|
+
* callers still holding native-separator paths hit the `current === appDir`
|
|
1347
|
+
* termination instead of overshooting the app root.
|
|
1344
1348
|
*
|
|
1345
1349
|
* Exported for tests.
|
|
1346
1350
|
*/
|
|
1347
1351
|
function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
1348
|
-
const appRoot =
|
|
1349
|
-
let current =
|
|
1352
|
+
const appRoot = toSlash(appDir);
|
|
1353
|
+
let current = toSlash(dir);
|
|
1350
1354
|
while (true) {
|
|
1351
1355
|
const exact = routesByDir.get(current);
|
|
1352
1356
|
if (exact) return exact;
|
|
@@ -1355,7 +1359,7 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1355
1359
|
for (const route of routes) {
|
|
1356
1360
|
const filePath = route.pagePath ?? route.routePath;
|
|
1357
1361
|
if (!filePath) continue;
|
|
1358
|
-
if (!
|
|
1362
|
+
if (!toSlash(filePath).startsWith(currentWithSep)) continue;
|
|
1359
1363
|
if (!best || route.patternParts.length < best.patternParts.length) best = route;
|
|
1360
1364
|
}
|
|
1361
1365
|
if (best) return best;
|
|
@@ -1369,22 +1373,19 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1369
1373
|
/**
|
|
1370
1374
|
* Recursively scan a directory tree for page.tsx files that are inside
|
|
1371
1375
|
* intercepting route directories.
|
|
1372
|
-
*
|
|
1373
|
-
* `currentDir`, `routeDir`, and `appDir` must be forward-slash. `currentDir`
|
|
1374
|
-
* descends with `path.posix.join` when building the intercept page paths.
|
|
1375
1376
|
*/
|
|
1376
|
-
function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
|
|
1377
|
+
function scanForInterceptingPages(currentDir, slotRootDir, routeDir, appDir, results, matcher) {
|
|
1377
1378
|
if (!fs.existsSync(currentDir)) return;
|
|
1378
1379
|
const entries = fs.readdirSync(currentDir, { withFileTypes: true });
|
|
1379
1380
|
for (const entry of entries) {
|
|
1380
1381
|
if (!entry.isDirectory()) continue;
|
|
1381
1382
|
if (entry.name.startsWith("_")) continue;
|
|
1382
1383
|
const interceptMatch = matchInterceptConvention(entry.name);
|
|
1383
|
-
const interceptDir = path.
|
|
1384
|
+
const interceptDir = path.join(currentDir, entry.name);
|
|
1384
1385
|
if (interceptMatch) {
|
|
1385
1386
|
const restOfName = entry.name.slice(interceptMatch.prefix.length);
|
|
1386
|
-
collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, results, matcher);
|
|
1387
|
-
} 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);
|
|
1388
1389
|
}
|
|
1389
1390
|
}
|
|
1390
1391
|
/**
|
|
@@ -1397,14 +1398,8 @@ function matchInterceptConvention(name) {
|
|
|
1397
1398
|
/**
|
|
1398
1399
|
* Collect page.tsx files inside an intercepting route directory tree
|
|
1399
1400
|
* and compute their target URL patterns.
|
|
1400
|
-
*
|
|
1401
|
-
* `currentDir`, `interceptRoot`, `routeDir`, `appDir`, and `interceptParentDir`
|
|
1402
|
-
* must all be forward-slash. `currentDir` descends with `path.posix.join` and
|
|
1403
|
-
* its `findFile` results become the stored layout/page paths. The others are
|
|
1404
|
-
* relativized against `appDir` (and each other) to derive the intercept page
|
|
1405
|
-
* segments and URL patterns.
|
|
1406
1401
|
*/
|
|
1407
|
-
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 = []) {
|
|
1408
1403
|
const currentLayoutPath = findFile(currentDir, "layout", matcher);
|
|
1409
1404
|
const layoutPaths = currentLayoutPath ? [...parentLayoutPaths, currentLayoutPath] : parentLayoutPaths;
|
|
1410
1405
|
const page = findFile(currentDir, "page", matcher);
|
|
@@ -1412,17 +1407,23 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1412
1407
|
const targetPattern = computeInterceptTarget(convention, interceptSegment, currentDir, interceptRoot, routeDir, appDir);
|
|
1413
1408
|
if (targetPattern) {
|
|
1414
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) : [];
|
|
1415
1413
|
results.push({
|
|
1416
|
-
branchSegments
|
|
1414
|
+
branchSegments,
|
|
1417
1415
|
convention,
|
|
1418
1416
|
layoutPaths: [...layoutPaths],
|
|
1419
1417
|
layoutSegments: layoutPaths.map((layoutPath) => {
|
|
1420
1418
|
return [interceptSegment, ...path.relative(interceptRoot, path.dirname(layoutPath)).split(path.sep).filter(Boolean)];
|
|
1421
1419
|
}),
|
|
1420
|
+
notFoundBranchSegments: [...slotParentSegments, ...branchSegments],
|
|
1421
|
+
notFoundPath: notFoundBoundary.path,
|
|
1422
|
+
notFoundTreePosition: notFoundBoundary.treePosition === null ? null : slotRootDir ? notFoundBoundary.treePosition : notFoundBoundary.treePosition + 1,
|
|
1422
1423
|
targetPattern: targetPattern.pattern,
|
|
1423
1424
|
sourceMatchPattern,
|
|
1424
1425
|
pagePath: page,
|
|
1425
|
-
sourcePageSegments:
|
|
1426
|
+
sourcePageSegments: path.relative(appDir, path.dirname(page)).split("/").filter(Boolean),
|
|
1426
1427
|
params: targetPattern.params
|
|
1427
1428
|
});
|
|
1428
1429
|
}
|
|
@@ -1432,7 +1433,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1432
1433
|
for (const entry of entries) {
|
|
1433
1434
|
if (!entry.isDirectory()) continue;
|
|
1434
1435
|
if (entry.name.startsWith("_")) continue;
|
|
1435
|
-
collectInterceptingPages(path.
|
|
1436
|
+
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, slotRootDir, results, matcher, layoutPaths);
|
|
1436
1437
|
}
|
|
1437
1438
|
}
|
|
1438
1439
|
/**
|
|
@@ -1552,9 +1553,6 @@ const findFileProbeCache = /* @__PURE__ */ new WeakMap();
|
|
|
1552
1553
|
* registered per-scan cache; otherwise falls back to a direct probe (identical
|
|
1553
1554
|
* result). The `null` "not found" outcome is cached too, so repeated misses on
|
|
1554
1555
|
* shared ancestors cost a single set of `existsSync` calls per scan.
|
|
1555
|
-
*
|
|
1556
|
-
* `dir` must be forward-slash. The returned path comes from `findFileWithExts`,
|
|
1557
|
-
* so it is forward-slash too.
|
|
1558
1556
|
*/
|
|
1559
1557
|
function findFile(dir, name, matcher) {
|
|
1560
1558
|
const cache = findFileProbeCache.get(matcher);
|
|
@@ -12,16 +12,11 @@ declare function invalidateAppRouteCache(): void;
|
|
|
12
12
|
* TODO(#726): Layer 4 should consume this read model directly once the
|
|
13
13
|
* navigation planner owns route graph facts.
|
|
14
14
|
*
|
|
15
|
-
* `appDir` must be forward-slash — callers normalize it at their entry, and it
|
|
16
|
-
* flows into `buildAppRouteGraph`, which builds every path with `path.posix.*`.
|
|
17
|
-
*
|
|
18
15
|
* @internal
|
|
19
16
|
*/
|
|
20
17
|
declare function appRouteGraph(appDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<AppRouteGraph>;
|
|
21
18
|
/**
|
|
22
19
|
* Scan the app/ directory and return a list of routes.
|
|
23
|
-
*
|
|
24
|
-
* `appDir` must be forward-slash — it is forwarded to `appRouteGraph`.
|
|
25
20
|
*/
|
|
26
21
|
declare function appRouter(appDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<AppRouteGraphRoute[]>;
|
|
27
22
|
/**
|