@wular/pnext 0.0.1

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.
Files changed (371) hide show
  1. package/README.md +153 -0
  2. package/bin/pnext +67 -0
  3. package/config/lint/base.js +48 -0
  4. package/config/ts/base.json +26 -0
  5. package/config/ts/react.json +13 -0
  6. package/package.json +70 -0
  7. package/reference/compat.md +63 -0
  8. package/reference/config.md +120 -0
  9. package/reference/css.md +58 -0
  10. package/reference/dev.md +69 -0
  11. package/reference/env.md +40 -0
  12. package/reference/metadata.md +86 -0
  13. package/reference/navigation.md +149 -0
  14. package/reference/overview.md +35 -0
  15. package/reference/performance.md +97 -0
  16. package/reference/rendering.md +127 -0
  17. package/reference/routing.md +167 -0
  18. package/reference/typegen.md +64 -0
  19. package/src/api/cache.ts +80 -0
  20. package/src/api/client-cache.ts +9 -0
  21. package/src/api/client-navigation.ts +279 -0
  22. package/src/api/dynamic.tsx +102 -0
  23. package/src/api/link.tsx +119 -0
  24. package/src/api/navigation.ts +198 -0
  25. package/src/api/router/events.ts +53 -0
  26. package/src/api/router/history.ts +70 -0
  27. package/src/api/router/hub.ts +194 -0
  28. package/src/api/router/policies.ts +107 -0
  29. package/src/api/router/runtime.ts +5238 -0
  30. package/src/api/router/types.ts +299 -0
  31. package/src/api/router.ts +167 -0
  32. package/src/api/server.ts +323 -0
  33. package/src/api/suspense.ts +16 -0
  34. package/src/cache/context.ts +61 -0
  35. package/src/cli/adapters/vercel-warm.ts +375 -0
  36. package/src/cli/adapters/vercel.ts +1310 -0
  37. package/src/cli/analyze-print.ts +181 -0
  38. package/src/cli/analyze.ts +328 -0
  39. package/src/cli/boot-trace.ts +29 -0
  40. package/src/cli/build.ts +3114 -0
  41. package/src/cli/dev.ts +276 -0
  42. package/src/cli/index.ts +196 -0
  43. package/src/cli/named-bin.ts +119 -0
  44. package/src/cli/serve-ui.ts +160 -0
  45. package/src/cli/start.ts +1425 -0
  46. package/src/client/build.ts +2136 -0
  47. package/src/client/chunk-fold.ts +526 -0
  48. package/src/client/entry.ts +1525 -0
  49. package/src/client/paths.ts +22 -0
  50. package/src/client/prebuilt.ts +621 -0
  51. package/src/client/profile.ts +75 -0
  52. package/src/client/react-compiler.ts +94 -0
  53. package/src/client/reference-stub.ts +145 -0
  54. package/src/client/reference.ts +47 -0
  55. package/src/compat/actions/action-client.ts +531 -0
  56. package/src/compat/actions/action-dispatch.ts +676 -0
  57. package/src/compat/actions/action-router.ts +40 -0
  58. package/src/compat/actions/action-shared.ts +95 -0
  59. package/src/compat/actions/client-plugin.ts +135 -0
  60. package/src/compat/actions/client-stub.ts +65 -0
  61. package/src/compat/actions/config.ts +164 -0
  62. package/src/compat/actions/detect.ts +208 -0
  63. package/src/compat/actions/discovery.ts +244 -0
  64. package/src/compat/actions/early-submit.ts +40 -0
  65. package/src/compat/actions/endpoint.ts +602 -0
  66. package/src/compat/actions/flight.ts +52 -0
  67. package/src/compat/actions/form-state.ts +73 -0
  68. package/src/compat/actions/hoist.ts +485 -0
  69. package/src/compat/actions/ids.ts +39 -0
  70. package/src/compat/actions/index.ts +41 -0
  71. package/src/compat/actions/instances.ts +144 -0
  72. package/src/compat/actions/origin.ts +109 -0
  73. package/src/compat/actions/protocol.ts +125 -0
  74. package/src/compat/actions/registry.ts +74 -0
  75. package/src/compat/actions/rewrite.ts +282 -0
  76. package/src/compat/actions/serve.ts +414 -0
  77. package/src/compat/actions/server-tag.ts +21 -0
  78. package/src/compat/actions/unrecognized-error.ts +30 -0
  79. package/src/compat/adapter/build-complete.ts +257 -0
  80. package/src/compat/bundler/bun-externals.ts +53 -0
  81. package/src/compat/bundler/cjs-exports.ts +542 -0
  82. package/src/compat/bundler/config.ts +363 -0
  83. package/src/compat/bundler/externals.ts +34 -0
  84. package/src/compat/bundler/import-meta-url.ts +60 -0
  85. package/src/compat/bundler/modularize-imports.ts +119 -0
  86. package/src/compat/bundler/new-url-asset.ts +87 -0
  87. package/src/compat/bundler/optimize-package-imports.ts +273 -0
  88. package/src/compat/bundler/polyfill.ts +88 -0
  89. package/src/compat/bundler/react-compiler.ts +61 -0
  90. package/src/compat/bundler/react-profiler.tsx +25 -0
  91. package/src/compat/bundler/relay-transform.ts +116 -0
  92. package/src/compat/bundler/require-context.ts +281 -0
  93. package/src/compat/bundler/resolve-extensions.ts +75 -0
  94. package/src/compat/bundler/source-cache.ts +61 -0
  95. package/src/compat/bundler/static-imports.ts +25 -0
  96. package/src/compat/bundler/symlink-imports.ts +119 -0
  97. package/src/compat/bundler/tsconfig-paths.ts +50 -0
  98. package/src/compat/bundler/wasm.ts +153 -0
  99. package/src/compat/bundler/webpack-loaders.ts +685 -0
  100. package/src/compat/bundler/worker.ts +278 -0
  101. package/src/compat/cache/build-flags.ts +81 -0
  102. package/src/compat/cache/build-prerender-errors.ts +163 -0
  103. package/src/compat/cache/custom-handler.ts +159 -0
  104. package/src/compat/cache/fetch-patch.ts +745 -0
  105. package/src/compat/cache/handler.ts +100 -0
  106. package/src/compat/cache/modern-handler.ts +275 -0
  107. package/src/compat/cache/resume-data-cache.ts +143 -0
  108. package/src/compat/cache/revalidate.ts +759 -0
  109. package/src/compat/cache/runtime-error.ts +124 -0
  110. package/src/compat/cache/use-cache-transform.ts +961 -0
  111. package/src/compat/cache/use-cache.ts +1695 -0
  112. package/src/compat/cache-control.ts +269 -0
  113. package/src/compat/client/base-path.ts +64 -0
  114. package/src/compat/client/css-order.ts +36 -0
  115. package/src/compat/client/errors/control-flow.ts +92 -0
  116. package/src/compat/client/errors/error-boundary.ts +222 -0
  117. package/src/compat/client/errors/global-error.ts +238 -0
  118. package/src/compat/client/errors/install.ts +217 -0
  119. package/src/compat/client/errors/lazy.ts +53 -0
  120. package/src/compat/client/errors/primitive-throw.ts +126 -0
  121. package/src/compat/client/errors/soft-refresh.ts +14 -0
  122. package/src/compat/client/link-status.ts +86 -0
  123. package/src/compat/client/nav-compat-runtime.ts +57 -0
  124. package/src/compat/client/nav-compat.ts +42 -0
  125. package/src/compat/client/navigation-scroll.ts +154 -0
  126. package/src/compat/client/optimistic-routing.ts +206 -0
  127. package/src/compat/client/prefetch-cache.ts +111 -0
  128. package/src/compat/client/route-announcer.ts +72 -0
  129. package/src/compat/client/segment-cache-policy.ts +159 -0
  130. package/src/compat/client/segment-cache.ts +1077 -0
  131. package/src/compat/client/segment-prefetch.ts +375 -0
  132. package/src/compat/client/trailing-slash.ts +24 -0
  133. package/src/compat/css/chunking.ts +254 -0
  134. package/src/compat/css/inline-css.ts +73 -0
  135. package/src/compat/css/lightningcss.ts +90 -0
  136. package/src/compat/css/modules.ts +373 -0
  137. package/src/compat/css/nonce.ts +30 -0
  138. package/src/compat/css/sass-plugin.ts +65 -0
  139. package/src/compat/css/sass.ts +392 -0
  140. package/src/compat/css/styled-jsx-runtime.ts +80 -0
  141. package/src/compat/css/styled-jsx.ts +49 -0
  142. package/src/compat/edge-runtime.ts +71 -0
  143. package/src/compat/export/client.ts +112 -0
  144. package/src/compat/export/index.ts +272 -0
  145. package/src/compat/export/standalone.ts +207 -0
  146. package/src/compat/image-optimizer/cache.ts +119 -0
  147. package/src/compat/image-optimizer/detect.ts +143 -0
  148. package/src/compat/image-optimizer/index.ts +601 -0
  149. package/src/compat/image-optimizer/source.ts +243 -0
  150. package/src/compat/index.ts +458 -0
  151. package/src/compat/lifecycle/after-scope.ts +86 -0
  152. package/src/compat/lifecycle/after.ts +173 -0
  153. package/src/compat/lifecycle/error-funnel.ts +306 -0
  154. package/src/compat/lifecycle/error-serialize.ts +87 -0
  155. package/src/compat/lifecycle/error-ui.ts +167 -0
  156. package/src/compat/lifecycle/instrumentation-client.ts +138 -0
  157. package/src/compat/lifecycle/instrumentation.ts +277 -0
  158. package/src/compat/lifecycle/node-console.ts +19 -0
  159. package/src/compat/lifecycle/testmode.ts +263 -0
  160. package/src/compat/mdx/compile.ts +219 -0
  161. package/src/compat/mdx/next-mdx-stub.ts +46 -0
  162. package/src/compat/mdx/plugin.ts +37 -0
  163. package/src/compat/metadata-route-artifacts.ts +458 -0
  164. package/src/compat/metadata.ts +295 -0
  165. package/src/compat/middleware/manifest.ts +210 -0
  166. package/src/compat/misc/action-return.ts +173 -0
  167. package/src/compat/next/cache.ts +211 -0
  168. package/src/compat/next/canonical-url.ts +35 -0
  169. package/src/compat/next/client-cache.ts +57 -0
  170. package/src/compat/next/client-navigation.ts +313 -0
  171. package/src/compat/next/client-only.ts +3 -0
  172. package/src/compat/next/client-script.tsx +215 -0
  173. package/src/compat/next/client-server.ts +39 -0
  174. package/src/compat/next/config-loader.ts +569 -0
  175. package/src/compat/next/config.ts +29 -0
  176. package/src/compat/next/constants.cjs +6 -0
  177. package/src/compat/next/constants.ts +6 -0
  178. package/src/compat/next/custom-server.ts +236 -0
  179. package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
  180. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
  181. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
  182. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
  183. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
  184. package/src/compat/next/dynamic.tsx +46 -0
  185. package/src/compat/next/error.tsx +148 -0
  186. package/src/compat/next/font/cache.ts +171 -0
  187. package/src/compat/next/font/google.ts +2 -0
  188. package/src/compat/next/font/index.ts +8 -0
  189. package/src/compat/next/font/local.ts +5 -0
  190. package/src/compat/next/font/runtime-client.ts +71 -0
  191. package/src/compat/next/font/runtime.ts +974 -0
  192. package/src/compat/next/font/shared.ts +281 -0
  193. package/src/compat/next/form.tsx +156 -0
  194. package/src/compat/next/head.tsx +10 -0
  195. package/src/compat/next/headers.ts +247 -0
  196. package/src/compat/next/image/config.ts +196 -0
  197. package/src/compat/next/image/optimizer.ts +96 -0
  198. package/src/compat/next/image/patterns.ts +103 -0
  199. package/src/compat/next/image/shared.ts +141 -0
  200. package/src/compat/next/image/static-metadata.ts +283 -0
  201. package/src/compat/next/image/validate.ts +269 -0
  202. package/src/compat/next/image-client.tsx +215 -0
  203. package/src/compat/next/image-props.ts +575 -0
  204. package/src/compat/next/image-usage.ts +102 -0
  205. package/src/compat/next/image.tsx +56 -0
  206. package/src/compat/next/index.ts +1 -0
  207. package/src/compat/next/legacy-image.tsx +97 -0
  208. package/src/compat/next/link-usage.ts +29 -0
  209. package/src/compat/next/link-validation-transform.ts +200 -0
  210. package/src/compat/next/link.tsx +466 -0
  211. package/src/compat/next/navigation.cjs +21 -0
  212. package/src/compat/next/navigation.ts +188 -0
  213. package/src/compat/next/offline.ts +51 -0
  214. package/src/compat/next/og.ts +324 -0
  215. package/src/compat/next/optimistic-route-state.ts +188 -0
  216. package/src/compat/next/preferred-region.ts +39 -0
  217. package/src/compat/next/redirects.ts +131 -0
  218. package/src/compat/next/resource-hints.ts +136 -0
  219. package/src/compat/next/rewrites.ts +350 -0
  220. package/src/compat/next/root-params.ts +142 -0
  221. package/src/compat/next/router.cjs +49 -0
  222. package/src/compat/next/router.ts +143 -0
  223. package/src/compat/next/script.tsx +355 -0
  224. package/src/compat/next/server-only.ts +3 -0
  225. package/src/compat/next/server.ts +28 -0
  226. package/src/compat/next/svgr.ts +58 -0
  227. package/src/compat/next/telemetry.ts +77 -0
  228. package/src/compat/next/user-agent.ts +100 -0
  229. package/src/compat/next/web-vitals.ts +56 -0
  230. package/src/compat/otel/api.ts +95 -0
  231. package/src/compat/otel/client-trace-metadata.ts +71 -0
  232. package/src/compat/otel/fetch-span.ts +77 -0
  233. package/src/compat/otel/tracer.ts +944 -0
  234. package/src/compat/pages/client-plugin.ts +108 -0
  235. package/src/compat/pages/index.ts +527 -0
  236. package/src/compat/pages/router-state.ts +94 -0
  237. package/src/compat/ppr/io.ts +38 -0
  238. package/src/compat/ppr/missing-root-params.ts +105 -0
  239. package/src/compat/ppr/root-params-scan.ts +164 -0
  240. package/src/compat/ppr/root-params-transform.ts +75 -0
  241. package/src/compat/ppr/root-params.ts +129 -0
  242. package/src/compat/ppr/segment-config-incompat.ts +34 -0
  243. package/src/compat/protocol.ts +202 -0
  244. package/src/compat/react/client.ts +59 -0
  245. package/src/compat/react/compiler-runtime.ts +60 -0
  246. package/src/compat/react/dom-client.ts +115 -0
  247. package/src/compat/react/dom-react-server.ts +20 -0
  248. package/src/compat/react/dom-server.ts +40 -0
  249. package/src/compat/react/dom.ts +154 -0
  250. package/src/compat/react/preact.ts +522 -0
  251. package/src/compat/react/react-server.ts +84 -0
  252. package/src/compat/react/router-shim.ts +26 -0
  253. package/src/compat/react/server-component-use.ts +48 -0
  254. package/src/compat/react/server-inserted-html.ts +87 -0
  255. package/src/compat/react/server.ts +156 -0
  256. package/src/compat/react/view-transition.ts +60 -0
  257. package/src/compat/register/actions.ts +875 -0
  258. package/src/compat/register/boot.ts +141 -0
  259. package/src/compat/register/build-tier.ts +11 -0
  260. package/src/compat/register/build.ts +182 -0
  261. package/src/compat/register/bundler.ts +587 -0
  262. package/src/compat/register/cache.ts +85 -0
  263. package/src/compat/register/client-errors.ts +18 -0
  264. package/src/compat/register/config.ts +17 -0
  265. package/src/compat/register/css-extras.ts +120 -0
  266. package/src/compat/register/edge-runtime.ts +6 -0
  267. package/src/compat/register/errors.ts +46 -0
  268. package/src/compat/register/export.ts +23 -0
  269. package/src/compat/register/font.ts +36 -0
  270. package/src/compat/register/hooks.ts +34 -0
  271. package/src/compat/register/image.ts +133 -0
  272. package/src/compat/register/index.ts +111 -0
  273. package/src/compat/register/instrumentation-client.ts +35 -0
  274. package/src/compat/register/lifecycle.ts +86 -0
  275. package/src/compat/register/mdx.ts +48 -0
  276. package/src/compat/register/middleware.ts +36 -0
  277. package/src/compat/register/misc.ts +44 -0
  278. package/src/compat/register/otel.ts +288 -0
  279. package/src/compat/register/pages-api.ts +473 -0
  280. package/src/compat/register/ppr.ts +56 -0
  281. package/src/compat/register/protocol.ts +57 -0
  282. package/src/compat/register/proxy.ts +127 -0
  283. package/src/compat/register/render.ts +268 -0
  284. package/src/compat/register/routing.ts +410 -0
  285. package/src/compat/register/segment.ts +1903 -0
  286. package/src/compat/register/static-image.ts +21 -0
  287. package/src/compat/register/typed-routes.ts +35 -0
  288. package/src/compat/register/usecache.ts +131 -0
  289. package/src/compat/register/validation.ts +56 -0
  290. package/src/compat/segment/loading-boundary.ts +113 -0
  291. package/src/compat/segment/page-slot.ts +200 -0
  292. package/src/compat/segment/tree.ts +481 -0
  293. package/src/compat/segment/vary-key.ts +102 -0
  294. package/src/compat/segment/vary-params.ts +551 -0
  295. package/src/compat/static-params.ts +33 -0
  296. package/src/compat/tsconfig-defaults.ts +301 -0
  297. package/src/compat/typecheck/index.ts +1481 -0
  298. package/src/compat/typecheck/worker.ts +26 -0
  299. package/src/compat/typed-routes/index.ts +92 -0
  300. package/src/compat/typed-routes/manifest.ts +356 -0
  301. package/src/compat/typed-routes/typegen.ts +566 -0
  302. package/src/compat/validation/errors.ts +159 -0
  303. package/src/compat/validation/index.ts +1770 -0
  304. package/src/compat/validation/prerender-diagnostics.ts +1508 -0
  305. package/src/compat-bootstrap.ts +67 -0
  306. package/src/config.ts +218 -0
  307. package/src/css/build.ts +697 -0
  308. package/src/css/index.ts +2 -0
  309. package/src/css/postcss.ts +236 -0
  310. package/src/css/worker.ts +34 -0
  311. package/src/dev/client-actions.ts +35 -0
  312. package/src/dev/client-chunk-store.ts +92 -0
  313. package/src/dev/client-key-cache.ts +178 -0
  314. package/src/dev/global-css-cache.ts +212 -0
  315. package/src/dev/imports.ts +2430 -0
  316. package/src/dev/module-cache.ts +721 -0
  317. package/src/dev/module-generations.ts +38 -0
  318. package/src/dev/module-transform.ts +188 -0
  319. package/src/dev/node-module-bundle-cache.ts +63 -0
  320. package/src/dev/restart-cache.ts +10 -0
  321. package/src/dev/route-bundle-key-cache.ts +154 -0
  322. package/src/dev/route-facts-cache.ts +223 -0
  323. package/src/dev/server.ts +1710 -0
  324. package/src/dynamic/source.ts +307 -0
  325. package/src/dynamic/tree-shake.ts +262 -0
  326. package/src/env.ts +92 -0
  327. package/src/extensions.ts +1898 -0
  328. package/src/index.ts +34 -0
  329. package/src/internal.ts +43 -0
  330. package/src/islands/boundary-error.ts +8 -0
  331. package/src/islands/static-children.ts +37 -0
  332. package/src/islands/static-slots.ts +106 -0
  333. package/src/ppr-postpone.ts +24 -0
  334. package/src/ppr.ts +784 -0
  335. package/src/proxy.ts +752 -0
  336. package/src/render/hooks.ts +384 -0
  337. package/src/render/index.ts +1 -0
  338. package/src/render/island-context.ts +47 -0
  339. package/src/render/metadata.ts +857 -0
  340. package/src/render/renderer.ts +7391 -0
  341. package/src/render/resource-hints.ts +44 -0
  342. package/src/render/slots.tsx +679 -0
  343. package/src/request/context.ts +396 -0
  344. package/src/resolve/engine.ts +219 -0
  345. package/src/resolve/imports.ts +1104 -0
  346. package/src/resolve/scan-facts.ts +474 -0
  347. package/src/resolve/source-text.ts +86 -0
  348. package/src/routing/forwarded.ts +41 -0
  349. package/src/routing/handler.ts +271 -0
  350. package/src/routing/href.ts +203 -0
  351. package/src/routing/metadata.ts +1018 -0
  352. package/src/routing/request-runtime.ts +43 -0
  353. package/src/routing/routes.ts +2560 -0
  354. package/src/routing/slots.ts +432 -0
  355. package/src/runtime/server.ts +3453 -0
  356. package/src/runtime/vendor.ts +1160 -0
  357. package/src/style-modules.d.ts +9 -0
  358. package/src/typegen.ts +151 -0
  359. package/src/types.ts +725 -0
  360. package/src/utils/ansi.ts +9 -0
  361. package/src/utils/content-type.ts +31 -0
  362. package/src/utils/decode.ts +7 -0
  363. package/src/utils/dev-profile.ts +31 -0
  364. package/src/utils/error-log.ts +29 -0
  365. package/src/utils/fs-cache.ts +31 -0
  366. package/src/utils/fs.ts +119 -0
  367. package/src/utils/html.ts +46 -0
  368. package/src/utils/serialize.ts +378 -0
  369. package/src/utils/source.ts +35 -0
  370. package/src/utils/verbose.ts +39 -0
  371. package/tsconfig.json +10 -0
@@ -0,0 +1,432 @@
1
+ import path from 'node:path';
2
+ import { safeDecode } from '../utils/decode';
3
+ import { readDirListing, toPosixPath } from '../utils/fs';
4
+ import type { InterceptionMarker, RouteParamValue } from '../types';
5
+
6
+ // Parallel-route (@slot) and intercepting-route ((.)segment) tree matching.
7
+ // Slot content resolves at request time against the filesystem: the app source
8
+ // tree is present in dev and production alike (start imports source modules),
9
+ // so matching walks directories directly instead of precomputing every slot
10
+ // combination into the manifest.
11
+
12
+ const pageExtensions = ['tsx', 'ts', 'jsx', 'js', 'mjs'] as const;
13
+
14
+ const interceptionMarkers: InterceptionMarker[] = ['(..)(..)', '(...)', '(..)', '(.)'];
15
+ const interceptionLevels: Record<InterceptionMarker, number> = {
16
+ '(.)': 0,
17
+ '(..)': 1,
18
+ '(..)(..)': 2,
19
+ '(...)': Number.POSITIVE_INFINITY,
20
+ };
21
+
22
+ export function interceptionMarkerOf(segment: string): InterceptionMarker | undefined {
23
+ return interceptionMarkers.find(
24
+ marker => segment.startsWith(marker) && segment.length > marker.length,
25
+ );
26
+ }
27
+
28
+ export function interceptionMarkerLevels(marker: InterceptionMarker) {
29
+ return interceptionLevels[marker];
30
+ }
31
+
32
+ export function isSlotSegment(name: string) {
33
+ return name.startsWith('@');
34
+ }
35
+
36
+ export function isGroupSegment(name: string) {
37
+ return name.startsWith('(') && name.endsWith(')') && !interceptionMarkerOf(name);
38
+ }
39
+
40
+ /** Nearest existing `<base>.<ext>` convention file in `dir`. */
41
+ export function slotConventionFile(dir: string, base: string) {
42
+ const { files } = readDirListing(dir);
43
+ for (const extension of pageExtensions) {
44
+ if (files.has(`${base}.${extension}`)) return path.join(dir, `${base}.${extension}`);
45
+ }
46
+ return undefined;
47
+ }
48
+
49
+ function childDirectories(dir: string) {
50
+ return readDirListing(dir).dirs;
51
+ }
52
+
53
+ /** `@slot` directories directly inside `dir`. */
54
+ export function slotDirectoriesIn(dir: string) {
55
+ return childDirectories(dir)
56
+ .filter(isSlotSegment)
57
+ .map(name => path.join(dir, name));
58
+ }
59
+
60
+ /**
61
+ * URL segments consumed by walking from `appPath` to `dir` (both absolute).
62
+ * Group and @slot directories consume nothing; `[param]` consumes one segment;
63
+ * a catch-all consumes the rest (represented as Infinity). Interception marker
64
+ * directories count as their bare segment (correct for `(.)`; deeper markers
65
+ * shift the mapping and are not supported for slot resolution below them).
66
+ */
67
+ export function consumedSegments(appPath: string, dir: string) {
68
+ const relative = path.relative(appPath, dir);
69
+ if (relative.startsWith('..')) return 0;
70
+ let consumed = 0;
71
+ for (const name of relative.split(path.sep).filter(Boolean)) {
72
+ if (isGroupSegment(name) || isSlotSegment(name)) continue;
73
+ const marker = interceptionMarkerOf(name);
74
+ const bare = marker ? name.slice(marker.length) : name;
75
+ if (/^\[\[?\.\.\./.test(bare)) return Number.POSITIVE_INFINITY;
76
+ consumed += 1;
77
+ }
78
+ return consumed;
79
+ }
80
+
81
+ export interface SegmentMatch {
82
+ /**
83
+ * The matched page file. Absent for a pageless match - a fully-consumed path that still has a layout or
84
+ * parallel slots to render, Next's implicit null children inside intercepted units.
85
+ */
86
+ file?: string;
87
+ /** Directories entered from (excluding) the start dir to the page's dir. */
88
+ dirs: string[];
89
+ /** Params captured while matching (decoded). */
90
+ params: Record<string, RouteParamValue>;
91
+ }
92
+
93
+ interface MatchOptions {
94
+ /** Enter interception marker dirs as if they were their bare segment. */
95
+ markersAsSegments?: boolean;
96
+ }
97
+
98
+ /**
99
+ * Match URL `segments` against the children tree rooted at `dir`. Specificity:
100
+ * a page at the exact depth, then literal children, then group dirs
101
+ * (transparent), then `[param]`, then `[...catchAll]`, then `[[...opt]]`.
102
+ */
103
+ export function matchSegments(
104
+ dir: string,
105
+ segments: string[],
106
+ options: MatchOptions = {},
107
+ params: Record<string, RouteParamValue> = {},
108
+ dirs: string[] = [],
109
+ ): SegmentMatch | null {
110
+ const children = childDirectories(dir);
111
+ const groups = children.filter(isGroupSegment);
112
+
113
+ if (segments.length === 0) {
114
+ const page = slotConventionFile(dir, 'page');
115
+ if (page) return { file: page, dirs, params };
116
+ for (const group of groups) {
117
+ const match = matchSegments(path.join(dir, group), [], options, params, [...dirs, group]);
118
+ if (match) return match;
119
+ }
120
+ for (const name of children) {
121
+ const param = optionalCatchAllName(bareName(name, options));
122
+ if (!param) continue;
123
+ const match = matchSegments(path.join(dir, name), [], options, { ...params, [param]: [] }, [
124
+ ...dirs,
125
+ name,
126
+ ]);
127
+ if (match) return match;
128
+ }
129
+ // Inside an intercepted unit a fully-consumed path may have no page yet
130
+ // still render: a layout (or parallel slots) with implicit null children.
131
+ if (
132
+ options.markersAsSegments &&
133
+ (slotConventionFile(dir, 'layout') || children.some(isSlotSegment))
134
+ ) {
135
+ return { dirs, params };
136
+ }
137
+ return null;
138
+ }
139
+
140
+ const [head, ...rest] = segments as [string, ...string[]];
141
+
142
+ // Literal segment.
143
+ for (const name of children) {
144
+ if (bareName(name, options) !== head) continue;
145
+ const match = matchSegments(path.join(dir, name), rest, options, params, [...dirs, name]);
146
+ if (match) return match;
147
+ }
148
+
149
+ // Route groups are transparent at this level.
150
+ for (const group of groups) {
151
+ const match = matchSegments(path.join(dir, group), segments, options, params, [...dirs, group]);
152
+ if (match) return match;
153
+ }
154
+
155
+ // Dynamic `[param]`.
156
+ for (const name of children) {
157
+ const param = dynamicParamName(bareName(name, options));
158
+ if (!param) continue;
159
+ const match = matchSegments(
160
+ path.join(dir, name),
161
+ rest,
162
+ options,
163
+ { ...params, [param]: safeDecode(head) },
164
+ [...dirs, name],
165
+ );
166
+ if (match) return match;
167
+ }
168
+
169
+ // `[...catchAll]` and `[[...opt]]` consume everything remaining.
170
+ for (const name of children) {
171
+ const bare = bareName(name, options);
172
+ const param = catchAllName(bare) ?? optionalCatchAllName(bare);
173
+ if (!param) continue;
174
+ const match = matchSegments(
175
+ path.join(dir, name),
176
+ [],
177
+ options,
178
+ { ...params, [param]: segments.map(safeDecode) },
179
+ [...dirs, name],
180
+ );
181
+ if (match) return match;
182
+ }
183
+
184
+ return null;
185
+ }
186
+
187
+ function bareName(name: string, options: MatchOptions) {
188
+ if (isSlotSegment(name)) return '\0';
189
+ const marker = interceptionMarkerOf(name);
190
+ if (!marker) return name;
191
+ return options.markersAsSegments ? name.slice(marker.length) : '\0';
192
+ }
193
+
194
+ function dynamicParamName(name: string) {
195
+ const match = /^\[([^\].]+)\]$/.exec(name);
196
+ return match?.[1];
197
+ }
198
+
199
+ function catchAllName(name: string) {
200
+ const match = /^\[\.\.\.([^\]]+)\]$/.exec(name);
201
+ return match?.[1];
202
+ }
203
+
204
+ function optionalCatchAllName(name: string) {
205
+ const match = /^\[\[\.\.\.([^\]]+)\]\]$/.exec(name);
206
+ return match?.[1];
207
+ }
208
+
209
+ /**
210
+ * The children fallback for a URL whose page is provided by a parallel slot:
211
+ * walk the children tree along `segments` while directories exist, returning
212
+ * the deepest `default.*` seen. Missing default means the children slot
213
+ * renders nothing (Next's implicit null default).
214
+ */
215
+ export function childrenDefaultFile(dir: string, segments: string[]): string | undefined {
216
+ let current = dir;
217
+ let best = slotConventionFile(current, 'default');
218
+ for (const segment of segments) {
219
+ const next = childSegmentDir(current, segment);
220
+ if (!next) break;
221
+ current = next;
222
+ best = slotConventionFile(current, 'default') ?? best;
223
+ }
224
+ return best;
225
+ }
226
+
227
+ function childSegmentDir(dir: string, segment: string): string | undefined {
228
+ const children = childDirectories(dir);
229
+ if (children.includes(segment)) return path.join(dir, segment);
230
+ for (const group of children.filter(isGroupSegment)) {
231
+ const nested = childSegmentDir(path.join(dir, group), segment);
232
+ if (nested) return nested;
233
+ }
234
+ const dynamic = children.find(name => dynamicParamName(name));
235
+ if (dynamic) return path.join(dir, dynamic);
236
+ return undefined;
237
+ }
238
+
239
+ export interface InterceptorUnit {
240
+ /** The marker directory (absolute). Content below renders when intercepted. */
241
+ dir: string;
242
+ /** Route-style target prefix segments from the app root (e.g. `['groups', ':id', 'new']`). */
243
+ targetSegments: string[];
244
+ /** Regex source matching the target prefix (no anchors). */
245
+ targetPattern: string;
246
+ /** Params captured by targetPattern, in group order. */
247
+ params: { name: string; catchAll: boolean }[];
248
+ /** The final target token is a catch-all (consumes every remaining segment). */
249
+ catchAllTail: boolean;
250
+ }
251
+
252
+ /**
253
+ * Interception marker directories inside a slot tree, with their full-path
254
+ * target prefixes resolved against the slot's owner path. Only top-level
255
+ * marker units are returned: markers nested under another marker resolve as
256
+ * plain segments during `matchSegments({ markersAsSegments: true })`.
257
+ */
258
+ export function slotInterceptorUnits(appPath: string, slotDir: string): InterceptorUnit[] {
259
+ const owner = routeTokensForDir(appPath, path.dirname(slotDir));
260
+ if (!owner) return [];
261
+ const units: InterceptorUnit[] = [];
262
+ collectInterceptorUnits(slotDir, owner, [], units);
263
+ return units;
264
+ }
265
+
266
+ interface RouteToken {
267
+ route: string;
268
+ pattern: string;
269
+ param?: string;
270
+ catchAll?: boolean;
271
+ }
272
+
273
+ function collectInterceptorUnits(
274
+ dir: string,
275
+ owner: RouteToken[],
276
+ inner: RouteToken[],
277
+ units: InterceptorUnit[],
278
+ ) {
279
+ for (const name of childDirectories(dir)) {
280
+ const child = path.join(dir, name);
281
+ if (isSlotSegment(name) || isGroupSegment(name)) {
282
+ collectInterceptorUnits(child, owner, inner, units);
283
+ continue;
284
+ }
285
+ const marker = interceptionMarkerOf(name);
286
+ if (marker) {
287
+ const bare = name.slice(marker.length);
288
+ const levels = interceptionLevels[marker];
289
+ const context = [...owner, ...inner];
290
+ const kept =
291
+ levels === Number.POSITIVE_INFINITY ? [] : context.slice(0, Math.max(0, context.length - levels));
292
+ const target = [...kept, segmentToken(bare)];
293
+ units.push({
294
+ dir: child,
295
+ targetSegments: target.map(token => token.route),
296
+ targetPattern: target.map(token => token.pattern).join('/'),
297
+ params: target
298
+ .filter(token => token.param)
299
+ .map(token => ({ name: token.param!, catchAll: Boolean(token.catchAll) })),
300
+ catchAllTail: Boolean(target[target.length - 1]?.catchAll),
301
+ });
302
+ continue;
303
+ }
304
+ collectInterceptorUnits(child, owner, [...inner, segmentToken(name)], units);
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Route tokens (URL-contributing segments) for a directory under the app
310
+ * root; groups and @slots contribute nothing; markers resolve their levels.
311
+ * Returns undefined when the dir is outside the app root.
312
+ */
313
+ export function routeTokensForDir(appPath: string, dir: string): RouteToken[] | undefined {
314
+ const relative = path.relative(appPath, dir);
315
+ if (relative.startsWith('..')) return undefined;
316
+ let tokens: RouteToken[] = [];
317
+ for (const name of toPosixPath(relative).split('/').filter(Boolean)) {
318
+ if (isGroupSegment(name) || isSlotSegment(name)) continue;
319
+ const marker = interceptionMarkerOf(name);
320
+ if (marker) {
321
+ const levels = interceptionLevels[marker];
322
+ tokens =
323
+ levels === Number.POSITIVE_INFINITY
324
+ ? []
325
+ : tokens.slice(0, Math.max(0, tokens.length - levels));
326
+ tokens.push(segmentToken(name.slice(marker.length)));
327
+ continue;
328
+ }
329
+ tokens.push(segmentToken(name));
330
+ }
331
+ return tokens;
332
+ }
333
+
334
+ function segmentToken(name: string): RouteToken {
335
+ const optional = optionalCatchAllName(name);
336
+ if (optional) return { route: `:${optional}*`, pattern: '(.*)', param: optional, catchAll: true };
337
+ const catchAll = catchAllName(name);
338
+ if (catchAll) return { route: `:${catchAll}*`, pattern: '(.*)', param: catchAll, catchAll: true };
339
+ const param = dynamicParamName(name);
340
+ if (param) return { route: `:${param}`, pattern: '([^/]+)', param };
341
+ return { route: name, pattern: escapeRegex(name) };
342
+ }
343
+
344
+ /**
345
+ * Try to match `pathname` against a slot's interceptor units: the unit's
346
+ * target prefix must match the head of the pathname and the remainder must
347
+ * resolve to a page inside the marker directory (markers below the unit are
348
+ * treated as plain segments).
349
+ */
350
+ export function matchSlotInterceptor(
351
+ appPath: string,
352
+ slotDir: string,
353
+ pathname: string,
354
+ ): SegmentMatch | null {
355
+ const segments = pathnameSegments(pathname);
356
+ const markerOwner = markerOwnerTokens(appPath, slotDir);
357
+ if (markerOwner) {
358
+ const params = matchTargetTokens(markerOwner, pathname);
359
+ if (params) {
360
+ const rest = markerOwner.at(-1)?.catchAll ? [] : segments.slice(markerOwner.length);
361
+ const inner = matchSegments(slotDir, rest, { markersAsSegments: true }, params, []);
362
+ if (inner) return inner;
363
+ }
364
+ }
365
+ // Specificity: non-catch-all first, deeper targets first, fewer dynamic
366
+ // params first (a literal `(.)foo` beats `(.)[param]` for /foo).
367
+ const units = slotInterceptorUnits(appPath, slotDir).sort(
368
+ (a, b) =>
369
+ Number(a.catchAllTail) - Number(b.catchAllTail) ||
370
+ b.targetSegments.length - a.targetSegments.length ||
371
+ a.params.length - b.params.length,
372
+ );
373
+ for (const unit of units) {
374
+ const regex = new RegExp(`^/${unit.targetPattern}(?:/(.*))?$`);
375
+ const match = regex.exec(pathname.replace(/\/+$/, '') || '/');
376
+ if (!match) continue;
377
+ const params: Record<string, RouteParamValue> = {};
378
+ unit.params.forEach((param, index) => {
379
+ const value = match[index + 1] ?? '';
380
+ params[param.name] = param.catchAll
381
+ ? value.split('/').filter(Boolean).map(safeDecode)
382
+ : safeDecode(value);
383
+ });
384
+ const rest = unit.catchAllTail ? [] : segments.slice(unit.targetSegments.length);
385
+ const inner = matchSegments(unit.dir, rest, { markersAsSegments: true }, params, []);
386
+ if (inner) {
387
+ return {
388
+ file: inner.file,
389
+ dirs: [path.basename(unit.dir), ...inner.dirs],
390
+ params: inner.params,
391
+ };
392
+ }
393
+ }
394
+ return null;
395
+ }
396
+
397
+ function markerOwnerTokens(appPath: string, slotDir: string): RouteToken[] | null {
398
+ const relative = path.relative(appPath, slotDir);
399
+ if (relative.startsWith('..')) return null;
400
+ const segments = toPosixPath(relative).split('/').filter(Boolean);
401
+ const markerIndex = segments.findIndex(segment => Boolean(interceptionMarkerOf(segment)));
402
+ if (markerIndex === -1) return null;
403
+ return routeTokensForDir(appPath, path.join(appPath, ...segments.slice(0, markerIndex + 1))) ?? null;
404
+ }
405
+
406
+ function matchTargetTokens(
407
+ tokens: RouteToken[],
408
+ pathname: string,
409
+ ): Record<string, RouteParamValue> | null {
410
+ const pattern = tokens.map(token => token.pattern).join('/');
411
+ const regex = new RegExp(`^/${pattern}(?:/.*)?$`);
412
+ const match = regex.exec(pathname.replace(/\/+$/, '') || '/');
413
+ if (!match) return null;
414
+ const params: Record<string, RouteParamValue> = {};
415
+ tokens
416
+ .filter(token => token.param)
417
+ .forEach((token, index) => {
418
+ const value = match[index + 1] ?? '';
419
+ params[token.param!] = token.catchAll
420
+ ? value.split('/').filter(Boolean).map(safeDecode)
421
+ : safeDecode(value);
422
+ });
423
+ return params;
424
+ }
425
+
426
+ export function pathnameSegments(pathname: string) {
427
+ return pathname.split('?')[0]!.split('/').filter(Boolean);
428
+ }
429
+
430
+ function escapeRegex(value: string) {
431
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
432
+ }