vinext 0.0.53 → 0.0.55
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 +1 -0
- package/dist/build/inline-css.d.ts +7 -0
- package/dist/build/inline-css.js +50 -0
- package/dist/build/inline-css.js.map +1 -0
- package/dist/build/prerender.js +2 -1
- package/dist/build/prerender.js.map +1 -1
- package/dist/check.js +19 -3
- package/dist/check.js.map +1 -1
- package/dist/client/navigation-runtime.d.ts +3 -1
- package/dist/client/navigation-runtime.js +1 -1
- package/dist/client/navigation-runtime.js.map +1 -1
- package/dist/client/window-next.d.ts +7 -0
- package/dist/client/window-next.js.map +1 -1
- package/dist/config/next-config.d.ts +97 -2
- package/dist/config/next-config.js +155 -6
- package/dist/config/next-config.js.map +1 -1
- package/dist/config/tsconfig-paths.d.ts +12 -3
- package/dist/config/tsconfig-paths.js +55 -24
- package/dist/config/tsconfig-paths.js.map +1 -1
- package/dist/deploy.js +13 -0
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-browser-entry.d.ts +11 -1
- package/dist/entries/app-browser-entry.js +16 -6
- package/dist/entries/app-browser-entry.js.map +1 -1
- package/dist/entries/app-rsc-entry.d.ts +9 -1
- package/dist/entries/app-rsc-entry.js +30 -5
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/app-rsc-manifest.d.ts +21 -1
- package/dist/entries/app-rsc-manifest.js +28 -9
- package/dist/entries/app-rsc-manifest.js.map +1 -1
- package/dist/entries/pages-client-entry.d.ts +4 -1
- package/dist/entries/pages-client-entry.js +18 -2
- package/dist/entries/pages-client-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.js +123 -8
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/entries/runtime-entry-module.d.ts +1 -10
- package/dist/entries/runtime-entry-module.js +2 -12
- package/dist/entries/runtime-entry-module.js.map +1 -1
- package/dist/index.js +144 -44
- package/dist/index.js.map +1 -1
- package/dist/plugins/import-meta-url.d.ts +16 -0
- package/dist/plugins/import-meta-url.js +193 -0
- package/dist/plugins/import-meta-url.js.map +1 -0
- package/dist/plugins/remove-console.d.ts +16 -0
- package/dist/plugins/remove-console.js +176 -0
- package/dist/plugins/remove-console.js.map +1 -0
- package/dist/routing/app-route-graph.d.ts +24 -1
- package/dist/routing/app-route-graph.js +52 -4
- package/dist/routing/app-route-graph.js.map +1 -1
- package/dist/routing/app-router.d.ts +2 -2
- package/dist/routing/app-router.js +2 -2
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/file-matcher.d.ts +21 -1
- package/dist/routing/file-matcher.js +39 -1
- package/dist/routing/file-matcher.js.map +1 -1
- package/dist/routing/pages-router.d.ts +1 -1
- package/dist/routing/pages-router.js +10 -3
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/server/api-handler.js +1 -1
- package/dist/server/app-browser-action-result.d.ts +9 -16
- package/dist/server/app-browser-action-result.js +25 -14
- package/dist/server/app-browser-action-result.js.map +1 -1
- package/dist/server/app-browser-entry.js +195 -60
- package/dist/server/app-browser-entry.js.map +1 -1
- package/dist/server/app-browser-mpa-navigation.d.ts +16 -0
- package/dist/server/app-browser-mpa-navigation.js +36 -0
- package/dist/server/app-browser-mpa-navigation.js.map +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +2 -0
- package/dist/server/app-browser-navigation-controller.js +4 -0
- package/dist/server/app-browser-navigation-controller.js.map +1 -1
- package/dist/server/app-browser-popstate.d.ts +3 -1
- package/dist/server/app-browser-popstate.js +15 -1
- package/dist/server/app-browser-popstate.js.map +1 -1
- package/dist/server/app-browser-state.js +2 -1
- package/dist/server/app-browser-state.js.map +1 -1
- package/dist/server/app-elements-wire.d.ts +13 -4
- package/dist/server/app-elements-wire.js +10 -1
- package/dist/server/app-elements-wire.js.map +1 -1
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-elements.js.map +1 -1
- package/dist/server/app-fallback-renderer.d.ts +15 -5
- package/dist/server/app-fallback-renderer.js +10 -4
- package/dist/server/app-fallback-renderer.js.map +1 -1
- package/dist/server/app-inline-css-client.d.ts +7 -0
- package/dist/server/app-inline-css-client.js +37 -0
- package/dist/server/app-inline-css-client.js.map +1 -0
- package/dist/server/app-layout-param-observation.d.ts +30 -0
- package/dist/server/app-layout-param-observation.js +130 -0
- package/dist/server/app-layout-param-observation.js.map +1 -0
- package/dist/server/app-page-boundary-render.js +2 -2
- package/dist/server/app-page-boundary-render.js.map +1 -1
- package/dist/server/app-page-boundary.d.ts +21 -1
- package/dist/server/app-page-boundary.js +28 -3
- package/dist/server/app-page-boundary.js.map +1 -1
- package/dist/server/app-page-cache.d.ts +7 -3
- package/dist/server/app-page-cache.js +7 -7
- package/dist/server/app-page-cache.js.map +1 -1
- package/dist/server/app-page-dispatch.d.ts +10 -1
- package/dist/server/app-page-dispatch.js +126 -79
- package/dist/server/app-page-dispatch.js.map +1 -1
- package/dist/server/app-page-element-builder.js +12 -28
- package/dist/server/app-page-element-builder.js.map +1 -1
- package/dist/server/app-page-params.d.ts +2 -1
- package/dist/server/app-page-params.js +14 -1
- package/dist/server/app-page-params.js.map +1 -1
- package/dist/server/app-page-probe.d.ts +12 -1
- package/dist/server/app-page-probe.js +116 -1
- package/dist/server/app-page-probe.js.map +1 -1
- package/dist/server/app-page-render-identity.d.ts +22 -0
- package/dist/server/app-page-render-identity.js +42 -0
- package/dist/server/app-page-render-identity.js.map +1 -0
- package/dist/server/app-page-render.d.ts +8 -1
- package/dist/server/app-page-render.js +4 -1
- package/dist/server/app-page-render.js.map +1 -1
- package/dist/server/app-page-request.d.ts +6 -3
- package/dist/server/app-page-request.js +5 -2
- package/dist/server/app-page-request.js.map +1 -1
- package/dist/server/app-page-response.js +2 -2
- package/dist/server/app-page-response.js.map +1 -1
- package/dist/server/app-page-route-wiring.d.ts +15 -0
- package/dist/server/app-page-route-wiring.js +7 -5
- package/dist/server/app-page-route-wiring.js.map +1 -1
- package/dist/server/app-page-stream.d.ts +11 -0
- package/dist/server/app-page-stream.js +1 -0
- package/dist/server/app-page-stream.js.map +1 -1
- package/dist/server/app-route-handler-response.js +37 -5
- package/dist/server/app-route-handler-response.js.map +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +3 -2
- package/dist/server/app-rsc-cache-busting.js +9 -7
- package/dist/server/app-rsc-cache-busting.js.map +1 -1
- package/dist/server/app-rsc-handler.d.ts +14 -3
- package/dist/server/app-rsc-handler.js +56 -6
- package/dist/server/app-rsc-handler.js.map +1 -1
- package/dist/server/app-rsc-request-normalization.d.ts +2 -1
- package/dist/server/app-rsc-request-normalization.js +3 -2
- package/dist/server/app-rsc-request-normalization.js.map +1 -1
- package/dist/server/app-segment-config.d.ts +1 -1
- package/dist/server/app-segment-config.js +4 -1
- package/dist/server/app-segment-config.js.map +1 -1
- package/dist/server/app-server-action-execution.d.ts +26 -3
- package/dist/server/app-server-action-execution.js +240 -29
- package/dist/server/app-server-action-execution.js.map +1 -1
- package/dist/server/app-ssr-entry.d.ts +6 -0
- package/dist/server/app-ssr-entry.js +22 -7
- package/dist/server/app-ssr-entry.js.map +1 -1
- package/dist/server/app-ssr-error-meta.js +3 -3
- package/dist/server/app-ssr-error-meta.js.map +1 -1
- package/dist/server/app-ssr-stream.d.ts +2 -1
- package/dist/server/app-ssr-stream.js +176 -31
- package/dist/server/app-ssr-stream.js.map +1 -1
- package/dist/server/artifact-compatibility.d.ts +2 -1
- package/dist/server/artifact-compatibility.js +10 -1
- package/dist/server/artifact-compatibility.js.map +1 -1
- package/dist/server/client-reuse-manifest.d.ts +9 -4
- package/dist/server/client-reuse-manifest.js +2 -1
- package/dist/server/client-reuse-manifest.js.map +1 -1
- package/dist/server/client-trace-metadata.d.ts +31 -0
- package/dist/server/client-trace-metadata.js +83 -0
- package/dist/server/client-trace-metadata.js.map +1 -0
- package/dist/server/cookie-utils.d.ts +13 -0
- package/dist/server/cookie-utils.js +20 -0
- package/dist/server/cookie-utils.js.map +1 -0
- package/dist/server/dev-server.d.ts +8 -1
- package/dist/server/dev-server.js +83 -12
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/document-initial-head.d.ts +7 -0
- package/dist/server/document-initial-head.js +35 -0
- package/dist/server/document-initial-head.js.map +1 -0
- package/dist/server/html.d.ts +2 -1
- package/dist/server/html.js +6 -1
- package/dist/server/html.js.map +1 -1
- package/dist/server/isr-cache.d.ts +7 -5
- package/dist/server/isr-cache.js +17 -6
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/middleware-runtime.js +1 -2
- package/dist/server/middleware-runtime.js.map +1 -1
- package/dist/server/pages-document-initial-props.d.ts +89 -0
- package/dist/server/pages-document-initial-props.js +140 -0
- package/dist/server/pages-document-initial-props.js.map +1 -0
- package/dist/server/pages-node-compat.js +1 -1
- package/dist/server/pages-page-data.js +3 -0
- package/dist/server/pages-page-data.js.map +1 -1
- package/dist/server/pages-page-method.d.ts +48 -0
- package/dist/server/pages-page-method.js +19 -0
- package/dist/server/pages-page-method.js.map +1 -0
- package/dist/server/pages-page-response.d.ts +20 -0
- package/dist/server/pages-page-response.js +37 -7
- package/dist/server/pages-page-response.js.map +1 -1
- package/dist/server/pages-serializable-props.d.ts +25 -0
- package/dist/server/pages-serializable-props.js +69 -0
- package/dist/server/pages-serializable-props.js.map +1 -0
- package/dist/server/prod-server.js +16 -6
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/server-action-not-found.js +3 -2
- package/dist/server/server-action-not-found.js.map +1 -1
- package/dist/server/skip-cache-proof.d.ts +23 -2
- package/dist/server/skip-cache-proof.js +81 -12
- package/dist/server/skip-cache-proof.js.map +1 -1
- package/dist/server/static-file-cache.js +2 -1
- package/dist/server/static-file-cache.js.map +1 -1
- package/dist/server/static-layout-client-reuse-proof.d.ts +16 -0
- package/dist/server/static-layout-client-reuse-proof.js +35 -0
- package/dist/server/static-layout-client-reuse-proof.js.map +1 -0
- package/dist/shims/app-router-scroll-state.d.ts +4 -2
- package/dist/shims/app-router-scroll-state.js +16 -3
- package/dist/shims/app-router-scroll-state.js.map +1 -1
- package/dist/shims/app-router-scroll.d.ts +16 -2
- package/dist/shims/app-router-scroll.js +18 -3
- package/dist/shims/app-router-scroll.js.map +1 -1
- package/dist/shims/cache.d.ts +27 -1
- package/dist/shims/cache.js +108 -6
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/document.d.ts +6 -0
- package/dist/shims/document.js +7 -8
- package/dist/shims/document.js.map +1 -1
- package/dist/shims/error-boundary.d.ts +4 -4
- package/dist/shims/error-boundary.js +27 -28
- package/dist/shims/error-boundary.js.map +1 -1
- package/dist/shims/error.js +3 -0
- package/dist/shims/error.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +16 -5
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/hash-scroll.d.ts +4 -1
- package/dist/shims/hash-scroll.js +13 -1
- package/dist/shims/hash-scroll.js.map +1 -1
- package/dist/shims/head-state.d.ts +1 -0
- package/dist/shims/head-state.js +18 -3
- package/dist/shims/head-state.js.map +1 -1
- package/dist/shims/head.d.ts +35 -1
- package/dist/shims/head.js +113 -14
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/headers.d.ts +7 -0
- package/dist/shims/headers.js +9 -1
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/internal/app-route-detection.d.ts +37 -0
- package/dist/shims/internal/app-route-detection.js +69 -0
- package/dist/shims/internal/app-route-detection.js.map +1 -0
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +56 -0
- package/dist/shims/internal/pages-data-fetch-dedup.js +70 -0
- package/dist/shims/internal/pages-data-fetch-dedup.js.map +1 -0
- package/dist/shims/link.d.ts +18 -2
- package/dist/shims/link.js +98 -8
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.d.ts +7 -6
- package/dist/shims/metadata.js +9 -5
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/navigation.d.ts +40 -3
- package/dist/shims/navigation.js +124 -25
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/router.d.ts +5 -0
- package/dist/shims/router.js +51 -21
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/script.d.ts +11 -1
- package/dist/shims/script.js +75 -6
- package/dist/shims/script.js.map +1 -1
- package/dist/shims/thenable-params.d.ts +5 -2
- package/dist/shims/thenable-params.js +25 -1
- package/dist/shims/thenable-params.js.map +1 -1
- package/dist/shims/unified-request-context.js +3 -0
- package/dist/shims/unified-request-context.js.map +1 -1
- package/dist/utils/client-build-manifest.d.ts +15 -0
- package/dist/utils/client-build-manifest.js +54 -0
- package/dist/utils/client-build-manifest.js.map +1 -0
- package/dist/utils/hash.js +1 -1
- package/dist/utils/hash.js.map +1 -1
- package/dist/utils/lazy-chunks.d.ts +1 -1
- package/dist/utils/lazy-chunks.js.map +1 -1
- package/dist/utils/path.d.ts +13 -0
- package/dist/utils/path.js +16 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/vite-version.d.ts +11 -0
- package/dist/utils/vite-version.js +36 -0
- package/dist/utils/vite-version.js.map +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-router.js","names":[],"sources":["../../src/routing/app-router.ts"],"sourcesContent":["/**\n * App Router file-system routing.\n *\n * Scans the app/ directory following Next.js App Router conventions:\n * - app/page.tsx -> /\n * - app/about/page.tsx -> /about\n * - app/blog/[slug]/page.tsx -> /blog/:slug\n * - app/[...catchAll]/page.tsx -> /:catchAll+\n * - app/route.ts -> / (API route)\n * - app/(group)/page.tsx -> / (route groups are transparent)\n * - Layouts: app/layout.tsx wraps all children\n * - Loading: app/loading.tsx -> Suspense fallback\n * - Error: app/error.tsx -> ErrorBoundary\n * - Not Found: app/not-found.tsx\n */\nimport { createValidFileMatcher, type ValidFileMatcher } from \"./file-matcher.js\";\nimport { createRouteTrieCache, matchRouteWithTrie } from \"./route-matching.js\";\nimport {\n buildAppRouteGraph,\n type AppRoute,\n type AppRouteGraphRoute,\n type RouteManifest,\n} from \"./app-route-graph.js\";\nexport type { AppRoute } from \"./app-route-graph.js\";\nexport {
|
|
1
|
+
{"version":3,"file":"app-router.js","names":[],"sources":["../../src/routing/app-router.ts"],"sourcesContent":["/**\n * App Router file-system routing.\n *\n * Scans the app/ directory following Next.js App Router conventions:\n * - app/page.tsx -> /\n * - app/about/page.tsx -> /about\n * - app/blog/[slug]/page.tsx -> /blog/:slug\n * - app/[...catchAll]/page.tsx -> /:catchAll+\n * - app/route.ts -> / (API route)\n * - app/(group)/page.tsx -> / (route groups are transparent)\n * - Layouts: app/layout.tsx wraps all children\n * - Loading: app/loading.tsx -> Suspense fallback\n * - Error: app/error.tsx -> ErrorBoundary\n * - Not Found: app/not-found.tsx\n */\nimport { createValidFileMatcher, type ValidFileMatcher } from \"./file-matcher.js\";\nimport { createRouteTrieCache, matchRouteWithTrie } from \"./route-matching.js\";\nimport {\n buildAppRouteGraph,\n type AppRoute,\n type AppRouteGraphRoute,\n type RouteManifest,\n} from \"./app-route-graph.js\";\nexport type { AppRoute } from \"./app-route-graph.js\";\nexport {\n computeAppRouteStaticSiblings,\n computeRootParamNames,\n convertSegmentsToRouteParts,\n} from \"./app-route-graph.js\";\n\ntype AppRouteGraph = {\n routes: AppRouteGraphRoute[];\n routeManifest: RouteManifest;\n};\n\n// Cache for app routes\nlet cachedGraph: AppRouteGraph | null = null;\nlet cachedAppDir: string | null = null;\nlet cachedPageExtensionsKey: string | null = null;\n\nexport function invalidateAppRouteCache(): void {\n cachedGraph = null;\n cachedAppDir = null;\n cachedPageExtensionsKey = null;\n}\n\n/**\n * Scan the app/ directory and return the route graph.\n * TODO(#726): Layer 4 should consume this read model directly once the\n * navigation planner owns route graph facts.\n *\n * @internal\n */\nexport async function appRouteGraph(\n appDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<AppRouteGraph> {\n matcher ??= createValidFileMatcher(pageExtensions);\n const pageExtensionsKey = JSON.stringify(matcher.extensions);\n if (cachedGraph && cachedAppDir === appDir && cachedPageExtensionsKey === pageExtensionsKey) {\n return cachedGraph;\n }\n\n const graph = await buildAppRouteGraph(appDir, matcher);\n cachedGraph = graph;\n cachedAppDir = appDir;\n cachedPageExtensionsKey = pageExtensionsKey;\n return graph;\n}\n\n/**\n * Scan the app/ directory and return a list of routes.\n */\nexport async function appRouter(\n appDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<AppRouteGraphRoute[]> {\n const graph = await appRouteGraph(appDir, pageExtensions, matcher);\n return graph.routes;\n}\n\n// Trie cache — keyed by route array identity (same array = same trie)\nconst appTrieCache = createRouteTrieCache<AppRoute>();\n\n/**\n * Match a URL against App Router routes.\n */\nexport function matchAppRoute(\n url: string,\n routes: AppRoute[],\n): { route: AppRoute; params: Record<string, string | string[]> } | null {\n return matchRouteWithTrie(url, routes, appTrieCache);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoCA,IAAI,cAAoC;AACxC,IAAI,eAA8B;AAClC,IAAI,0BAAyC;AAE7C,SAAgB,0BAAgC;CAC9C,cAAc;CACd,eAAe;CACf,0BAA0B;;;;;;;;;AAU5B,eAAsB,cACpB,QACA,gBACA,SACwB;CACxB,YAAY,uBAAuB,eAAe;CAClD,MAAM,oBAAoB,KAAK,UAAU,QAAQ,WAAW;CAC5D,IAAI,eAAe,iBAAiB,UAAU,4BAA4B,mBACxE,OAAO;CAGT,MAAM,QAAQ,MAAM,mBAAmB,QAAQ,QAAQ;CACvD,cAAc;CACd,eAAe;CACf,0BAA0B;CAC1B,OAAO;;;;;AAMT,eAAsB,UACpB,QACA,gBACA,SAC+B;CAE/B,QAAO,MADa,cAAc,QAAQ,gBAAgB,QAAQ,EACrD;;AAIf,MAAM,eAAe,sBAAgC;;;;AAKrD,SAAgB,cACd,KACA,QACuE;CACvE,OAAO,mBAAmB,KAAK,QAAQ,aAAa"}
|
|
@@ -18,10 +18,30 @@ type ValidFileMatcher = {
|
|
|
18
18
|
declare function createValidFileMatcher(pageExtensions?: readonly string[] | null): ValidFileMatcher;
|
|
19
19
|
/** Check if a file exists with any configured page extension. */
|
|
20
20
|
declare function findFileWithExtensions(basePath: string, matcher: ValidFileMatcher): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Vite's default `resolve.extensions` covers `.tsx/.ts/.jsx/.js/.json` (and
|
|
23
|
+
* `.mjs/.mts`). When the user configures `pageExtensions` with values Vite
|
|
24
|
+
* does not know about — e.g. `["platform.tsx", "tsx", "mdx"]` from the
|
|
25
|
+
* Next.js `resolve-extensions` fixture — extensionless imports of those
|
|
26
|
+
* files fail to resolve, and the build crashes with "Custom deploy script
|
|
27
|
+
* failed: undefined (1)".
|
|
28
|
+
*
|
|
29
|
+
* Build the merged extension list that Vite should use:
|
|
30
|
+
*
|
|
31
|
+
* 1. User-configured pageExtensions go first (each prefixed with `.`) so
|
|
32
|
+
* the user's priority wins. e.g. `.platform.tsx` resolves before `.tsx`.
|
|
33
|
+
* 2. Vite's defaults follow, with duplicates removed.
|
|
34
|
+
*
|
|
35
|
+
* The user's pageExtensions retain their relative order, which is what
|
|
36
|
+
* Next.js / Turbopack do via the `resolveExtensions` config option.
|
|
37
|
+
*
|
|
38
|
+
* See: cloudflare/vinext#1502
|
|
39
|
+
*/
|
|
40
|
+
declare function buildViteResolveExtensions(pageExtensions?: readonly string[] | null, viteDefaults?: readonly string[]): string[];
|
|
21
41
|
/**
|
|
22
42
|
* Use function-form exclude for Node < 22.14 compatibility.
|
|
23
43
|
*/
|
|
24
44
|
declare function scanWithExtensions(stem: string, cwd: string, extensions: readonly string[], exclude?: (name: string) => boolean): AsyncGenerator<string>;
|
|
25
45
|
//#endregion
|
|
26
|
-
export { ValidFileMatcher, createValidFileMatcher, findFileWithExtensions, normalizePageExtensions, scanWithExtensions };
|
|
46
|
+
export { ValidFileMatcher, buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, normalizePageExtensions, scanWithExtensions };
|
|
27
47
|
//# sourceMappingURL=file-matcher.d.ts.map
|
|
@@ -65,6 +65,44 @@ function findFileWithExtensions(basePath, matcher) {
|
|
|
65
65
|
return matcher.dottedExtensions.some((ext) => existsSync(basePath + ext));
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
+
* Vite's default `resolve.extensions` covers `.tsx/.ts/.jsx/.js/.json` (and
|
|
69
|
+
* `.mjs/.mts`). When the user configures `pageExtensions` with values Vite
|
|
70
|
+
* does not know about — e.g. `["platform.tsx", "tsx", "mdx"]` from the
|
|
71
|
+
* Next.js `resolve-extensions` fixture — extensionless imports of those
|
|
72
|
+
* files fail to resolve, and the build crashes with "Custom deploy script
|
|
73
|
+
* failed: undefined (1)".
|
|
74
|
+
*
|
|
75
|
+
* Build the merged extension list that Vite should use:
|
|
76
|
+
*
|
|
77
|
+
* 1. User-configured pageExtensions go first (each prefixed with `.`) so
|
|
78
|
+
* the user's priority wins. e.g. `.platform.tsx` resolves before `.tsx`.
|
|
79
|
+
* 2. Vite's defaults follow, with duplicates removed.
|
|
80
|
+
*
|
|
81
|
+
* The user's pageExtensions retain their relative order, which is what
|
|
82
|
+
* Next.js / Turbopack do via the `resolveExtensions` config option.
|
|
83
|
+
*
|
|
84
|
+
* See: cloudflare/vinext#1502
|
|
85
|
+
*/
|
|
86
|
+
function buildViteResolveExtensions(pageExtensions, viteDefaults = [
|
|
87
|
+
".mjs",
|
|
88
|
+
".js",
|
|
89
|
+
".mts",
|
|
90
|
+
".ts",
|
|
91
|
+
".jsx",
|
|
92
|
+
".tsx",
|
|
93
|
+
".json"
|
|
94
|
+
]) {
|
|
95
|
+
const dotted = normalizePageExtensions(pageExtensions).map((ext) => `.${ext}`);
|
|
96
|
+
const seen = /* @__PURE__ */ new Set();
|
|
97
|
+
const result = [];
|
|
98
|
+
for (const ext of [...dotted, ...viteDefaults]) {
|
|
99
|
+
if (seen.has(ext)) continue;
|
|
100
|
+
seen.add(ext);
|
|
101
|
+
result.push(ext);
|
|
102
|
+
}
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
68
106
|
* Use function-form exclude for Node < 22.14 compatibility.
|
|
69
107
|
*/
|
|
70
108
|
async function* scanWithExtensions(stem, cwd, extensions, exclude) {
|
|
@@ -75,6 +113,6 @@ async function* scanWithExtensions(stem, cwd, extensions, exclude) {
|
|
|
75
113
|
})) yield file;
|
|
76
114
|
}
|
|
77
115
|
//#endregion
|
|
78
|
-
export { createValidFileMatcher, findFileWithExtensions, normalizePageExtensions, scanWithExtensions };
|
|
116
|
+
export { buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, normalizePageExtensions, scanWithExtensions };
|
|
79
117
|
|
|
80
118
|
//# sourceMappingURL=file-matcher.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-matcher.js","names":[],"sources":["../../src/routing/file-matcher.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { glob } from \"node:fs/promises\";\n\nconst DEFAULT_PAGE_EXTENSIONS = [\"tsx\", \"ts\", \"jsx\", \"js\"] as const;\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function normalizePageExtensions(pageExtensions?: readonly string[] | null): string[] {\n if (!Array.isArray(pageExtensions) || pageExtensions.length === 0) {\n return [...DEFAULT_PAGE_EXTENSIONS];\n }\n\n const filtered = pageExtensions\n .filter((ext): ext is string => typeof ext === \"string\")\n .map((ext) => ext.trim().replace(/^\\.+/, \"\"))\n .filter((ext) => ext.length > 0);\n return filtered.length > 0 ? [...filtered] : [...DEFAULT_PAGE_EXTENSIONS];\n}\n\nfunction buildExtensionGlob(stem: string, extensions: readonly string[]): string {\n if (extensions.length === 1) {\n return `${stem}.${extensions[0]}`;\n }\n return `${stem}.{${extensions.join(\",\")}}`;\n}\n\nexport type ValidFileMatcher = {\n extensions: string[];\n dottedExtensions: string[];\n extensionRegex: RegExp;\n isPageFile(filePath: string): boolean;\n isAppRouterPage(filePath: string): boolean;\n isAppRouterRoute(filePath: string): boolean;\n isAppLayoutFile(filePath: string): boolean;\n isAppDefaultFile(filePath: string): boolean;\n stripExtension(filePath: string): string;\n};\n\n/**\n * Ported in spirit from Next.js createValidFileMatcher:\n * packages/next/src/server/lib/find-page-file.ts\n */\nexport function createValidFileMatcher(\n pageExtensions?: readonly string[] | null,\n): ValidFileMatcher {\n const extensions = normalizePageExtensions(pageExtensions);\n const dottedExtensions = extensions.map((ext) => `.${ext}`);\n const extPattern = `(?:${extensions.map((ext) => escapeRegex(ext)).join(\"|\")})`;\n\n const extensionRegex = new RegExp(`\\\\.${extPattern}$`);\n const createLeafPattern = (fileNames: readonly string[]): RegExp => {\n const names = fileNames.length === 1 ? fileNames[0] : `(${fileNames.join(\"|\")})`;\n return new RegExp(`(^${names}|[\\\\\\\\/]${names})\\\\.${extPattern}$`);\n };\n\n const appRouterPageRegex = createLeafPattern([\"page\", \"route\"]);\n const appRouterRouteRegex = createLeafPattern([\"route\"]);\n const appLayoutRegex = createLeafPattern([\"layout\"]);\n const appDefaultRegex = createLeafPattern([\"default\"]);\n\n return {\n extensions,\n dottedExtensions,\n extensionRegex,\n isPageFile(filePath: string) {\n return extensionRegex.test(filePath);\n },\n isAppRouterPage(filePath: string) {\n return appRouterPageRegex.test(filePath);\n },\n isAppRouterRoute(filePath: string) {\n return appRouterRouteRegex.test(filePath);\n },\n isAppLayoutFile(filePath: string) {\n return appLayoutRegex.test(filePath);\n },\n isAppDefaultFile(filePath: string) {\n return appDefaultRegex.test(filePath);\n },\n stripExtension(filePath: string) {\n return filePath.replace(extensionRegex, \"\");\n },\n };\n}\n\n/** Check if a file exists with any configured page extension. */\nexport function findFileWithExtensions(basePath: string, matcher: ValidFileMatcher): boolean {\n return matcher.dottedExtensions.some((ext) => existsSync(basePath + ext));\n}\n\n/**\n * Use function-form exclude for Node < 22.14 compatibility.\n */\nexport async function* scanWithExtensions(\n stem: string,\n cwd: string,\n extensions: readonly string[],\n exclude?: (name: string) => boolean,\n): AsyncGenerator<string> {\n const pattern = buildExtensionGlob(stem, extensions);\n for await (const file of glob(pattern, {\n cwd,\n ...(exclude ? { exclude } : {}),\n })) {\n yield file;\n }\n}\n"],"mappings":";;;AAGA,MAAM,0BAA0B;CAAC;CAAO;CAAM;CAAO;CAAK;AAE1D,SAAS,YAAY,OAAuB;CAC1C,OAAO,MAAM,QAAQ,uBAAuB,OAAO;;AAGrD,SAAgB,wBAAwB,gBAAqD;CAC3F,IAAI,CAAC,MAAM,QAAQ,eAAe,IAAI,eAAe,WAAW,GAC9D,OAAO,CAAC,GAAG,wBAAwB;CAGrC,MAAM,WAAW,eACd,QAAQ,QAAuB,OAAO,QAAQ,SAAS,CACvD,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,QAAQ,GAAG,CAAC,CAC5C,QAAQ,QAAQ,IAAI,SAAS,EAAE;CAClC,OAAO,SAAS,SAAS,IAAI,CAAC,GAAG,SAAS,GAAG,CAAC,GAAG,wBAAwB;;AAG3E,SAAS,mBAAmB,MAAc,YAAuC;CAC/E,IAAI,WAAW,WAAW,GACxB,OAAO,GAAG,KAAK,GAAG,WAAW;CAE/B,OAAO,GAAG,KAAK,IAAI,WAAW,KAAK,IAAI,CAAC;;;;;;AAmB1C,SAAgB,uBACd,gBACkB;CAClB,MAAM,aAAa,wBAAwB,eAAe;CAC1D,MAAM,mBAAmB,WAAW,KAAK,QAAQ,IAAI,MAAM;CAC3D,MAAM,aAAa,MAAM,WAAW,KAAK,QAAQ,YAAY,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC;CAE7E,MAAM,iBAAiB,IAAI,OAAO,MAAM,WAAW,GAAG;CACtD,MAAM,qBAAqB,cAAyC;EAClE,MAAM,QAAQ,UAAU,WAAW,IAAI,UAAU,KAAK,IAAI,UAAU,KAAK,IAAI,CAAC;EAC9E,OAAO,IAAI,OAAO,KAAK,MAAM,UAAU,MAAM,MAAM,WAAW,GAAG;;CAGnE,MAAM,qBAAqB,kBAAkB,CAAC,QAAQ,QAAQ,CAAC;CAC/D,MAAM,sBAAsB,kBAAkB,CAAC,QAAQ,CAAC;CACxD,MAAM,iBAAiB,kBAAkB,CAAC,SAAS,CAAC;CACpD,MAAM,kBAAkB,kBAAkB,CAAC,UAAU,CAAC;CAEtD,OAAO;EACL;EACA;EACA;EACA,WAAW,UAAkB;GAC3B,OAAO,eAAe,KAAK,SAAS;;EAEtC,gBAAgB,UAAkB;GAChC,OAAO,mBAAmB,KAAK,SAAS;;EAE1C,iBAAiB,UAAkB;GACjC,OAAO,oBAAoB,KAAK,SAAS;;EAE3C,gBAAgB,UAAkB;GAChC,OAAO,eAAe,KAAK,SAAS;;EAEtC,iBAAiB,UAAkB;GACjC,OAAO,gBAAgB,KAAK,SAAS;;EAEvC,eAAe,UAAkB;GAC/B,OAAO,SAAS,QAAQ,gBAAgB,GAAG;;EAE9C;;;AAIH,SAAgB,uBAAuB,UAAkB,SAAoC;CAC3F,OAAO,QAAQ,iBAAiB,MAAM,QAAQ,WAAW,WAAW,IAAI,CAAC;;;;;
|
|
1
|
+
{"version":3,"file":"file-matcher.js","names":[],"sources":["../../src/routing/file-matcher.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { glob } from \"node:fs/promises\";\n\nconst DEFAULT_PAGE_EXTENSIONS = [\"tsx\", \"ts\", \"jsx\", \"js\"] as const;\n\nfunction escapeRegex(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport function normalizePageExtensions(pageExtensions?: readonly string[] | null): string[] {\n if (!Array.isArray(pageExtensions) || pageExtensions.length === 0) {\n return [...DEFAULT_PAGE_EXTENSIONS];\n }\n\n const filtered = pageExtensions\n .filter((ext): ext is string => typeof ext === \"string\")\n .map((ext) => ext.trim().replace(/^\\.+/, \"\"))\n .filter((ext) => ext.length > 0);\n return filtered.length > 0 ? [...filtered] : [...DEFAULT_PAGE_EXTENSIONS];\n}\n\nfunction buildExtensionGlob(stem: string, extensions: readonly string[]): string {\n if (extensions.length === 1) {\n return `${stem}.${extensions[0]}`;\n }\n return `${stem}.{${extensions.join(\",\")}}`;\n}\n\nexport type ValidFileMatcher = {\n extensions: string[];\n dottedExtensions: string[];\n extensionRegex: RegExp;\n isPageFile(filePath: string): boolean;\n isAppRouterPage(filePath: string): boolean;\n isAppRouterRoute(filePath: string): boolean;\n isAppLayoutFile(filePath: string): boolean;\n isAppDefaultFile(filePath: string): boolean;\n stripExtension(filePath: string): string;\n};\n\n/**\n * Ported in spirit from Next.js createValidFileMatcher:\n * packages/next/src/server/lib/find-page-file.ts\n */\nexport function createValidFileMatcher(\n pageExtensions?: readonly string[] | null,\n): ValidFileMatcher {\n const extensions = normalizePageExtensions(pageExtensions);\n const dottedExtensions = extensions.map((ext) => `.${ext}`);\n const extPattern = `(?:${extensions.map((ext) => escapeRegex(ext)).join(\"|\")})`;\n\n const extensionRegex = new RegExp(`\\\\.${extPattern}$`);\n const createLeafPattern = (fileNames: readonly string[]): RegExp => {\n const names = fileNames.length === 1 ? fileNames[0] : `(${fileNames.join(\"|\")})`;\n return new RegExp(`(^${names}|[\\\\\\\\/]${names})\\\\.${extPattern}$`);\n };\n\n const appRouterPageRegex = createLeafPattern([\"page\", \"route\"]);\n const appRouterRouteRegex = createLeafPattern([\"route\"]);\n const appLayoutRegex = createLeafPattern([\"layout\"]);\n const appDefaultRegex = createLeafPattern([\"default\"]);\n\n return {\n extensions,\n dottedExtensions,\n extensionRegex,\n isPageFile(filePath: string) {\n return extensionRegex.test(filePath);\n },\n isAppRouterPage(filePath: string) {\n return appRouterPageRegex.test(filePath);\n },\n isAppRouterRoute(filePath: string) {\n return appRouterRouteRegex.test(filePath);\n },\n isAppLayoutFile(filePath: string) {\n return appLayoutRegex.test(filePath);\n },\n isAppDefaultFile(filePath: string) {\n return appDefaultRegex.test(filePath);\n },\n stripExtension(filePath: string) {\n return filePath.replace(extensionRegex, \"\");\n },\n };\n}\n\n/** Check if a file exists with any configured page extension. */\nexport function findFileWithExtensions(basePath: string, matcher: ValidFileMatcher): boolean {\n return matcher.dottedExtensions.some((ext) => existsSync(basePath + ext));\n}\n\n/**\n * Vite's default `resolve.extensions` covers `.tsx/.ts/.jsx/.js/.json` (and\n * `.mjs/.mts`). When the user configures `pageExtensions` with values Vite\n * does not know about — e.g. `[\"platform.tsx\", \"tsx\", \"mdx\"]` from the\n * Next.js `resolve-extensions` fixture — extensionless imports of those\n * files fail to resolve, and the build crashes with \"Custom deploy script\n * failed: undefined (1)\".\n *\n * Build the merged extension list that Vite should use:\n *\n * 1. User-configured pageExtensions go first (each prefixed with `.`) so\n * the user's priority wins. e.g. `.platform.tsx` resolves before `.tsx`.\n * 2. Vite's defaults follow, with duplicates removed.\n *\n * The user's pageExtensions retain their relative order, which is what\n * Next.js / Turbopack do via the `resolveExtensions` config option.\n *\n * See: cloudflare/vinext#1502\n */\nexport function buildViteResolveExtensions(\n pageExtensions?: readonly string[] | null,\n viteDefaults: readonly string[] = [\".mjs\", \".js\", \".mts\", \".ts\", \".jsx\", \".tsx\", \".json\"],\n): string[] {\n const normalized = normalizePageExtensions(pageExtensions);\n const dotted = normalized.map((ext) => `.${ext}`);\n const seen = new Set<string>();\n const result: string[] = [];\n for (const ext of [...dotted, ...viteDefaults]) {\n if (seen.has(ext)) continue;\n seen.add(ext);\n result.push(ext);\n }\n return result;\n}\n\n/**\n * Use function-form exclude for Node < 22.14 compatibility.\n */\nexport async function* scanWithExtensions(\n stem: string,\n cwd: string,\n extensions: readonly string[],\n exclude?: (name: string) => boolean,\n): AsyncGenerator<string> {\n const pattern = buildExtensionGlob(stem, extensions);\n for await (const file of glob(pattern, {\n cwd,\n ...(exclude ? { exclude } : {}),\n })) {\n yield file;\n }\n}\n"],"mappings":";;;AAGA,MAAM,0BAA0B;CAAC;CAAO;CAAM;CAAO;CAAK;AAE1D,SAAS,YAAY,OAAuB;CAC1C,OAAO,MAAM,QAAQ,uBAAuB,OAAO;;AAGrD,SAAgB,wBAAwB,gBAAqD;CAC3F,IAAI,CAAC,MAAM,QAAQ,eAAe,IAAI,eAAe,WAAW,GAC9D,OAAO,CAAC,GAAG,wBAAwB;CAGrC,MAAM,WAAW,eACd,QAAQ,QAAuB,OAAO,QAAQ,SAAS,CACvD,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,QAAQ,GAAG,CAAC,CAC5C,QAAQ,QAAQ,IAAI,SAAS,EAAE;CAClC,OAAO,SAAS,SAAS,IAAI,CAAC,GAAG,SAAS,GAAG,CAAC,GAAG,wBAAwB;;AAG3E,SAAS,mBAAmB,MAAc,YAAuC;CAC/E,IAAI,WAAW,WAAW,GACxB,OAAO,GAAG,KAAK,GAAG,WAAW;CAE/B,OAAO,GAAG,KAAK,IAAI,WAAW,KAAK,IAAI,CAAC;;;;;;AAmB1C,SAAgB,uBACd,gBACkB;CAClB,MAAM,aAAa,wBAAwB,eAAe;CAC1D,MAAM,mBAAmB,WAAW,KAAK,QAAQ,IAAI,MAAM;CAC3D,MAAM,aAAa,MAAM,WAAW,KAAK,QAAQ,YAAY,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC;CAE7E,MAAM,iBAAiB,IAAI,OAAO,MAAM,WAAW,GAAG;CACtD,MAAM,qBAAqB,cAAyC;EAClE,MAAM,QAAQ,UAAU,WAAW,IAAI,UAAU,KAAK,IAAI,UAAU,KAAK,IAAI,CAAC;EAC9E,OAAO,IAAI,OAAO,KAAK,MAAM,UAAU,MAAM,MAAM,WAAW,GAAG;;CAGnE,MAAM,qBAAqB,kBAAkB,CAAC,QAAQ,QAAQ,CAAC;CAC/D,MAAM,sBAAsB,kBAAkB,CAAC,QAAQ,CAAC;CACxD,MAAM,iBAAiB,kBAAkB,CAAC,SAAS,CAAC;CACpD,MAAM,kBAAkB,kBAAkB,CAAC,UAAU,CAAC;CAEtD,OAAO;EACL;EACA;EACA;EACA,WAAW,UAAkB;GAC3B,OAAO,eAAe,KAAK,SAAS;;EAEtC,gBAAgB,UAAkB;GAChC,OAAO,mBAAmB,KAAK,SAAS;;EAE1C,iBAAiB,UAAkB;GACjC,OAAO,oBAAoB,KAAK,SAAS;;EAE3C,gBAAgB,UAAkB;GAChC,OAAO,eAAe,KAAK,SAAS;;EAEtC,iBAAiB,UAAkB;GACjC,OAAO,gBAAgB,KAAK,SAAS;;EAEvC,eAAe,UAAkB;GAC/B,OAAO,SAAS,QAAQ,gBAAgB,GAAG;;EAE9C;;;AAIH,SAAgB,uBAAuB,UAAkB,SAAoC;CAC3F,OAAO,QAAQ,iBAAiB,MAAM,QAAQ,WAAW,WAAW,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;AAsB3E,SAAgB,2BACd,gBACA,eAAkC;CAAC;CAAQ;CAAO;CAAQ;CAAO;CAAQ;CAAQ;CAAQ,EAC/E;CAEV,MAAM,SADa,wBAAwB,eAClB,CAAC,KAAK,QAAQ,IAAI,MAAM;CACjD,MAAM,uBAAO,IAAI,KAAa;CAC9B,MAAM,SAAmB,EAAE;CAC3B,KAAK,MAAM,OAAO,CAAC,GAAG,QAAQ,GAAG,aAAa,EAAE;EAC9C,IAAI,KAAK,IAAI,IAAI,EAAE;EACnB,KAAK,IAAI,IAAI;EACb,OAAO,KAAK,IAAI;;CAElB,OAAO;;;;;AAMT,gBAAuB,mBACrB,MACA,KACA,YACA,SACwB;CACxB,MAAM,UAAU,mBAAmB,MAAM,WAAW;CACpD,WAAW,MAAM,QAAQ,KAAK,SAAS;EACrC;EACA,GAAI,UAAU,EAAE,SAAS,GAAG,EAAE;EAC/B,CAAC,EACA,MAAM"}
|
|
@@ -23,7 +23,7 @@ declare function invalidateRouteCache(pagesDir: string): void;
|
|
|
23
23
|
* - pages/about.tsx -> /about
|
|
24
24
|
* - pages/posts/[id].tsx -> /posts/:id
|
|
25
25
|
* - pages/[...slug].tsx -> /:slug+
|
|
26
|
-
* - Ignores _app.tsx, _document.tsx, _error.tsx
|
|
26
|
+
* - Ignores _app.tsx, _document.tsx, _error.tsx (Next.js special files)
|
|
27
27
|
* - Ignores pages/api/ (handled separately later)
|
|
28
28
|
*/
|
|
29
29
|
declare function pagesRouter(pagesDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<Route[]>;
|
|
@@ -4,6 +4,12 @@ import { patternToNextFormat, validateRoutePatterns } from "./route-validation.j
|
|
|
4
4
|
import { createRouteTrieCache, matchRouteWithTrie } from "./route-matching.js";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
//#region src/routing/pages-router.ts
|
|
7
|
+
/** Next.js special pages that should not produce routes. */
|
|
8
|
+
const RESERVED_PAGE_NAMES = new Set([
|
|
9
|
+
"_app",
|
|
10
|
+
"_document",
|
|
11
|
+
"_error"
|
|
12
|
+
]);
|
|
7
13
|
const routeCache = /* @__PURE__ */ new Map();
|
|
8
14
|
/**
|
|
9
15
|
* Invalidate cached routes for a given pages directory.
|
|
@@ -21,7 +27,7 @@ function invalidateRouteCache(pagesDir) {
|
|
|
21
27
|
* - pages/about.tsx -> /about
|
|
22
28
|
* - pages/posts/[id].tsx -> /posts/:id
|
|
23
29
|
* - pages/[...slug].tsx -> /:slug+
|
|
24
|
-
* - Ignores _app.tsx, _document.tsx, _error.tsx
|
|
30
|
+
* - Ignores _app.tsx, _document.tsx, _error.tsx (Next.js special files)
|
|
25
31
|
* - Ignores pages/api/ (handled separately later)
|
|
26
32
|
*/
|
|
27
33
|
async function pagesRouter(pagesDir, pageExtensions, matcher) {
|
|
@@ -43,7 +49,7 @@ async function pagesRouter(pagesDir, pageExtensions, matcher) {
|
|
|
43
49
|
}
|
|
44
50
|
async function scanPageRoutes(pagesDir, matcher) {
|
|
45
51
|
const routes = [];
|
|
46
|
-
for await (const file of scanWithExtensions("**/*", pagesDir, matcher.extensions, (name) => name === "api" ||
|
|
52
|
+
for await (const file of scanWithExtensions("**/*", pagesDir, matcher.extensions, (name) => name === "api" || RESERVED_PAGE_NAMES.has(name))) {
|
|
47
53
|
const route = fileToRoute(file, pagesDir, matcher);
|
|
48
54
|
if (route) routes.push(route);
|
|
49
55
|
}
|
|
@@ -93,6 +99,7 @@ function fileToRoute(file, pagesDir, matcher) {
|
|
|
93
99
|
urlSegments.push(decodeRouteSegment(segment));
|
|
94
100
|
}
|
|
95
101
|
const pattern = "/" + urlSegments.join("/");
|
|
102
|
+
if (segments.length === 1 && RESERVED_PAGE_NAMES.has(segments[0])) return null;
|
|
96
103
|
return {
|
|
97
104
|
pattern: pattern === "/" ? "/" : pattern,
|
|
98
105
|
patternParts: urlSegments.filter(Boolean),
|
|
@@ -139,7 +146,7 @@ async function scanApiRoutes(pagesDir, matcher) {
|
|
|
139
146
|
let files;
|
|
140
147
|
try {
|
|
141
148
|
files = [];
|
|
142
|
-
for await (const file of scanWithExtensions("**/*", apiDir, matcher.extensions
|
|
149
|
+
for await (const file of scanWithExtensions("**/*", apiDir, matcher.extensions)) files.push(file);
|
|
143
150
|
} catch {
|
|
144
151
|
files = [];
|
|
145
152
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages-router.js","names":[],"sources":["../../src/routing/pages-router.ts"],"sourcesContent":["import path from \"node:path\";\nimport { compareRoutes, decodeRouteSegment } from \"./utils.js\";\nimport {\n createValidFileMatcher,\n scanWithExtensions,\n type ValidFileMatcher,\n} from \"./file-matcher.js\";\nimport { validateRoutePatterns } from \"./route-validation.js\";\nimport { createRouteTrieCache, matchRouteWithTrie } from \"./route-matching.js\";\n\nexport type Route = {\n /** URL pattern, e.g. \"/\" or \"/about\" or \"/posts/:id\" */\n pattern: string;\n /** Pre-split pattern segments (computed once at scan time, reused per request) */\n patternParts: string[];\n /** Absolute file path to the page component */\n filePath: string;\n /** Whether this is a dynamic route */\n isDynamic: boolean;\n /** Parameter names for dynamic segments */\n params: string[];\n};\n\n// Route cache — invalidated when pages directory changes\nconst routeCache = new Map<string, { routes: Route[]; promise: Promise<Route[]> }>();\n\n/**\n * Invalidate cached routes for a given pages directory.\n * Called by the file watcher when pages are added/removed.\n */\nexport function invalidateRouteCache(pagesDir: string): void {\n for (const key of routeCache.keys()) {\n if (key.startsWith(`pages:${pagesDir}:`) || key.startsWith(`api:${pagesDir}:`)) {\n routeCache.delete(key);\n }\n }\n}\n\n/**\n * Scan the pages/ directory and return a list of routes.\n * Results are cached — call invalidateRouteCache() when files change.\n *\n * Follows Next.js Pages Router conventions:\n * - pages/index.tsx -> /\n * - pages/about.tsx -> /about\n * - pages/posts/[id].tsx -> /posts/:id\n * - pages/[...slug].tsx -> /:slug+\n * - Ignores _app.tsx, _document.tsx, _error.tsx, files starting with _\n * - Ignores pages/api/ (handled separately later)\n */\nexport async function pagesRouter(\n pagesDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<Route[]> {\n matcher ??= createValidFileMatcher(pageExtensions);\n const cacheKey = `pages:${pagesDir}:${JSON.stringify(matcher.extensions)}`;\n const cached = routeCache.get(cacheKey);\n if (cached) return cached.promise;\n\n const promise = scanPageRoutes(pagesDir, matcher);\n routeCache.set(cacheKey, { routes: [], promise });\n const routes = await promise;\n routeCache.set(cacheKey, { routes, promise });\n return routes;\n}\n\nasync function scanPageRoutes(pagesDir: string, matcher: ValidFileMatcher): Promise<Route[]> {\n const routes: Route[] = [];\n\n // Use function form of exclude for Node < 22.14 compatibility (string arrays require >= 22.14)\n for await (const file of scanWithExtensions(\n \"**/*\",\n pagesDir,\n matcher.extensions,\n (name: string) => name === \"api\" || name.startsWith(\"_\"),\n )) {\n const route = fileToRoute(file, pagesDir, matcher);\n if (route) routes.push(route);\n }\n\n validateRoutePatterns(routes.map((route) => route.pattern));\n\n // Sort: static routes first, then dynamic, then catch-all\n routes.sort(compareRoutes);\n\n return routes;\n}\n\n/**\n * Convert a file path relative to pages/ into a Route.\n */\nfunction fileToRoute(file: string, pagesDir: string, matcher: ValidFileMatcher): Route | null {\n // Remove extension\n const withoutExt = matcher.stripExtension(file);\n if (withoutExt === file) return null;\n\n // Convert to URL segments\n const segments = withoutExt.split(path.sep);\n\n // Handle index files: pages/index.tsx -> /\n const lastSegment = segments[segments.length - 1];\n if (lastSegment === \"index\") {\n segments.pop();\n }\n\n const params: string[] = [];\n let isDynamic = false;\n\n // Convert Next.js dynamic segments to URL patterns.\n // Catch-all segments are only valid in terminal position.\n const urlSegments: string[] = [];\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i];\n\n // Catch-all: [...slug] -> :slug+ (param names may contain any non-] chars)\n // Matches Next.js PARAMETER_PATTERN.\n const catchAllMatch = segment.match(/^\\[\\.\\.\\.([^\\]]+)\\]$/);\n if (catchAllMatch) {\n if (i !== segments.length - 1) return null;\n // Guard: names ending in + or * would collide with internal pattern modifiers.\n if (catchAllMatch[1].endsWith(\"+\") || catchAllMatch[1].endsWith(\"*\")) return null;\n isDynamic = true;\n params.push(catchAllMatch[1]);\n urlSegments.push(`:${catchAllMatch[1]}+`);\n continue;\n }\n\n // Optional catch-all: [[...slug]] -> :slug* (param names may contain any non-] chars)\n const optionalCatchAllMatch = segment.match(/^\\[\\[\\.\\.\\.([^\\]]+)\\]\\]$/);\n if (optionalCatchAllMatch) {\n if (i !== segments.length - 1) return null;\n if (optionalCatchAllMatch[1].endsWith(\"+\") || optionalCatchAllMatch[1].endsWith(\"*\"))\n return null;\n isDynamic = true;\n params.push(optionalCatchAllMatch[1]);\n urlSegments.push(`:${optionalCatchAllMatch[1]}*`);\n continue;\n }\n\n // Dynamic segment: [id] -> :id (param names may contain any non-] chars)\n const dynamicMatch = segment.match(/^\\[([^\\]]+)\\]$/);\n if (dynamicMatch) {\n if (dynamicMatch[1].endsWith(\"+\") || dynamicMatch[1].endsWith(\"*\")) return null;\n isDynamic = true;\n params.push(dynamicMatch[1]);\n urlSegments.push(`:${dynamicMatch[1]}`);\n continue;\n }\n\n urlSegments.push(decodeRouteSegment(segment));\n }\n\n const pattern = \"/\" + urlSegments.join(\"/\");\n\n return {\n pattern: pattern === \"/\" ? \"/\" : pattern,\n patternParts: urlSegments.filter(Boolean),\n filePath: path.join(pagesDir, file),\n isDynamic,\n params,\n };\n}\n\n// Trie cache — keyed by route array identity (same array = same trie)\nconst trieCache = createRouteTrieCache<Route>();\n\n/**\n * Match a URL path against a route pattern.\n * Returns the matched params or null if no match.\n */\nexport function matchRoute(\n url: string,\n routes: Route[],\n): { route: Route; params: Record<string, string | string[]> } | null {\n return matchRouteWithTrie(url, routes, trieCache);\n}\n\n/**\n * Scan the pages/api/ directory and return API routes.\n * Results are cached — call invalidateRouteCache() when files change.\n *\n * Follows Next.js conventions:\n * - pages/api/hello.ts -> /api/hello\n * - pages/api/users/[id].ts -> /api/users/:id\n */\nexport async function apiRouter(\n pagesDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<Route[]> {\n matcher ??= createValidFileMatcher(pageExtensions);\n const cacheKey = `api:${pagesDir}:${JSON.stringify(matcher.extensions)}`;\n const cached = routeCache.get(cacheKey);\n if (cached) return cached.promise;\n\n const promise = scanApiRoutes(pagesDir, matcher);\n routeCache.set(cacheKey, { routes: [], promise });\n const routes = await promise;\n routeCache.set(cacheKey, { routes, promise });\n return routes;\n}\n\nasync function scanApiRoutes(pagesDir: string, matcher: ValidFileMatcher): Promise<Route[]> {\n const apiDir = path.join(pagesDir, \"api\");\n let files: string[];\n try {\n files = [];\n for await (const file of scanWithExtensions(\n \"**/*\",\n apiDir,\n matcher.extensions,\n (name: string) => name.startsWith(\"_\"),\n )) {\n files.push(file);\n }\n } catch {\n files = [];\n }\n\n const routes: Route[] = [];\n\n for (const file of files) {\n // Reuse fileToRoute but pretend the file is under a virtual \"api/\" prefix\n const route = fileToRoute(path.join(\"api\", file), pagesDir, matcher);\n if (route) {\n routes.push(route);\n }\n }\n\n validateRoutePatterns(routes.map((route) => route.pattern));\n\n // Sort same as page routes\n routes.sort(compareRoutes);\n\n return routes;\n}\n\n/**\n * Convert internal route pattern (e.g., \"/posts/:id\", \"/docs/:slug+\")\n * to Next.js bracket format (e.g., \"/posts/[id]\", \"/docs/[...slug]\").\n * Used for __NEXT_DATA__.page which apps expect in Next.js format.\n */\nexport { patternToNextFormat } from \"./route-validation.js\";\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pages-router.js","names":[],"sources":["../../src/routing/pages-router.ts"],"sourcesContent":["import path from \"node:path\";\nimport { compareRoutes, decodeRouteSegment } from \"./utils.js\";\nimport {\n createValidFileMatcher,\n scanWithExtensions,\n type ValidFileMatcher,\n} from \"./file-matcher.js\";\nimport { validateRoutePatterns } from \"./route-validation.js\";\nimport { createRouteTrieCache, matchRouteWithTrie } from \"./route-matching.js\";\n\nexport type Route = {\n /** URL pattern, e.g. \"/\" or \"/about\" or \"/posts/:id\" */\n pattern: string;\n /** Pre-split pattern segments (computed once at scan time, reused per request) */\n patternParts: string[];\n /** Absolute file path to the page component */\n filePath: string;\n /** Whether this is a dynamic route */\n isDynamic: boolean;\n /** Parameter names for dynamic segments */\n params: string[];\n};\n\n/** Next.js special pages that should not produce routes. */\nconst RESERVED_PAGE_NAMES = new Set([\"_app\", \"_document\", \"_error\"]);\n\n// Route cache — invalidated when pages directory changes\nconst routeCache = new Map<string, { routes: Route[]; promise: Promise<Route[]> }>();\n\n/**\n * Invalidate cached routes for a given pages directory.\n * Called by the file watcher when pages are added/removed.\n */\nexport function invalidateRouteCache(pagesDir: string): void {\n for (const key of routeCache.keys()) {\n if (key.startsWith(`pages:${pagesDir}:`) || key.startsWith(`api:${pagesDir}:`)) {\n routeCache.delete(key);\n }\n }\n}\n\n/**\n * Scan the pages/ directory and return a list of routes.\n * Results are cached — call invalidateRouteCache() when files change.\n *\n * Follows Next.js Pages Router conventions:\n * - pages/index.tsx -> /\n * - pages/about.tsx -> /about\n * - pages/posts/[id].tsx -> /posts/:id\n * - pages/[...slug].tsx -> /:slug+\n * - Ignores _app.tsx, _document.tsx, _error.tsx (Next.js special files)\n * - Ignores pages/api/ (handled separately later)\n */\nexport async function pagesRouter(\n pagesDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<Route[]> {\n matcher ??= createValidFileMatcher(pageExtensions);\n const cacheKey = `pages:${pagesDir}:${JSON.stringify(matcher.extensions)}`;\n const cached = routeCache.get(cacheKey);\n if (cached) return cached.promise;\n\n const promise = scanPageRoutes(pagesDir, matcher);\n routeCache.set(cacheKey, { routes: [], promise });\n const routes = await promise;\n routeCache.set(cacheKey, { routes, promise });\n return routes;\n}\n\nasync function scanPageRoutes(pagesDir: string, matcher: ValidFileMatcher): Promise<Route[]> {\n const routes: Route[] = [];\n\n // Use function form of exclude for Node < 22.14 compatibility (string arrays require >= 22.14).\n // The `RESERVED_PAGE_NAMES` check here is a directory-traversal optimization only — glob's\n // exclude callback fires on directory names, not file names, so root-level files like\n // `_app.tsx` still get yielded and are filtered by the guard in `fileToRoute()` below.\n for await (const file of scanWithExtensions(\n \"**/*\",\n pagesDir,\n matcher.extensions,\n (name: string) => name === \"api\" || RESERVED_PAGE_NAMES.has(name),\n )) {\n const route = fileToRoute(file, pagesDir, matcher);\n if (route) routes.push(route);\n }\n\n validateRoutePatterns(routes.map((route) => route.pattern));\n\n // Sort: static routes first, then dynamic, then catch-all\n routes.sort(compareRoutes);\n\n return routes;\n}\n\n/**\n * Convert a file path relative to pages/ into a Route.\n */\nfunction fileToRoute(file: string, pagesDir: string, matcher: ValidFileMatcher): Route | null {\n // Remove extension\n const withoutExt = matcher.stripExtension(file);\n if (withoutExt === file) return null;\n\n // Convert to URL segments\n const segments = withoutExt.split(path.sep);\n\n // Handle index files: pages/index.tsx -> /\n const lastSegment = segments[segments.length - 1];\n if (lastSegment === \"index\") {\n segments.pop();\n }\n\n const params: string[] = [];\n let isDynamic = false;\n\n // Convert Next.js dynamic segments to URL patterns.\n // Catch-all segments are only valid in terminal position.\n const urlSegments: string[] = [];\n for (let i = 0; i < segments.length; i++) {\n const segment = segments[i];\n\n // Catch-all: [...slug] -> :slug+ (param names may contain any non-] chars)\n // Matches Next.js PARAMETER_PATTERN.\n const catchAllMatch = segment.match(/^\\[\\.\\.\\.([^\\]]+)\\]$/);\n if (catchAllMatch) {\n if (i !== segments.length - 1) return null;\n // Guard: names ending in + or * would collide with internal pattern modifiers.\n if (catchAllMatch[1].endsWith(\"+\") || catchAllMatch[1].endsWith(\"*\")) return null;\n isDynamic = true;\n params.push(catchAllMatch[1]);\n urlSegments.push(`:${catchAllMatch[1]}+`);\n continue;\n }\n\n // Optional catch-all: [[...slug]] -> :slug* (param names may contain any non-] chars)\n const optionalCatchAllMatch = segment.match(/^\\[\\[\\.\\.\\.([^\\]]+)\\]\\]$/);\n if (optionalCatchAllMatch) {\n if (i !== segments.length - 1) return null;\n if (optionalCatchAllMatch[1].endsWith(\"+\") || optionalCatchAllMatch[1].endsWith(\"*\"))\n return null;\n isDynamic = true;\n params.push(optionalCatchAllMatch[1]);\n urlSegments.push(`:${optionalCatchAllMatch[1]}*`);\n continue;\n }\n\n // Dynamic segment: [id] -> :id (param names may contain any non-] chars)\n const dynamicMatch = segment.match(/^\\[([^\\]]+)\\]$/);\n if (dynamicMatch) {\n if (dynamicMatch[1].endsWith(\"+\") || dynamicMatch[1].endsWith(\"*\")) return null;\n isDynamic = true;\n params.push(dynamicMatch[1]);\n urlSegments.push(`:${dynamicMatch[1]}`);\n continue;\n }\n\n urlSegments.push(decodeRouteSegment(segment));\n }\n\n const pattern = \"/\" + urlSegments.join(\"/\");\n\n // Skip Next.js special pages (_app, _document, _error) at the root level only.\n // Subdirectory files like admin/_app.tsx are not reserved and should be served.\n // Read segments[0] after the index pop so this is correct for both `_app.tsx`\n // and `_app/index.tsx` shapes, independent of the glob-level exclude.\n if (segments.length === 1 && RESERVED_PAGE_NAMES.has(segments[0])) {\n return null;\n }\n\n return {\n pattern: pattern === \"/\" ? \"/\" : pattern,\n patternParts: urlSegments.filter(Boolean),\n filePath: path.join(pagesDir, file),\n isDynamic,\n params,\n };\n}\n\n// Trie cache — keyed by route array identity (same array = same trie)\nconst trieCache = createRouteTrieCache<Route>();\n\n/**\n * Match a URL path against a route pattern.\n * Returns the matched params or null if no match.\n */\nexport function matchRoute(\n url: string,\n routes: Route[],\n): { route: Route; params: Record<string, string | string[]> } | null {\n return matchRouteWithTrie(url, routes, trieCache);\n}\n\n/**\n * Scan the pages/api/ directory and return API routes.\n * Results are cached — call invalidateRouteCache() when files change.\n *\n * Follows Next.js conventions:\n * - pages/api/hello.ts -> /api/hello\n * - pages/api/users/[id].ts -> /api/users/:id\n */\nexport async function apiRouter(\n pagesDir: string,\n pageExtensions?: readonly string[],\n matcher?: ValidFileMatcher,\n): Promise<Route[]> {\n matcher ??= createValidFileMatcher(pageExtensions);\n const cacheKey = `api:${pagesDir}:${JSON.stringify(matcher.extensions)}`;\n const cached = routeCache.get(cacheKey);\n if (cached) return cached.promise;\n\n const promise = scanApiRoutes(pagesDir, matcher);\n routeCache.set(cacheKey, { routes: [], promise });\n const routes = await promise;\n routeCache.set(cacheKey, { routes, promise });\n return routes;\n}\n\nasync function scanApiRoutes(pagesDir: string, matcher: ValidFileMatcher): Promise<Route[]> {\n const apiDir = path.join(pagesDir, \"api\");\n let files: string[];\n try {\n files = [];\n for await (const file of scanWithExtensions(\"**/*\", apiDir, matcher.extensions)) {\n files.push(file);\n }\n } catch {\n files = [];\n }\n\n const routes: Route[] = [];\n\n for (const file of files) {\n // Reuse fileToRoute but pretend the file is under a virtual \"api/\" prefix\n const route = fileToRoute(path.join(\"api\", file), pagesDir, matcher);\n if (route) {\n routes.push(route);\n }\n }\n\n validateRoutePatterns(routes.map((route) => route.pattern));\n\n // Sort same as page routes\n routes.sort(compareRoutes);\n\n return routes;\n}\n\n/**\n * Convert internal route pattern (e.g., \"/posts/:id\", \"/docs/:slug+\")\n * to Next.js bracket format (e.g., \"/posts/[id]\", \"/docs/[...slug]\").\n * Used for __NEXT_DATA__.page which apps expect in Next.js format.\n */\nexport { patternToNextFormat } from \"./route-validation.js\";\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { matchRoute } from "../routing/pages-router.js";
|
|
2
2
|
import "./server-globals.js";
|
|
3
3
|
import { NextRequest } from "../shims/server.js";
|
|
4
|
-
import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
|
|
5
4
|
import { importModule, reportRequestError } from "./instrumentation.js";
|
|
5
|
+
import { mergeRouteParamsIntoQuery, parseQueryString } from "../utils/query.js";
|
|
6
6
|
import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-media-type.js";
|
|
7
7
|
import { isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
8
8
|
import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT, resolveBodyParserConfig } from "./pages-body-parser-config.js";
|
|
@@ -22,21 +22,14 @@ type ServerActionInitiationSnapshot<TRouterState> = {
|
|
|
22
22
|
declare function isServerActionResult<TRoot>(value: unknown): value is AppBrowserServerActionResult<TRoot>;
|
|
23
23
|
declare function shouldClearClientNavigationCachesForServerActionResult<TRoot>(result: AppBrowserServerActionResult<TRoot> | TRoot, revalidation?: ServerActionRevalidationKind): boolean;
|
|
24
24
|
declare function parseServerActionRevalidationHeader(headers: Pick<Headers, "get">): ServerActionRevalidationKind;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
* `./subpage` from `/subdir` lands on `/subdir/subpage`, not `/subpage`.
|
|
34
|
-
*/
|
|
35
|
-
declare function resolveServerActionRedirectLocation(options: {
|
|
36
|
-
currentHref: string;
|
|
37
|
-
location: string;
|
|
38
|
-
origin: string;
|
|
39
|
-
}): ServerActionRedirectLocation;
|
|
25
|
+
declare function normalizeServerActionThrownValue(data: unknown, responseStatus: number): unknown;
|
|
26
|
+
declare function readInvalidServerActionResponseError(response: Pick<Response, "headers" | "status" | "text">, hasRedirectLocation: boolean): Promise<Error | null>;
|
|
27
|
+
declare function shouldCheckRscCompatibilityForServerActionResponse(response: Pick<Response, "headers">): boolean;
|
|
28
|
+
declare function resolveServerActionRedirectCompatibilityHardNavigationTarget(options: {
|
|
29
|
+
actionRedirectHref: string | null;
|
|
30
|
+
clientCompatibilityId: string | null | undefined;
|
|
31
|
+
response: Pick<Response, "headers">;
|
|
32
|
+
}): string | null;
|
|
40
33
|
declare function shouldScheduleRefreshForDiscardedServerAction(revalidation: ServerActionRevalidationKind): boolean;
|
|
41
34
|
declare function createServerActionInitiationSnapshot<TRouterState>(options: {
|
|
42
35
|
href: string;
|
|
@@ -55,5 +48,5 @@ type DiscardedServerActionRefreshSchedulerOptions = {
|
|
|
55
48
|
};
|
|
56
49
|
declare function createDiscardedServerActionRefreshScheduler(options: DiscardedServerActionRefreshSchedulerOptions): DiscardedServerActionRefreshScheduler;
|
|
57
50
|
//#endregion
|
|
58
|
-
export { AppBrowserServerActionResult, ServerActionRevalidationKind, createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, isServerActionResult, parseServerActionRevalidationHeader,
|
|
51
|
+
export { AppBrowserServerActionResult, ServerActionRevalidationKind, createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, resolveServerActionRedirectCompatibilityHardNavigationTarget, shouldCheckRscCompatibilityForServerActionResponse, shouldClearClientNavigationCachesForServerActionResult, shouldScheduleRefreshForDiscardedServerAction };
|
|
59
52
|
//# sourceMappingURL=app-browser-action-result.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ACTION_REVALIDATED_HEADER } from "./headers.js";
|
|
2
|
+
import { VINEXT_RSC_CONTENT_TYPE, isRscCompatibilityIdCompatible } from "./app-rsc-cache-busting.js";
|
|
2
3
|
//#region src/server/app-browser-action-result.ts
|
|
3
4
|
const ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC = 1;
|
|
4
5
|
const ACTION_DID_REVALIDATE_DYNAMIC_ONLY = 2;
|
|
@@ -31,19 +32,29 @@ function parseServerActionRevalidationHeader(headers) {
|
|
|
31
32
|
default: return "none";
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
function
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
35
|
+
function createServerActionHttpFallbackError(status) {
|
|
36
|
+
if (status < 400 || status > 599) return null;
|
|
37
|
+
const digest = status === 404 ? "NEXT_HTTP_ERROR_FALLBACK;404" : `NEXT_HTTP_ERROR_FALLBACK;${status}`;
|
|
38
|
+
const error = /* @__PURE__ */ new Error(status === 404 ? "NEXT_NOT_FOUND" : `NEXT_HTTP_ERROR_FALLBACK;${status}`);
|
|
39
|
+
return Object.assign(error, { digest });
|
|
40
|
+
}
|
|
41
|
+
function normalizeServerActionThrownValue(data, responseStatus) {
|
|
42
|
+
return createServerActionHttpFallbackError(responseStatus) ?? data;
|
|
43
|
+
}
|
|
44
|
+
async function readInvalidServerActionResponseError(response, hasRedirectLocation) {
|
|
45
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
46
|
+
if (contentType.startsWith("text/x-component") || hasRedirectLocation) return null;
|
|
47
|
+
const message = response.status >= 400 && contentType.toLowerCase().startsWith("text/plain") ? await response.text() : "An unexpected response was received from the server.";
|
|
48
|
+
return new Error(message || "An unexpected response was received from the server.");
|
|
49
|
+
}
|
|
50
|
+
function shouldCheckRscCompatibilityForServerActionResponse(response) {
|
|
51
|
+
return (response.headers.get("content-type") ?? "").startsWith(VINEXT_RSC_CONTENT_TYPE);
|
|
52
|
+
}
|
|
53
|
+
function resolveServerActionRedirectCompatibilityHardNavigationTarget(options) {
|
|
54
|
+
if (!options.actionRedirectHref) return null;
|
|
55
|
+
if (!shouldCheckRscCompatibilityForServerActionResponse(options.response)) return null;
|
|
56
|
+
if (isRscCompatibilityIdCompatible(options.response.headers.get("X-Vinext-RSC-Compatibility-Id"), options.clientCompatibilityId)) return null;
|
|
57
|
+
return options.actionRedirectHref;
|
|
47
58
|
}
|
|
48
59
|
function shouldScheduleRefreshForDiscardedServerAction(revalidation) {
|
|
49
60
|
return revalidation !== "none";
|
|
@@ -88,6 +99,6 @@ function createDiscardedServerActionRefreshScheduler(options) {
|
|
|
88
99
|
};
|
|
89
100
|
}
|
|
90
101
|
//#endregion
|
|
91
|
-
export { createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, isServerActionResult, parseServerActionRevalidationHeader,
|
|
102
|
+
export { createDiscardedServerActionRefreshScheduler, createServerActionInitiationSnapshot, isServerActionResult, normalizeServerActionThrownValue, parseServerActionRevalidationHeader, readInvalidServerActionResponseError, resolveServerActionRedirectCompatibilityHardNavigationTarget, shouldCheckRscCompatibilityForServerActionResponse, shouldClearClientNavigationCachesForServerActionResult, shouldScheduleRefreshForDiscardedServerAction };
|
|
92
103
|
|
|
93
104
|
//# sourceMappingURL=app-browser-action-result.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-browser-action-result.js","names":[],"sources":["../../src/server/app-browser-action-result.ts"],"sourcesContent":["import { ACTION_REVALIDATED_HEADER } from \"./headers.js\";\n\nexport type AppBrowserServerActionResult<TRoot> = {\n root?: TRoot;\n returnValue?: {\n ok: boolean;\n data: unknown;\n };\n};\n\nexport type ServerActionRevalidationKind = \"dynamicOnly\" | \"none\" | \"staticAndDynamic\";\n\nconst ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC = 1;\nconst ACTION_DID_REVALIDATE_DYNAMIC_ONLY = 2;\n\ntype ServerActionInitiationSnapshot<TRouterState> = {\n href: string;\n navigationId: number;\n path: string;\n routerState: TRouterState;\n};\n\n/**\n * Structural discriminator: matches on `\"returnValue\"` or `\"root\"` keys.\n * This is safe because {@link AppWireElements} keys are prefixed (`route:`,\n * `slot:`, `__route`, etc.) and will never collide with these property names.\n * If the wire format ever adds a `\"root\"` key, this guard must be updated.\n */\nexport function isServerActionResult<TRoot>(\n value: unknown,\n): value is AppBrowserServerActionResult<TRoot> {\n return !!value && typeof value === \"object\" && (\"returnValue\" in value || \"root\" in value);\n}\n\nexport function shouldClearClientNavigationCachesForServerActionResult<TRoot>(\n result: AppBrowserServerActionResult<TRoot> | TRoot,\n revalidation: ServerActionRevalidationKind = \"none\",\n): boolean {\n if (revalidation !== \"none\") {\n return true;\n }\n\n if (!isServerActionResult<TRoot>(result)) {\n return true;\n }\n\n return result.root !== undefined;\n}\n\nexport function parseServerActionRevalidationHeader(\n headers: Pick<Headers, \"get\">,\n): ServerActionRevalidationKind {\n const value = headers.get(ACTION_REVALIDATED_HEADER);\n if (!value) return \"none\";\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(value);\n } catch {\n return \"none\";\n }\n\n switch (parsed) {\n case ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC:\n return \"staticAndDynamic\";\n case ACTION_DID_REVALIDATE_DYNAMIC_ONLY:\n return \"dynamicOnly\";\n default:\n return \"none\";\n }\n}\n\
|
|
1
|
+
{"version":3,"file":"app-browser-action-result.js","names":[],"sources":["../../src/server/app-browser-action-result.ts"],"sourcesContent":["import { ACTION_REVALIDATED_HEADER } from \"./headers.js\";\nimport {\n isRscCompatibilityIdCompatible,\n VINEXT_RSC_COMPATIBILITY_ID_HEADER,\n VINEXT_RSC_CONTENT_TYPE,\n} from \"./app-rsc-cache-busting.js\";\n\nexport type AppBrowserServerActionResult<TRoot> = {\n root?: TRoot;\n returnValue?: {\n ok: boolean;\n data: unknown;\n };\n};\n\nexport type ServerActionRevalidationKind = \"dynamicOnly\" | \"none\" | \"staticAndDynamic\";\n\nconst ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC = 1;\nconst ACTION_DID_REVALIDATE_DYNAMIC_ONLY = 2;\n\ntype ServerActionInitiationSnapshot<TRouterState> = {\n href: string;\n navigationId: number;\n path: string;\n routerState: TRouterState;\n};\n\n/**\n * Structural discriminator: matches on `\"returnValue\"` or `\"root\"` keys.\n * This is safe because {@link AppWireElements} keys are prefixed (`route:`,\n * `slot:`, `__route`, etc.) and will never collide with these property names.\n * If the wire format ever adds a `\"root\"` key, this guard must be updated.\n */\nexport function isServerActionResult<TRoot>(\n value: unknown,\n): value is AppBrowserServerActionResult<TRoot> {\n return !!value && typeof value === \"object\" && (\"returnValue\" in value || \"root\" in value);\n}\n\nexport function shouldClearClientNavigationCachesForServerActionResult<TRoot>(\n result: AppBrowserServerActionResult<TRoot> | TRoot,\n revalidation: ServerActionRevalidationKind = \"none\",\n): boolean {\n if (revalidation !== \"none\") {\n return true;\n }\n\n if (!isServerActionResult<TRoot>(result)) {\n return true;\n }\n\n return result.root !== undefined;\n}\n\nexport function parseServerActionRevalidationHeader(\n headers: Pick<Headers, \"get\">,\n): ServerActionRevalidationKind {\n const value = headers.get(ACTION_REVALIDATED_HEADER);\n if (!value) return \"none\";\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(value);\n } catch {\n return \"none\";\n }\n\n switch (parsed) {\n case ACTION_DID_REVALIDATE_STATIC_AND_DYNAMIC:\n return \"staticAndDynamic\";\n case ACTION_DID_REVALIDATE_DYNAMIC_ONLY:\n return \"dynamicOnly\";\n default:\n return \"none\";\n }\n}\n\nfunction createServerActionHttpFallbackError(status: number): (Error & { digest: string }) | null {\n if (status < 400 || status > 599) return null;\n\n const digest =\n status === 404 ? \"NEXT_HTTP_ERROR_FALLBACK;404\" : `NEXT_HTTP_ERROR_FALLBACK;${status}`;\n const error = new Error(status === 404 ? \"NEXT_NOT_FOUND\" : `NEXT_HTTP_ERROR_FALLBACK;${status}`);\n return Object.assign(error, { digest });\n}\n\nexport function normalizeServerActionThrownValue(data: unknown, responseStatus: number): unknown {\n return createServerActionHttpFallbackError(responseStatus) ?? data;\n}\n\nexport async function readInvalidServerActionResponseError(\n response: Pick<Response, \"headers\" | \"status\" | \"text\">,\n hasRedirectLocation: boolean,\n): Promise<Error | null> {\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n const isRscResponse = contentType.startsWith(VINEXT_RSC_CONTENT_TYPE);\n if (isRscResponse || hasRedirectLocation) return null;\n\n // Parity with Next.js' server-action reducer: any non-RSC action response,\n // including a 2xx, is surfaced to the action caller. Plain text 4xx/5xx\n // bodies are preserved when available; other responses use a stable generic\n // message.\n const message =\n response.status >= 400 && contentType.toLowerCase().startsWith(\"text/plain\")\n ? await response.text()\n : \"An unexpected response was received from the server.\";\n\n return new Error(message || \"An unexpected response was received from the server.\");\n}\n\nexport function shouldCheckRscCompatibilityForServerActionResponse(\n response: Pick<Response, \"headers\">,\n): boolean {\n return (response.headers.get(\"content-type\") ?? \"\").startsWith(VINEXT_RSC_CONTENT_TYPE);\n}\n\nexport function resolveServerActionRedirectCompatibilityHardNavigationTarget(options: {\n actionRedirectHref: string | null;\n clientCompatibilityId: string | null | undefined;\n response: Pick<Response, \"headers\">;\n}): string | null {\n if (!options.actionRedirectHref) return null;\n if (!shouldCheckRscCompatibilityForServerActionResponse(options.response)) return null;\n if (\n isRscCompatibilityIdCompatible(\n options.response.headers.get(VINEXT_RSC_COMPATIBILITY_ID_HEADER),\n options.clientCompatibilityId,\n )\n ) {\n return null;\n }\n return options.actionRedirectHref;\n}\n\nexport function shouldScheduleRefreshForDiscardedServerAction(\n revalidation: ServerActionRevalidationKind,\n): boolean {\n return revalidation !== \"none\";\n}\n\nexport function createServerActionInitiationSnapshot<TRouterState>(options: {\n href: string;\n navigationId: number;\n origin?: string;\n routerState: TRouterState;\n}): ServerActionInitiationSnapshot<TRouterState> {\n const url =\n options.origin === undefined ? new URL(options.href) : new URL(options.href, options.origin);\n return {\n href: url.href,\n navigationId: options.navigationId,\n path: url.pathname + url.search,\n routerState: options.routerState,\n };\n}\n\ntype DiscardedServerActionRefreshScheduler = {\n markNavigationSettled(): void;\n markNavigationStart(): void;\n schedule(): void;\n};\n\ntype DiscardedServerActionRefreshSchedulerOptions = {\n queueTask?: (callback: () => void) => void;\n runRefresh: () => void;\n};\n\nexport function createDiscardedServerActionRefreshScheduler(\n options: DiscardedServerActionRefreshSchedulerOptions,\n): DiscardedServerActionRefreshScheduler {\n const queueTask = options.queueTask ?? queueMicrotask;\n let activeNavigationCount = 0;\n let flushQueued = false;\n let refreshPending = false;\n\n function flush(): void {\n flushQueued = false;\n if (!refreshPending || activeNavigationCount > 0) return;\n\n refreshPending = false;\n options.runRefresh();\n }\n\n function queueFlush(): void {\n if (flushQueued) return;\n flushQueued = true;\n queueTask(flush);\n }\n\n return {\n markNavigationSettled() {\n if (activeNavigationCount > 0) {\n activeNavigationCount -= 1;\n }\n queueFlush();\n },\n markNavigationStart() {\n activeNavigationCount += 1;\n },\n schedule() {\n refreshPending = true;\n queueFlush();\n },\n };\n}\n"],"mappings":";;;AAiBA,MAAM,2CAA2C;AACjD,MAAM,qCAAqC;;;;;;;AAe3C,SAAgB,qBACd,OAC8C;CAC9C,OAAO,CAAC,CAAC,SAAS,OAAO,UAAU,aAAa,iBAAiB,SAAS,UAAU;;AAGtF,SAAgB,uDACd,QACA,eAA6C,QACpC;CACT,IAAI,iBAAiB,QACnB,OAAO;CAGT,IAAI,CAAC,qBAA4B,OAAO,EACtC,OAAO;CAGT,OAAO,OAAO,SAAS,KAAA;;AAGzB,SAAgB,oCACd,SAC8B;CAC9B,MAAM,QAAQ,QAAQ,IAAI,0BAA0B;CACpD,IAAI,CAAC,OAAO,OAAO;CAEnB,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,MAAM;SACpB;EACN,OAAO;;CAGT,QAAQ,QAAR;EACE,KAAK,0CACH,OAAO;EACT,KAAK,oCACH,OAAO;EACT,SACE,OAAO;;;AAIb,SAAS,oCAAoC,QAAqD;CAChG,IAAI,SAAS,OAAO,SAAS,KAAK,OAAO;CAEzC,MAAM,SACJ,WAAW,MAAM,iCAAiC,4BAA4B;CAChF,MAAM,wBAAQ,IAAI,MAAM,WAAW,MAAM,mBAAmB,4BAA4B,SAAS;CACjG,OAAO,OAAO,OAAO,OAAO,EAAE,QAAQ,CAAC;;AAGzC,SAAgB,iCAAiC,MAAe,gBAAiC;CAC/F,OAAO,oCAAoC,eAAe,IAAI;;AAGhE,eAAsB,qCACpB,UACA,qBACuB;CACvB,MAAM,cAAc,SAAS,QAAQ,IAAI,eAAe,IAAI;CAE5D,IADsB,YAAY,WAAA,mBACjB,IAAI,qBAAqB,OAAO;CAMjD,MAAM,UACJ,SAAS,UAAU,OAAO,YAAY,aAAa,CAAC,WAAW,aAAa,GACxE,MAAM,SAAS,MAAM,GACrB;CAEN,OAAO,IAAI,MAAM,WAAW,uDAAuD;;AAGrF,SAAgB,mDACd,UACS;CACT,QAAQ,SAAS,QAAQ,IAAI,eAAe,IAAI,IAAI,WAAW,wBAAwB;;AAGzF,SAAgB,6DAA6D,SAI3D;CAChB,IAAI,CAAC,QAAQ,oBAAoB,OAAO;CACxC,IAAI,CAAC,mDAAmD,QAAQ,SAAS,EAAE,OAAO;CAClF,IACE,+BACE,QAAQ,SAAS,QAAQ,IAAA,gCAAuC,EAChE,QAAQ,sBACT,EAED,OAAO;CAET,OAAO,QAAQ;;AAGjB,SAAgB,8CACd,cACS;CACT,OAAO,iBAAiB;;AAG1B,SAAgB,qCAAmD,SAKlB;CAC/C,MAAM,MACJ,QAAQ,WAAW,KAAA,IAAY,IAAI,IAAI,QAAQ,KAAK,GAAG,IAAI,IAAI,QAAQ,MAAM,QAAQ,OAAO;CAC9F,OAAO;EACL,MAAM,IAAI;EACV,cAAc,QAAQ;EACtB,MAAM,IAAI,WAAW,IAAI;EACzB,aAAa,QAAQ;EACtB;;AAcH,SAAgB,4CACd,SACuC;CACvC,MAAM,YAAY,QAAQ,aAAa;CACvC,IAAI,wBAAwB;CAC5B,IAAI,cAAc;CAClB,IAAI,iBAAiB;CAErB,SAAS,QAAc;EACrB,cAAc;EACd,IAAI,CAAC,kBAAkB,wBAAwB,GAAG;EAElD,iBAAiB;EACjB,QAAQ,YAAY;;CAGtB,SAAS,aAAmB;EAC1B,IAAI,aAAa;EACjB,cAAc;EACd,UAAU,MAAM;;CAGlB,OAAO;EACL,wBAAwB;GACtB,IAAI,wBAAwB,GAC1B,yBAAyB;GAE3B,YAAY;;EAEd,sBAAsB;GACpB,yBAAyB;;EAE3B,WAAW;GACT,iBAAiB;GACjB,YAAY;;EAEf"}
|