@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,410 @@
1
+ // Routing extension registration (COMPAT - may import core freely).
2
+ //
3
+ // Wires the Next-only routing conventions and request-dependency detectors into the core routing registry
4
+ // so core's route scanner carries no Next hardcoding: registerRouteConventions gets the authInterrupt
5
+ // boundaries (forbidden/unauthorized), and registerUsageDetectors gets next/headers, next/navigation
6
+ // request-hook and next/server connection() import detection.
7
+ //
8
+ // A pure-core app registers none of these, so its route scan discovers only the generic conventions.
9
+
10
+ import type { ResolvedConfig } from '../../config';
11
+ import {
12
+ registerAlwaysClientEntryReason,
13
+ registerClientEntryDetectors,
14
+ registerRouteDependencyClassifiers,
15
+ registerRouteConventions,
16
+ registerUsageDetectors,
17
+ } from '../../extensions';
18
+ import { nextCompatEnabled } from '../../compat';
19
+ import { escapeRegex } from '../../utils/source';
20
+
21
+ export function registerRoutingExtensions(config: ResolvedConfig): void {
22
+ if (!nextCompatEnabled(config)) return;
23
+ // Next's experimental authInterrupts boundaries mirror error/not-found.
24
+ registerRouteConventions(
25
+ { name: 'forbidden', boundary: true },
26
+ { name: 'unauthorized', boundary: true },
27
+ );
28
+ registerUsageDetectors(usesNextRequestImport, usesNextServerConnection);
29
+ registerRouteDependencyClassifiers(classifyNextRouteDependencies);
30
+ registerClientEntryDetectors(
31
+ { reason: 'actions', detect: usesServerActionForm },
32
+ { reason: 'form', detect: usesNextForm },
33
+ { reason: 'control-flow', detect: usesClientControlFlowThrow },
34
+ );
35
+ // Next emits its app-router bootstrap for every app page, so every document
36
+ // hydrates — instrumentation-client runs before hydration and the hydration
37
+ // timestamp exists even on a page with no client components at all
38
+ // (instrumentation-client-hook asserts both). Core keeps its leaner default.
39
+ registerAlwaysClientEntryReason('compat-parity');
40
+ }
41
+
42
+ // A route's layout chain is re-classified for every route below it, and the answer depends only on
43
+ // the source and the route kind - memoize on exactly that.
44
+ const classificationCache = new Map<string, boolean>();
45
+
46
+ function classifyNextRouteDependencies({
47
+ kind,
48
+ files,
49
+ }: import('../../extensions').RouteDependencyContext) {
50
+ for (const { source } of files) {
51
+ const key = `${kind}\0${source.length}\0${Bun.hash(source).toString()}`;
52
+ let usesRequest = classificationCache.get(key);
53
+ if (usesRequest === undefined) {
54
+ usesRequest = sourceUsesRequest(kind, source);
55
+ classificationCache.set(key, usesRequest);
56
+ }
57
+ if (usesRequest) return { usesRequest: true };
58
+ }
59
+ return undefined;
60
+ }
61
+
62
+ function sourceUsesRequest(kind: 'page' | 'handler', source: string): boolean {
63
+ const requestUse = usesNextRequestImport(source) || usesNextServerConnection(source);
64
+ const afterOnly = hasAfterCallbacks(source) && !hasRequestApiOutsideAfter(source);
65
+ const handlerAfterDraftMode = kind === 'handler' && hasAfterDraftMode(source);
66
+
67
+ // Page after() callbacks run after their render scope has detached, so an
68
+ // API used only there does not make the page dynamic. Handler callbacks
69
+ // retain the request scope and must render per request instead.
70
+ if ((requestUse && (kind === 'handler' || !afterOnly)) || handlerAfterDraftMode) return true;
71
+ // An automatic page that schedules after() work needs a request lifecycle to flush it after the
72
+ // response. Explicit static segment config still wins in core's mode inference: the page is statically
73
+ // prerendered and its after() runs during the build prerender, so it must NOT be forced dynamic here.
74
+ // draftMode()-only after() work needs no request lifecycle either: it is readable at buildtime and
75
+ // enable()/disable() throw inside after() regardless. Such a page stays statically prerenderable so its
76
+ // callbacks run during the build prerender.
77
+ return (
78
+ kind === 'page' &&
79
+ hasAfterCallbacks(source) &&
80
+ !hasRequestApiUse(source) &&
81
+ !hasAfterDraftMode(source) &&
82
+ !hasExplicitStaticDynamic(source)
83
+ );
84
+ }
85
+
86
+ function hasAfterDraftMode(source: string): boolean {
87
+ return hasAfterCallbacks(source) && nextHeadersBindings(source).draftMode.length > 0;
88
+ }
89
+
90
+ function hasRequestApiOutsideAfter(source: string): boolean {
91
+ return hasRequestApiUse(withoutAfterCallbacks(source));
92
+ }
93
+
94
+ function hasRequestApiUse(source: string): boolean {
95
+ const outside = stripCommentsAndStrings(source);
96
+ const headers = nextHeadersBindings(source);
97
+ return (
98
+ headers.headers.some(name => new RegExp(`\\b${escapeRegex(name)}\\s*\\(`).test(outside)) ||
99
+ headers.cookies.some(name => new RegExp(`\\b${escapeRegex(name)}\\s*\\(`).test(outside)) ||
100
+ connectionBindings(source).some(name => new RegExp(`\\b${escapeRegex(name)}\\s*\\(`).test(outside))
101
+ );
102
+ }
103
+
104
+ function nextHeadersBindings(source: string) {
105
+ const headers: string[] = [];
106
+ const cookies: string[] = [];
107
+ const draftMode: string[] = [];
108
+ const pattern = /\bimport\s+\{([^}]*)\}\s+from\s*['"]next\/headers(?:\.js)?['"]/g;
109
+ let match: RegExpExecArray | null;
110
+ while ((match = pattern.exec(stripComments(source)))) {
111
+ for (const item of match[1]!.split(',')) {
112
+ const [imported, local] = item.trim().split(/\s+as\s+/);
113
+ const name = (local ?? imported)?.trim();
114
+ if (!name) continue;
115
+ if (imported?.trim() === 'headers') headers.push(name);
116
+ if (imported?.trim() === 'cookies') cookies.push(name);
117
+ if (imported?.trim() === 'draftMode') draftMode.push(name);
118
+ }
119
+ }
120
+ return { headers, cookies, draftMode };
121
+ }
122
+
123
+ function connectionBindings(source: string): string[] {
124
+ const names: string[] = [];
125
+ const pattern = /\bimport\s+[^'";]+?\s+from\s*['"]next\/server(?:\.js)?['"]/g;
126
+ let match: RegExpExecArray | null;
127
+ while ((match = pattern.exec(stripComments(source)))) {
128
+ const named = /\{([^}]*)\}/.exec(match[0])?.[1];
129
+ if (!named) continue;
130
+ for (const item of named.split(',')) {
131
+ const [imported, local] = item.trim().split(/\s+as\s+/);
132
+ if (imported?.trim() === 'connection') names.push((local ?? imported).trim());
133
+ }
134
+ }
135
+ return names;
136
+ }
137
+
138
+ // Masking only blanks comment/string characters, so a source with no `after`
139
+ // token at all cannot grow one — the gate skips the whole-source mask (the
140
+ // common case) without changing a single answer.
141
+ const AFTER_TOKEN = /\bafter\b/;
142
+
143
+ function mentionsAfter(source: string): boolean {
144
+ return source.includes('after') && AFTER_TOKEN.test(source);
145
+ }
146
+
147
+ function hasAfterCallbacks(source: string): boolean {
148
+ return mentionsAfter(source) && /\bafter\s*\(/.test(maskCommentsAndStrings(source));
149
+ }
150
+
151
+ // Explicit static segment config that keeps a page statically prerenderable
152
+ // (so its after() runs during the build prerender, not per request). Matches
153
+ // `export const dynamic = 'error'` / `'force-static'`.
154
+ function hasExplicitStaticDynamic(source: string): boolean {
155
+ return /\bexport\s+const\s+dynamic\s*=\s*['"](?:error|force-static)['"]/.test(
156
+ stripComments(source),
157
+ );
158
+ }
159
+
160
+ function withoutAfterCallbacks(source: string): string {
161
+ if (!mentionsAfter(source)) return source;
162
+ const chars = [...source];
163
+ const searchable = maskCommentsAndStrings(source);
164
+ const pattern = /\bafter\s*\(/g;
165
+ let match: RegExpExecArray | null;
166
+ while ((match = pattern.exec(searchable))) {
167
+ let depth = 1;
168
+ let index = match.index + match[0].length;
169
+ for (; index < searchable.length && depth > 0; index++) {
170
+ if (searchable[index] === '(') depth++;
171
+ else if (searchable[index] === ')') depth--;
172
+ }
173
+ if (depth !== 0) continue;
174
+ for (let cursor = match.index; cursor < index; cursor++) chars[cursor] = ' ';
175
+ pattern.lastIndex = index;
176
+ }
177
+ return chars.join('');
178
+ }
179
+
180
+ const maskCommentsAndStrings = memoizeBySource(maskSource);
181
+
182
+ function maskSource(source: string): string {
183
+ const chars = [...source];
184
+ let quote: string | undefined;
185
+ let lineComment = false;
186
+ let blockComment = false;
187
+ for (let index = 0; index < chars.length; index++) {
188
+ const char = chars[index]!;
189
+ const next = chars[index + 1];
190
+ if (lineComment) {
191
+ if (char === '\n') lineComment = false;
192
+ else chars[index] = ' ';
193
+ continue;
194
+ }
195
+ if (blockComment) {
196
+ chars[index] = char === '\n' ? '\n' : ' ';
197
+ if (char === '*' && next === '/') {
198
+ chars[index + 1] = ' ';
199
+ blockComment = false;
200
+ index++;
201
+ }
202
+ continue;
203
+ }
204
+ if (quote) {
205
+ chars[index] = char === '\n' ? '\n' : ' ';
206
+ if (char === '\\') {
207
+ if (next && next !== '\n') chars[index + 1] = ' ';
208
+ index++;
209
+ } else if (char === quote) {
210
+ quote = undefined;
211
+ }
212
+ continue;
213
+ }
214
+ if (char === '/' && next === '/') {
215
+ chars[index] = chars[index + 1] = ' ';
216
+ lineComment = true;
217
+ index++;
218
+ continue;
219
+ }
220
+ if (char === '/' && next === '*') {
221
+ chars[index] = chars[index + 1] = ' ';
222
+ blockComment = true;
223
+ index++;
224
+ continue;
225
+ }
226
+ if (char === '"' || char === "'" || char === '`') {
227
+ chars[index] = ' ';
228
+ quote = char;
229
+ }
230
+ }
231
+ return chars.join('');
232
+ }
233
+
234
+ // A source that declares a server action needs the client action runtime, even
235
+ // when the <form> itself lives in a client component the page hands the action
236
+ // to. Matched on the RAW source: stripComments also blanks string literals,
237
+ // which would erase the directive being looked for.
238
+ function usesServerActionForm(source: string): boolean {
239
+ if (source.includes('use server') && /(['"])use server\1/.test(source)) return true;
240
+ return usesClientFunctionFormAction(source);
241
+ }
242
+
243
+ // React runs a FUNCTION passed to `<form action>` / `<button formAction>` on the client with the form's
244
+ // FormData - no server action involved. That needs the action runtime's submit interception even in an app
245
+ // with no `'use server'` anywhere. Restricted to `'use client'` sources (a server component passing a
246
+ // function needs the directive) and to non-string expressions, so `<form action={url}>` keeps the lean
247
+ // entry.
248
+ const CLIENT_FUNCTION_ACTION_PATTERN = /\b(?:action|formAction)\s*=\s*\{\s*(?!['"`])/;
249
+
250
+ function usesClientFunctionFormAction(source: string): boolean {
251
+ if (!hasUseClientDirective(source)) return false;
252
+ return CLIENT_FUNCTION_ACTION_PATTERN.test(stripComments(source));
253
+ }
254
+
255
+ const USE_CLIENT_DIRECTIVE = /^\s*(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/\s*|#![^\n]*\n|\s)*(['"])use client\1/;
256
+
257
+ // The prologue regex scans every leading comment; the literal it ends in has to
258
+ // be somewhere in the source for it to match at all.
259
+ function hasUseClientDirective(source: string): boolean {
260
+ return source.includes('use client') && USE_CLIENT_DIRECTIVE.test(source);
261
+ }
262
+ const CONTROL_FLOW_IMPORT_PATTERN =
263
+ /import\s*\{[^}]*\b(?:notFound|forbidden|unauthorized)\b[^}]*\}\s*from\s*['"]next\/navigation(?:\.js)?['"]/;
264
+
265
+ // A `'use client'` module that throws notFound()/forbidden()/unauthorized() does it during HYDRATION,
266
+ // where only the in-tree ClientErrorBoundary can render the outcome - the deferred window last resort
267
+ // lands at idle, long after the throw has unmounted the SSR tree. Server components keep the lean entry:
268
+ // their throw is resolved by the server render.
269
+ function usesClientControlFlowThrow(source: string): boolean {
270
+ return hasUseClientDirective(source) && CONTROL_FLOW_IMPORT_PATTERN.test(source);
271
+ }
272
+
273
+ const NEXT_FORM_IMPORT_PATTERN = /import\s+[^'";]*\s+from\s+['"]next\/form(?:\.js)?['"]/;
274
+
275
+ function usesNextForm(source: string): boolean {
276
+ if (!source.includes('next/form')) return false;
277
+ const searchable = stripComments(source);
278
+ if (!NEXT_FORM_IMPORT_PATTERN.test(searchable)) return false;
279
+ return (
280
+ /\b<form\b[^>]*\b(?:action|formAction)\s*=/.test(searchable) ||
281
+ /\bformAction\s*:\s*/.test(searchable) ||
282
+ /<\s*[A-Za-z_$][\w$]*\b[^>]*\b(?:action|formAction)\s*=/.test(searchable)
283
+ );
284
+ }
285
+
286
+ // A next/headers or next/navigation request-hook import makes a route dynamic.
287
+ function usesNextRequestImport(source: string): boolean {
288
+ const withoutComments = stripComments(source);
289
+ // A `draftMode`-only import of next/headers is not request-bound: Next still
290
+ // fully prerenders such pages (isEnabled is false at build time); only a
291
+ // __prerender_bypass cookie opts into a fresh dynamic render at request time.
292
+ const draftModeOnlyImport =
293
+ /\bimport\s+\{\s*draftMode\s*(?:as\s+[\w$]+\s*)?,?\s*\}\s*from\s*['"]next\/headers(?:\.js)?['"]/g;
294
+ return (
295
+ nextHeadersImportPattern().test(withoutComments.replace(draftModeOnlyImport, '')) ||
296
+ importsNextNavigationRequestHook(withoutComments)
297
+ );
298
+ }
299
+
300
+ function nextHeadersImportPattern() {
301
+ return /(?:from\s+|import\s*\()\s*['"]next\/headers(?:\.js)?['"]/;
302
+ }
303
+
304
+ function importsNextNavigationRequestHook(source: string): boolean {
305
+ const importPattern = /\bimport\s+(type\s+)?([^'";]+?)\s+from\s+['"]next\/navigation['"]/g;
306
+ let match: RegExpExecArray | null;
307
+ while ((match = importPattern.exec(source))) {
308
+ const [, typeKeyword, clause] = match;
309
+ if (!typeKeyword && clause && includesRequestNavigationHook(importedExports(clause)))
310
+ return true;
311
+ }
312
+
313
+ const exportPattern = /\bexport\s+(type\s+)?\{([^}]*)\}\s+from\s+['"]next\/navigation['"]/g;
314
+ while ((match = exportPattern.exec(source))) {
315
+ const [, typeKeyword, clause] = match;
316
+ if (!typeKeyword && clause && includesRequestNavigationHook(exportedNames(clause))) return true;
317
+ }
318
+ return false;
319
+ }
320
+
321
+ function usesNextServerConnection(source: string): boolean {
322
+ const withoutComments = stripComments(source);
323
+ const searchable = stripCommentsAndStrings(source);
324
+ const importPattern = /\bimport\s+(type\s+)?([^'";]+?)\s+from\s+['"]next\/server(?:\.js)?['"]/g;
325
+ let match: RegExpExecArray | null;
326
+ while ((match = importPattern.exec(withoutComments))) {
327
+ const [, typeKeyword, clause] = match;
328
+ if (typeKeyword || !clause) continue;
329
+ for (const pattern of importedConnectionCallPatterns(clause)) {
330
+ if (pattern.test(searchable)) return true;
331
+ }
332
+ }
333
+ return false;
334
+ }
335
+
336
+ function importedConnectionCallPatterns(clause: string): RegExp[] {
337
+ const patterns: RegExp[] = [];
338
+ const namespace = /\*\s+as\s+([A-Za-z_$][\w$]*)/.exec(clause)?.[1];
339
+ if (namespace) patterns.push(new RegExp(`\\b${escapeRegex(namespace)}\\.connection\\s*\\(`));
340
+ const named = /\{([^}]*)\}/.exec(clause)?.[1];
341
+ if (named) {
342
+ for (const item of named.split(',')) {
343
+ const [imported, local] = item.trim().split(/\s+as\s+/);
344
+ if (imported?.trim() === 'connection') {
345
+ patterns.push(new RegExp(`\\b${escapeRegex((local ?? imported).trim())}\\s*\\(`));
346
+ }
347
+ }
348
+ }
349
+ return patterns;
350
+ }
351
+
352
+ function includesRequestNavigationHook(exports: string[]): boolean {
353
+ return (
354
+ exports.includes('*') || exports.includes('useSearchParams') || exports.includes('useRoute')
355
+ );
356
+ }
357
+
358
+ function importedExports(clause: string): string[] {
359
+ const value = clause.trim();
360
+ if (!value) return [];
361
+ const exports = new Set<string>();
362
+ if (value.startsWith('*')) exports.add('*');
363
+ const defaultImport = /^[A-Za-z_$][\w$]*/.exec(value);
364
+ if (defaultImport && !value.startsWith('{') && !value.startsWith('*')) exports.add('default');
365
+ const named = /\{([^}]*)\}/.exec(value);
366
+ if (named?.[1]) {
367
+ for (const item of named[1].split(',')) {
368
+ const [name] = item.trim().split(/\s+as\s+/);
369
+ if (name) exports.add(name.trim());
370
+ }
371
+ }
372
+ return [...exports];
373
+ }
374
+
375
+ function exportedNames(clause: string): string[] {
376
+ return clause
377
+ .split(',')
378
+ .map(item =>
379
+ item
380
+ .trim()
381
+ .split(/\s+as\s+/)[0]
382
+ ?.trim(),
383
+ )
384
+ .filter((name): name is string => Boolean(name) && name !== 'type');
385
+ }
386
+
387
+ /**
388
+ * One route's request-dependency answer strips the same source several times over and masks it several
389
+ * more, each a whole-source pass. A tiny FIFO of recent (source -> derived) pairs collapses those to one
390
+ * each; the key is the source itself, so it never stales.
391
+ */
392
+ function memoizeBySource(derive: (source: string) => string) {
393
+ const cache = new Map<string, string>();
394
+ return (source: string): string => {
395
+ const hit = cache.get(source);
396
+ if (hit !== undefined) return hit;
397
+ const value = derive(source);
398
+ if (cache.size >= 4) cache.delete(cache.keys().next().value!);
399
+ cache.set(source, value);
400
+ return value;
401
+ };
402
+ }
403
+
404
+ const stripCommentsAndStrings = memoizeBySource(source =>
405
+ stripComments(source).replace(/(['"`])(?:\\[\s\S]|(?!\1)[^\\])*\1/g, ''),
406
+ );
407
+
408
+ const stripComments = memoizeBySource(source =>
409
+ source.replace(/\/\*[\s\S]*?\*\//g, '').replace(/(^|[^:])\/\/.*$/gm, '$1'),
410
+ );