@wular/pnext 0.0.4 → 0.0.7

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 (395) hide show
  1. package/README.md +22 -16
  2. package/bin/pnext +6 -1
  3. package/config/lint/base.js +7 -7
  4. package/config/ts/base.json +2 -4
  5. package/config/ts/react.json +2 -6
  6. package/package.json +23 -2
  7. package/reference/compat.md +87 -22
  8. package/reference/config.md +40 -93
  9. package/reference/css.md +42 -33
  10. package/reference/dev.md +26 -33
  11. package/reference/env.md +18 -20
  12. package/reference/getting-started.md +132 -0
  13. package/reference/metadata.md +31 -46
  14. package/reference/navigation.md +65 -105
  15. package/reference/performance.md +21 -142
  16. package/reference/rendering.md +78 -75
  17. package/reference/routing.md +97 -107
  18. package/reference/typegen.md +27 -29
  19. package/src/api/cache.ts +36 -37
  20. package/src/api/client-cache.ts +2 -2
  21. package/src/api/client-navigation.ts +121 -114
  22. package/src/api/dynamic.tsx +58 -55
  23. package/src/api/link.tsx +52 -53
  24. package/src/api/navigation.ts +59 -58
  25. package/src/api/server.ts +152 -144
  26. package/src/api/suspense.ts +4 -4
  27. package/src/cli/adapters/vercel-warm.ts +126 -121
  28. package/src/cli/adapters/vercel.ts +437 -443
  29. package/src/cli/analyze.ts +333 -144
  30. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  31. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  32. package/src/cli/build.ts +976 -978
  33. package/src/cli/create.ts +72 -64
  34. package/src/cli/dev.ts +116 -116
  35. package/src/cli/index.ts +94 -85
  36. package/src/cli/migrate/package-json.ts +73 -71
  37. package/src/cli/migrate/report.ts +32 -33
  38. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  39. package/src/cli/migrate/scan.ts +46 -46
  40. package/src/cli/migrate/spinner.ts +9 -9
  41. package/src/cli/migrate/tsconfig.ts +35 -35
  42. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  43. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  44. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  45. package/src/cli/start.ts +65 -66
  46. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  47. package/src/client/build.ts +802 -768
  48. package/src/client/chunk-fold.ts +245 -240
  49. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  50. package/src/client/entry.ts +162 -147
  51. package/src/client/prebuilt.ts +231 -223
  52. package/src/client/profile.ts +29 -29
  53. package/src/client/react-compiler.ts +20 -15
  54. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  55. package/src/client/reference-stub.ts +51 -51
  56. package/src/client/reference.ts +19 -19
  57. package/src/{api → client}/router/events.ts +32 -17
  58. package/src/{api → client}/router/history.ts +16 -16
  59. package/src/{api → client}/router/hub.ts +52 -53
  60. package/src/{api/router.ts → client/router/index.ts} +61 -60
  61. package/src/{api → client}/router/policies.ts +24 -24
  62. package/src/{api → client}/router/runtime.ts +2022 -1995
  63. package/src/{api → client}/router/types.ts +98 -98
  64. package/src/compat/actions/client-plugin.ts +42 -43
  65. package/src/compat/actions/client-stub.ts +14 -14
  66. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  67. package/src/compat/actions/config.ts +63 -64
  68. package/src/compat/actions/detect.ts +68 -68
  69. package/src/compat/actions/discovery.ts +105 -105
  70. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  71. package/src/compat/actions/early-submit.ts +1 -1
  72. package/src/compat/actions/endpoint.ts +198 -198
  73. package/src/compat/actions/extensions.ts +811 -0
  74. package/src/compat/actions/flight.ts +23 -23
  75. package/src/compat/actions/form-state.ts +34 -34
  76. package/src/compat/actions/hoist.ts +382 -236
  77. package/src/compat/actions/ids.ts +7 -12
  78. package/src/compat/actions/index.ts +8 -8
  79. package/src/compat/actions/instances.ts +46 -46
  80. package/src/compat/actions/origin.ts +47 -48
  81. package/src/compat/actions/protocol.ts +22 -22
  82. package/src/compat/actions/registry.ts +19 -19
  83. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  84. package/src/compat/actions/rewrite.ts +125 -125
  85. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  86. package/src/compat/actions/serve.ts +132 -136
  87. package/src/compat/actions/server-tag.ts +4 -4
  88. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  89. package/src/compat/actions/unrecognized-error.ts +4 -4
  90. package/src/compat/{index.ts → aliases.ts} +112 -109
  91. package/src/compat/bundler/bun-externals.ts +18 -18
  92. package/src/compat/bundler/cjs-exports.ts +271 -223
  93. package/src/compat/bundler/config.ts +116 -111
  94. package/src/compat/bundler/externals.ts +12 -12
  95. package/src/compat/bundler/import-meta-url.ts +19 -19
  96. package/src/compat/bundler/modularize-imports.ts +36 -33
  97. package/src/compat/bundler/new-url-asset.ts +22 -24
  98. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  99. package/src/compat/bundler/polyfill.ts +28 -28
  100. package/src/compat/bundler/react-compiler.ts +35 -29
  101. package/src/compat/bundler/react-profiler.tsx +11 -11
  102. package/src/compat/bundler/relay-transform.ts +48 -47
  103. package/src/compat/bundler/require-context.ts +119 -113
  104. package/src/compat/bundler/resolve-extensions.ts +19 -19
  105. package/src/compat/bundler/source-cache.ts +25 -25
  106. package/src/compat/bundler/static-imports.ts +7 -7
  107. package/src/compat/bundler/symlink-imports.ts +46 -46
  108. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  109. package/src/compat/bundler/wasm.ts +58 -60
  110. package/src/compat/bundler/webpack-loaders.ts +254 -241
  111. package/src/compat/bundler/worker.ts +101 -104
  112. package/src/compat/cache/build-flags.ts +29 -29
  113. package/src/compat/cache/build-prerender-errors.ts +40 -44
  114. package/src/compat/cache/custom-handler.ts +60 -53
  115. package/src/compat/cache/fetch-patch.ts +240 -240
  116. package/src/compat/cache/handler.ts +27 -31
  117. package/src/compat/cache/modern-handler.ts +148 -126
  118. package/src/compat/cache/resume-data-cache.ts +47 -45
  119. package/src/compat/cache/revalidate.ts +233 -232
  120. package/src/compat/cache/runtime-error.ts +35 -35
  121. package/src/compat/cache/use-cache-transform.ts +442 -417
  122. package/src/compat/cache/use-cache.ts +650 -614
  123. package/src/compat/cache-control.ts +140 -142
  124. package/src/compat/client/base-path.ts +21 -20
  125. package/src/compat/client/css-order.ts +18 -18
  126. package/src/compat/client/errors/bare-boundary.ts +11 -11
  127. package/src/compat/client/errors/control-flow.ts +23 -23
  128. package/src/compat/client/errors/error-boundary.ts +61 -61
  129. package/src/compat/client/errors/global-error.ts +101 -91
  130. package/src/compat/client/errors/install.ts +71 -72
  131. package/src/compat/client/errors/lazy.ts +23 -23
  132. package/src/compat/client/errors/primitive-throw.ts +47 -45
  133. package/src/compat/client/errors/soft-refresh.ts +4 -4
  134. package/src/compat/client/link-status.ts +33 -33
  135. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  136. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  137. package/src/compat/client/navigation-scroll.ts +63 -63
  138. package/src/compat/client/optimistic-routing.ts +93 -88
  139. package/src/compat/client/prefetch-cache.ts +23 -24
  140. package/src/compat/client/route-announcer.ts +35 -35
  141. package/src/compat/client/segment-cache-policy.ts +20 -20
  142. package/src/compat/client/segment-cache.ts +309 -315
  143. package/src/compat/client/segment-prefetch.ts +127 -132
  144. package/src/compat/client/trailing-slash.ts +5 -4
  145. package/src/compat/css/chunking.ts +113 -116
  146. package/src/compat/css/inline-css.ts +21 -21
  147. package/src/compat/css/lightningcss.ts +37 -38
  148. package/src/compat/css/modules.ts +161 -175
  149. package/src/compat/css/nonce.ts +7 -7
  150. package/src/compat/css/sass-plugin.ts +18 -21
  151. package/src/compat/css/sass.ts +150 -152
  152. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  153. package/src/compat/css/styled-jsx.ts +21 -21
  154. package/src/compat/edge-runtime.ts +27 -27
  155. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  156. package/src/compat/export/client.ts +29 -31
  157. package/src/compat/export/{index.ts → emit.ts} +111 -110
  158. package/src/compat/export/standalone.ts +62 -54
  159. package/src/compat/image-optimizer/cache.ts +51 -49
  160. package/src/compat/image-optimizer/detect.ts +52 -52
  161. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  162. package/src/compat/image-optimizer/source.ts +77 -80
  163. package/src/compat/lifecycle/after-scope.ts +26 -26
  164. package/src/compat/lifecycle/after.ts +48 -45
  165. package/src/compat/lifecycle/error-funnel.ts +98 -102
  166. package/src/compat/lifecycle/error-serialize.ts +30 -26
  167. package/src/compat/lifecycle/error-ui.ts +67 -32
  168. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  169. package/src/compat/lifecycle/instrumentation.ts +85 -85
  170. package/src/compat/lifecycle/node-console.ts +11 -11
  171. package/src/compat/lifecycle/testmode.ts +160 -132
  172. package/src/compat/mdx/compile.ts +60 -57
  173. package/src/compat/mdx/plugin.ts +11 -11
  174. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  175. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  176. package/src/compat/metadata.ts +75 -81
  177. package/src/compat/next/cache.ts +68 -69
  178. package/src/compat/next/canonical-url.ts +9 -9
  179. package/src/compat/next/client-cache.ts +19 -19
  180. package/src/compat/next/client-navigation.ts +112 -116
  181. package/src/compat/next/client-only.ts +1 -1
  182. package/src/compat/next/client-script.tsx +99 -93
  183. package/src/compat/next/client-server.ts +10 -10
  184. package/src/compat/next/config-loader.ts +176 -173
  185. package/src/compat/next/config.ts +7 -7
  186. package/src/compat/next/constants.cjs +6 -6
  187. package/src/compat/next/constants.ts +6 -6
  188. package/src/compat/next/custom-server.ts +26 -24
  189. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  190. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  191. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  192. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  193. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  194. package/src/compat/next/dynamic.tsx +21 -18
  195. package/src/compat/next/error.tsx +52 -52
  196. package/src/compat/next/font/cache.ts +74 -74
  197. package/src/compat/next/font/google.ts +2 -2
  198. package/src/compat/next/font/index.ts +1 -1
  199. package/src/compat/next/font/local.ts +3 -3
  200. package/src/compat/next/font/runtime-client.ts +17 -15
  201. package/src/compat/next/font/runtime.ts +443 -408
  202. package/src/compat/next/font/shared.ts +120 -108
  203. package/src/compat/next/form.tsx +63 -63
  204. package/src/compat/next/head.tsx +2 -2
  205. package/src/compat/next/headers.ts +103 -95
  206. package/src/compat/next/image/client.tsx +220 -0
  207. package/src/compat/next/image/config.ts +56 -58
  208. package/src/compat/next/image/optimizer.ts +40 -36
  209. package/src/compat/next/image/patterns.ts +37 -40
  210. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  211. package/src/compat/next/image/shared.ts +65 -57
  212. package/src/compat/next/image/static-metadata.ts +98 -107
  213. package/src/compat/next/image/validate.ts +79 -88
  214. package/src/compat/next/image.tsx +19 -23
  215. package/src/compat/next/index.ts +1 -1
  216. package/src/compat/next/legacy-image.tsx +59 -60
  217. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  218. package/src/compat/next/link.tsx +158 -158
  219. package/src/compat/next/navigation.cjs +12 -3
  220. package/src/compat/next/navigation.ts +48 -50
  221. package/src/compat/next/offline.ts +27 -27
  222. package/src/compat/next/og.ts +121 -124
  223. package/src/compat/next/preferred-region.ts +13 -14
  224. package/src/compat/next/redirects.ts +58 -56
  225. package/src/compat/next/resource-hints.ts +73 -76
  226. package/src/compat/next/rewrites.ts +130 -133
  227. package/src/compat/next/root-params.ts +45 -45
  228. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  229. package/src/compat/next/router.cjs +4 -2
  230. package/src/compat/next/router.ts +58 -61
  231. package/src/compat/next/script.tsx +108 -108
  232. package/src/compat/next/server-only.ts +1 -1
  233. package/src/compat/next/server.ts +19 -19
  234. package/src/compat/next/svgr.ts +18 -17
  235. package/src/compat/next/telemetry.ts +24 -24
  236. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  237. package/src/compat/next/user-agent.ts +53 -49
  238. package/src/compat/next/web-vitals.ts +22 -24
  239. package/src/compat/otel/api.ts +41 -41
  240. package/src/compat/otel/client-trace-metadata.ts +25 -27
  241. package/src/compat/otel/fetch-span.ts +29 -29
  242. package/src/compat/otel/tracer.ts +331 -331
  243. package/src/compat/pages/api.ts +456 -0
  244. package/src/compat/pages/client-plugin.ts +36 -36
  245. package/src/compat/pages/router-state.ts +34 -34
  246. package/src/compat/pages/{index.ts → router.ts} +130 -135
  247. package/src/compat/ppr/io.ts +12 -12
  248. package/src/compat/ppr/missing-root-params.ts +34 -38
  249. package/src/compat/ppr/root-params-scan.ts +66 -66
  250. package/src/compat/ppr/root-params-transform.ts +24 -26
  251. package/src/compat/ppr/root-params.ts +30 -30
  252. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  253. package/src/compat/protocol.ts +71 -70
  254. package/src/compat/react/action-state.ts +47 -48
  255. package/src/compat/react/client-lite.ts +15 -15
  256. package/src/compat/react/client.ts +4 -4
  257. package/src/compat/react/compiler-runtime.ts +11 -11
  258. package/src/compat/react/dom-client.ts +44 -44
  259. package/src/compat/react/dom-react-server.ts +10 -16
  260. package/src/compat/react/dom-server.ts +10 -10
  261. package/src/compat/react/dom.ts +52 -52
  262. package/src/compat/react/hooks-extra.ts +34 -35
  263. package/src/compat/react/parity.ts +64 -61
  264. package/src/compat/react/preact.ts +81 -82
  265. package/src/compat/react/react-server.ts +28 -28
  266. package/src/compat/react/router-shim.ts +1 -1
  267. package/src/compat/react/server-component-use.ts +8 -8
  268. package/src/compat/react/server-inserted-html.ts +30 -31
  269. package/src/compat/react/server.ts +53 -55
  270. package/src/compat/react/use.ts +32 -32
  271. package/src/compat/react/view-transition.ts +20 -20
  272. package/src/compat/register/actions.ts +35 -824
  273. package/src/compat/register/boot.ts +41 -41
  274. package/src/compat/register/build-tier.ts +5 -5
  275. package/src/compat/register/build.ts +74 -70
  276. package/src/compat/register/bundler.ts +161 -156
  277. package/src/compat/register/cache.ts +20 -20
  278. package/src/compat/register/client-errors.ts +3 -3
  279. package/src/compat/register/config.ts +6 -6
  280. package/src/compat/register/css-extras.ts +30 -34
  281. package/src/compat/register/edge-runtime.ts +3 -3
  282. package/src/compat/register/errors.ts +10 -12
  283. package/src/compat/register/export.ts +16 -16
  284. package/src/compat/register/font.ts +11 -11
  285. package/src/compat/register/hooks.ts +2 -2
  286. package/src/compat/register/image.ts +40 -40
  287. package/src/compat/register/index.ts +59 -62
  288. package/src/compat/register/instrumentation-client.ts +10 -10
  289. package/src/compat/register/lifecycle.ts +25 -28
  290. package/src/compat/register/mdx.ts +10 -10
  291. package/src/compat/register/middleware.ts +226 -16
  292. package/src/compat/register/otel.ts +80 -88
  293. package/src/compat/register/pages-api.ts +10 -463
  294. package/src/compat/register/ppr.ts +16 -16
  295. package/src/compat/register/protocol.ts +17 -18
  296. package/src/compat/register/proxy.ts +49 -51
  297. package/src/compat/register/render.ts +79 -76
  298. package/src/compat/register/routing.ts +162 -159
  299. package/src/compat/register/segment.ts +24 -1897
  300. package/src/compat/register/static-image.ts +3 -3
  301. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  302. package/src/compat/register/typed-routes.ts +14 -14
  303. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  304. package/src/compat/register/validation.ts +18 -18
  305. package/src/compat/segment/loading-boundary.ts +43 -45
  306. package/src/compat/segment/page-slot.ts +69 -69
  307. package/src/compat/segment/serve.ts +1884 -0
  308. package/src/compat/segment/tree.ts +113 -112
  309. package/src/compat/segment/vary-key.ts +38 -38
  310. package/src/compat/segment/vary-params.ts +138 -142
  311. package/src/compat/static-params.ts +14 -12
  312. package/src/compat/tsconfig-defaults.ts +87 -91
  313. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  314. package/src/compat/typecheck/worker.ts +15 -12
  315. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  316. package/src/compat/typed-routes/manifest.ts +174 -170
  317. package/src/compat/typed-routes/typegen.ts +127 -110
  318. package/src/compat/validation/errors.ts +16 -19
  319. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  320. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  321. package/src/compat-bootstrap.ts +16 -16
  322. package/src/config.ts +86 -69
  323. package/src/css/build.ts +230 -227
  324. package/src/css/postcss.ts +79 -80
  325. package/src/css/worker.ts +14 -15
  326. package/src/dev/client-actions.ts +10 -10
  327. package/src/dev/client-chunk-store.ts +27 -27
  328. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  329. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  330. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  331. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  332. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  333. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  334. package/src/dev/server.ts +804 -820
  335. package/src/env.ts +46 -43
  336. package/src/extensions.ts +491 -478
  337. package/src/index.ts +8 -8
  338. package/src/internal.ts +20 -23
  339. package/src/{islands → render}/boundary-error.ts +3 -3
  340. package/src/render/hooks.ts +71 -71
  341. package/src/render/island-context.ts +14 -14
  342. package/src/render/metadata.ts +310 -310
  343. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  344. package/src/{ppr.ts → render/ppr.ts} +244 -245
  345. package/src/render/renderer.ts +2087 -2082
  346. package/src/render/resource-hints.ts +16 -17
  347. package/src/render/slots.tsx +224 -235
  348. package/src/{islands → render}/static-children.ts +9 -12
  349. package/src/{islands → render}/static-slots.ts +37 -37
  350. package/src/{cache/context.ts → request/cache.ts} +20 -20
  351. package/src/request/context.ts +107 -107
  352. package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
  353. package/src/resolve/engine.ts +90 -77
  354. package/src/resolve/imports.ts +475 -463
  355. package/src/resolve/scan-facts.ts +318 -296
  356. package/src/resolve/source-text.ts +37 -37
  357. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  358. package/src/routing/forwarded.ts +19 -19
  359. package/src/routing/handler.ts +84 -91
  360. package/src/routing/href.ts +87 -70
  361. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  362. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  363. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  364. package/src/routing/routes.ts +827 -815
  365. package/src/routing/slots.ts +164 -160
  366. package/src/runtime/loader.ts +954 -0
  367. package/src/{dev → runtime}/module-cache.ts +309 -287
  368. package/src/{dev → runtime}/module-generations.ts +9 -9
  369. package/src/{dev → runtime}/module-transform.ts +81 -72
  370. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  371. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  372. package/src/runtime/vendor.ts +428 -406
  373. package/src/styles.d.ts +9 -0
  374. package/src/types.ts +324 -335
  375. package/src/utils/ansi.ts +5 -5
  376. package/src/utils/{source.ts → code.ts} +15 -12
  377. package/src/utils/content-type.ts +3 -3
  378. package/src/utils/decode.ts +2 -2
  379. package/src/utils/dev-profile.ts +13 -13
  380. package/src/utils/error-log.ts +6 -6
  381. package/src/utils/esbuild.ts +18 -18
  382. package/src/utils/fs-cache.ts +13 -13
  383. package/src/utils/fs.ts +57 -49
  384. package/src/utils/html.ts +20 -24
  385. package/src/utils/native-require.ts +8 -8
  386. package/src/utils/serialize.ts +139 -146
  387. package/src/utils/verbose.ts +18 -18
  388. package/reference/overview.md +0 -35
  389. package/src/cli/analyze-print.ts +0 -181
  390. package/src/compat/middleware/manifest.ts +0 -210
  391. package/src/compat/next/image-client.tsx +0 -215
  392. package/src/compat/next/link-usage.ts +0 -29
  393. package/src/css/index.ts +0 -2
  394. package/src/render/index.ts +0 -1
  395. package/src/style-modules.d.ts +0 -9
@@ -7,14 +7,14 @@
7
7
  //
8
8
  // The graph the hash walks is read from a persisted index keyed by (mtime, size, content hash), so a
9
9
  // restart re-derives it with one stat per source instead of re-scanning and re-resolving every import.
10
- import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
11
- import { cachedExistsSync } from '../utils/fs-cache';
12
- import { mkdir, readFile, stat } from 'node:fs/promises';
13
- import { createHash, randomUUID } from 'node:crypto';
14
- import path from 'node:path';
15
- import { fileURLToPath } from 'node:url';
16
- import type { ResolvedConfig } from '../config';
17
- import { writeFileAtomic } from '../utils/fs';
10
+ import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'
11
+ import { cachedExistsSync } from '../utils/fs-cache'
12
+ import { mkdir, readFile, stat } from 'node:fs/promises'
13
+ import { createHash, randomUUID } from 'node:crypto'
14
+ import path from 'node:path'
15
+ import { fileURLToPath } from 'node:url'
16
+ import type { ResolvedConfig } from '../config'
17
+ import { writeFileAtomic } from '../utils/fs'
18
18
 
19
19
  /**
20
20
  * Kill switch for the request-head trims: the naming walk's synchronous source
@@ -22,7 +22,7 @@ import { writeFileAtomic } from '../utils/fs';
22
22
  */
23
23
  export function devHeadTrimEnabled(): boolean {
24
24
  // eslint-disable-next-line turbo/no-undeclared-env-vars
25
- return process.env.PNEXT_HEAD_TRIM !== '0';
25
+ return process.env.PNEXT_HEAD_TRIM !== '0'
26
26
  }
27
27
 
28
28
  // While a recursive fs watcher is reporting every save, the memoized graph is
@@ -31,27 +31,27 @@ export function devHeadTrimEnabled(): boolean {
31
31
  // fails. Per cache root, so other processes' caches (tests, one-off renders)
32
32
  // keep the stat-based freshness they rely on.
33
33
  // Bisect seam: PNEXT_DEV_WATCH_FRESHNESS=0 restores the per-request re-stat.
34
- const watcherFreshRoots = new Set<string>();
34
+ const watcherFreshRoots = new Set<string>()
35
35
 
36
36
  export function setDevWatcherFreshness(outPath: string, trusted: boolean) {
37
- if (trusted) watcherFreshRoots.add(cacheRoot(outPath));
38
- else watcherFreshRoots.delete(cacheRoot(outPath));
37
+ if (trusted) watcherFreshRoots.add(cacheRoot(outPath))
38
+ else watcherFreshRoots.delete(cacheRoot(outPath))
39
39
  }
40
40
 
41
41
  function watcherFreshnessTrusted(root: string) {
42
42
  // eslint-disable-next-line turbo/no-undeclared-env-vars
43
- return watcherFreshRoots.has(root) && process.env.PNEXT_DEV_WATCH_FRESHNESS !== '0';
43
+ return watcherFreshRoots.has(root) && process.env.PNEXT_DEV_WATCH_FRESHNESS !== '0'
44
44
  }
45
45
 
46
46
  /** Files whose bytes are not source: hashed from stat, never read or scanned. */
47
- const BINARY_SOURCE = /\.(?:png|jpe?g|gif|webp|avif|ico|bmp|woff2?|ttf|otf|eot|mp[34]|webm|pdf)$/i;
47
+ const BINARY_SOURCE = /\.(?:png|jpe?g|gif|webp|avif|ico|bmp|woff2?|ttf|otf|eot|mp[34]|webm|pdf)$/i
48
48
 
49
- const MISSING = 'missing';
49
+ const MISSING = 'missing'
50
50
 
51
51
  // The framework's own package root. Compat runtime modules (next/form and
52
52
  // friends) are compiled like app sources but live outside the workspace, so
53
53
  // they need their own relocation anchor.
54
- const frameworkRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
54
+ const frameworkRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..')
55
55
 
56
56
  /**
57
57
  * A source's identity for hashing and for the persisted index - relative to the workspace (or the
@@ -64,123 +64,129 @@ const frameworkRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)),
64
64
  // Two `path.relative` calls per source per lookup, and every graph hash asks for its whole closure.
65
65
  // The answer is a pure function of the pair and the set of sources is bounded by the app, so it is
66
66
  // simply remembered.
67
- const portablePaths = new Map<string, string>();
67
+ const portablePaths = new Map<string, string>()
68
68
  // Same bisect seam as the naming walk it serves (PNEXT_DEV_GRAPH_FAST=0).
69
69
  // eslint-disable-next-line turbo/no-undeclared-env-vars
70
- const memoPortablePaths = process.env.PNEXT_DEV_GRAPH_FAST !== '0';
70
+ const memoPortablePaths = process.env.PNEXT_DEV_GRAPH_FAST !== '0'
71
71
 
72
72
  function portableSourcePath(file: string, workspaceRoot: string) {
73
- if (!memoPortablePaths) return computePortableSourcePath(file, workspaceRoot);
74
- const memoKey = `${workspaceRoot}\0${file}`;
75
- const memoized = portablePaths.get(memoKey);
76
- if (memoized !== undefined) return memoized;
77
- const computed = computePortableSourcePath(file, workspaceRoot);
78
- portablePaths.set(memoKey, computed);
79
- return computed;
73
+ if (!memoPortablePaths) return computePortableSourcePath(file, workspaceRoot)
74
+ const memoKey = `${workspaceRoot}\0${file}`
75
+ const memoized = portablePaths.get(memoKey)
76
+ if (memoized !== undefined) return memoized
77
+ const computed = computePortableSourcePath(file, workspaceRoot)
78
+ portablePaths.set(memoKey, computed)
79
+ return computed
80
80
  }
81
81
 
82
82
  function computePortableSourcePath(file: string, workspaceRoot: string) {
83
- const inWorkspace = path.relative(workspaceRoot, file);
83
+ const inWorkspace = path.relative(workspaceRoot, file)
84
84
  if (inWorkspace && !inWorkspace.startsWith('..') && !path.isAbsolute(inWorkspace)) {
85
- return `w:${toPosix(inWorkspace)}`;
85
+ return `w:${toPosix(inWorkspace)}`
86
86
  }
87
- const inFramework = path.relative(frameworkRoot, file);
87
+ const inFramework = path.relative(frameworkRoot, file)
88
88
  if (inFramework && !inFramework.startsWith('..') && !path.isAbsolute(inFramework)) {
89
- return `f:${toPosix(inFramework)}`;
89
+ return `f:${toPosix(inFramework)}`
90
90
  }
91
- return `a:${toPosix(file)}`;
91
+ return `a:${toPosix(file)}`
92
92
  }
93
93
 
94
94
  /** Inverse of `portableSourcePath` for the roots this process is running under. */
95
95
  function absoluteSourcePath(key: string, workspaceRoot: string) {
96
- const value = key.slice(2).split('/').join(path.sep);
97
- if (key.startsWith('w:')) return path.resolve(workspaceRoot, value);
98
- if (key.startsWith('f:')) return path.resolve(frameworkRoot, value);
99
- return value;
96
+ const value = key.slice(2).split('/').join(path.sep)
97
+ if (key.startsWith('w:')) return path.resolve(workspaceRoot, value)
98
+ if (key.startsWith('f:')) return path.resolve(frameworkRoot, value)
99
+ return value
100
100
  }
101
101
 
102
- const toPosix = (file: string) => (path.sep === '/' ? file : file.split(path.sep).join('/'));
102
+ const toPosix = (file: string) => (path.sep === '/' ? file : file.split(path.sep).join('/'))
103
103
 
104
104
  /** Stable identity for a source outside the dev cache — see `portableSourcePath`. */
105
105
  export function devSourceIdentity(file: string, workspaceRoot: string) {
106
- return portableSourcePath(file, workspaceRoot);
106
+ return portableSourcePath(file, workspaceRoot)
107
107
  }
108
108
 
109
109
  interface SourceRecord {
110
- mtimeMs: number;
111
- size: number;
110
+ mtimeMs: number
111
+ size: number
112
112
  /** Content hash (stat hash for binaries, `missing` when the file is gone). */
113
- srcHash: string;
113
+ srcHash: string
114
114
  /** Resolved local import targets — the same edges the compile walk follows. */
115
- imports: string[];
115
+ imports: string[]
116
116
  /** Bare package specifiers, so a route's vendor demand is known before it compiles. */
117
- packages: string[];
117
+ packages: string[]
118
118
  /** Whether the source opens with `'use client'` — the layer boundary, recorded by the scan. */
119
- client: boolean;
119
+ client: boolean
120
120
  }
121
121
 
122
122
  export interface DevModuleCacheOptions {
123
123
  /** Local import targets, package specifiers and layer of `source`, as the compile walk sees them. */
124
- edges: (file: string, source: string) => { imports: string[]; packages: string[]; client: boolean };
124
+ edges: (
125
+ file: string,
126
+ source: string,
127
+ ) => { imports: string[]; packages: string[]; client: boolean }
125
128
  /** Everything outside the sources that changes output: aliases, defines, compat. */
126
- compileKey: string;
129
+ compileKey: string
127
130
  }
128
131
 
129
132
  /** `[file, mtimeMs, size, srcHash]` — one indexed source, as callers revalidate it. */
130
- export type DevGraphSource = [string, number, number, string];
133
+ export type DevGraphSource = [string, number, number, string]
131
134
 
132
135
  /** `srcHash` for a source outside the index — `missing` when it cannot be read. */
133
- export const DEV_SOURCE_MISSING = MISSING;
136
+ export const DEV_SOURCE_MISSING = MISSING
134
137
 
135
138
  /** Files whose `srcHash` comes from stat rather than content (see BINARY_SOURCE). */
136
139
  export function isBinaryDevSource(file: string): boolean {
137
- return BINARY_SOURCE.test(file);
140
+ return BINARY_SOURCE.test(file)
138
141
  }
139
142
 
140
143
  /** The `srcHash` rule for a text source, for caches revalidating indexed records. */
141
144
  export function devTextSourceHash(source: string): string {
142
- return Bun.hash(source).toString(36);
145
+ return Bun.hash(source).toString(36)
143
146
  }
144
147
 
145
148
  export interface DevModuleCache {
146
149
  /** Hash naming `file`'s artifact: its own source plus its whole source graph. */
147
- graphHash(file: string): Promise<string>;
150
+ graphHash(file: string): Promise<string>
148
151
  /** Everything the last `graphHash` of these files walked, as indexed records. */
149
- graphSources(files: string[]): Promise<DevGraphSource[]>;
152
+ graphSources(files: string[]): Promise<DevGraphSource[]>
150
153
  /** Everything outside the sources that names an artifact — compat, aliases, pnext itself. */
151
- readonly graphKey: string;
154
+ readonly graphKey: string
152
155
  /** Forget the given sources and every module that reaches them (its dependents). */
153
- invalidate(files: Iterable<string>): void;
156
+ invalidate(files: Iterable<string>): void
154
157
  /** Whether this source is already part of the compiled graph. */
155
- knows(file: string): boolean;
158
+ knows(file: string): boolean
156
159
  /**
157
160
  * Whether this source opens with `'use client'`, from the record the walk already scanned - the
158
161
  * layer-aware seed used to re-read and re-scan every source of the route to learn the same thing, on the
159
162
  * request the response is blocked on.
160
163
  */
161
- isClientSource(file: string): Promise<boolean>;
164
+ isClientSource(file: string): Promise<boolean>
162
165
  /**
163
166
  * Every package specifier reachable from `file` - the route's vendor demand. With `prune`, a SEPARATE
164
167
  * non-memoized walk that skips pruned files and their subtrees (layer-aware seeding); `closure()` keeps
165
168
  * its superset for invalidation.
166
169
  */
167
- packageDemand(file: string, prune?: (file: string) => boolean | Promise<boolean>): Promise<string[]>;
170
+ packageDemand(
171
+ file: string,
172
+ prune?: (file: string) => boolean | Promise<boolean>,
173
+ ): Promise<string[]>
168
174
  /**
169
175
  * Freeze names for the duration of one compile pass: each source is re-checked once when the pass first
170
176
  * touches it, never again while it runs. A hash that moved mid-pass would let a module bake an import
171
177
  * href its target is then never written to - a dangling import Bun caches for the life of the process.
172
178
  */
173
- hold(): () => void;
179
+ hold(): () => void
174
180
  /** Directory the compiled artifacts live in. */
175
- readonly root: string;
181
+ readonly root: string
176
182
  }
177
183
 
178
- const caches = new Map<string, DevModuleCache>();
179
- const persistFlushes = new Set<() => void>();
184
+ const caches = new Map<string, DevModuleCache>()
185
+ const persistFlushes = new Set<() => void>()
180
186
 
181
187
  /** Write every cache's pending graph.json now. A finished build must leave no unref'd timer renaming temp files under .pnext. */
182
188
  export function flushDevModuleCaches() {
183
- for (const flushNow of persistFlushes) flushNow();
189
+ for (const flushNow of persistFlushes) flushNow()
184
190
  }
185
191
 
186
192
  /** The cache for `config.outPath`, created once per process. */
@@ -188,16 +194,16 @@ export function devModuleCache(
188
194
  config: ResolvedConfig,
189
195
  options: DevModuleCacheOptions,
190
196
  ): DevModuleCache {
191
- const root = cacheRoot(config.outPath);
192
- const existing = caches.get(root);
193
- if (existing) return existing;
194
- const created = createDevModuleCache(root, config.workspaceRoot, options);
195
- caches.set(root, created);
196
- return created;
197
+ const root = cacheRoot(config.outPath)
198
+ const existing = caches.get(root)
199
+ if (existing) return existing
200
+ const created = createDevModuleCache(root, config.workspaceRoot, options)
201
+ caches.set(root, created)
202
+ return created
197
203
  }
198
204
 
199
205
  export function cacheRoot(outPath: string) {
200
- return path.join(outPath, 'cache', 'server');
206
+ return path.join(outPath, 'cache', 'server')
201
207
  }
202
208
 
203
209
  function createDevModuleCache(
@@ -205,19 +211,19 @@ function createDevModuleCache(
205
211
  workspaceRoot: string,
206
212
  options: DevModuleCacheOptions,
207
213
  ): DevModuleCache {
208
- const indexFile = path.join(root, 'graph.json');
214
+ const indexFile = path.join(root, 'graph.json')
209
215
  // Compiler-generated sources — pnext's own output tree and the materialized
210
216
  // pages-compat app — are rewritten from scratch on every boot and can land at
211
217
  // a fresh path each time; they are hashed by content alone (below).
212
- const outRoot = `${path.dirname(path.dirname(root))}${path.sep}`;
218
+ const outRoot = `${path.dirname(path.dirname(root))}${path.sep}`
213
219
  const generated = (file: string) =>
214
- file.startsWith(outRoot) || file.includes(`${path.sep}pnext-pages-compat${path.sep}`);
220
+ file.startsWith(outRoot) || file.includes(`${path.sep}pnext-pages-compat${path.sep}`)
215
221
  const key = createHash('sha256')
216
222
  .update(`${options.compileKey}\0${frameworkFingerprint()}`)
217
223
  .digest('hex')
218
- .slice(0, 16);
219
- establishCacheTrust(root);
220
- const records = indexRecords(readIndexFile(indexFile), key, workspaceRoot);
224
+ .slice(0, 16)
225
+ establishCacheTrust(root)
226
+ const records = indexRecords(readIndexFile(indexFile), key, workspaceRoot)
221
227
  // PNEXT_DEV_CACHE_DEBUG=1 names every source whose hash differs from the one
222
228
  // the last boot persisted — i.e. exactly why an artifact was renamed and
223
229
  // recompiled instead of reused.
@@ -225,8 +231,8 @@ function createDevModuleCache(
225
231
  // eslint-disable-next-line turbo/no-undeclared-env-vars
226
232
  process.env.PNEXT_DEV_CACHE_DEBUG && records.size > 0
227
233
  ? new Map([...records].map(([file, record]) => [file, record.srcHash] as const))
228
- : undefined;
229
- const pending = new Map<string, Promise<SourceRecord>>();
234
+ : undefined
235
+ const pending = new Map<string, Promise<SourceRecord>>()
230
236
  // Records already re-checked in the current pass - what `hold()` promises ("re-checked once when
231
237
  // the pass first touches it, never again while it runs") and what `pending` alone does not
232
238
  // deliver: it drops each entry the moment the stat settles, so naming a route re-stat'ed its whole
@@ -234,34 +240,34 @@ function createDevModuleCache(
234
240
  // Bisect seam: `PNEXT_DEV_GRAPH_FAST=0` restores the previous cost model - re-stat every touch,
235
241
  // one module per turn, no path memo.
236
242
  // eslint-disable-next-line turbo/no-undeclared-env-vars
237
- const graphFast = process.env.PNEXT_DEV_GRAPH_FAST !== '0';
238
- const settledThisPass = new Map<string, Promise<SourceRecord>>();
239
- const refreshedThisPass = new Set<string>();
240
- let passes = 0;
241
- const closures = new Map<string, Set<string>>();
242
- const hashes = new Map<string, Promise<string>>();
243
- let dirty = false;
244
- let flush: Timer | undefined;
245
- let retries = 0;
243
+ const graphFast = process.env.PNEXT_DEV_GRAPH_FAST !== '0'
244
+ const settledThisPass = new Map<string, Promise<SourceRecord>>()
245
+ const refreshedThisPass = new Set<string>()
246
+ let passes = 0
247
+ const closures = new Map<string, Set<string>>()
248
+ const hashes = new Map<string, Promise<string>>()
249
+ let dirty = false
250
+ let flush: Timer | undefined
251
+ let retries = 0
246
252
 
247
253
  function persist() {
248
- if (flush) return;
254
+ if (flush) return
249
255
  flush = setTimeout(async () => {
250
- flush = undefined;
251
- if (!dirty) return;
252
- const contents = serializeIndex(key, records, workspaceRoot);
256
+ flush = undefined
257
+ if (!dirty) return
258
+ const contents = serializeIndex(key, records, workspaceRoot)
253
259
  try {
254
- await mkdir(root, { recursive: true });
255
- await writeFileAtomic(indexFile, contents);
256
- dirty = false;
260
+ await mkdir(root, { recursive: true })
261
+ await writeFileAtomic(indexFile, contents)
262
+ dirty = false
257
263
  } catch {
258
264
  // A write that lost a race with a cache wipe must not look like a
259
265
  // persisted index: keep `dirty` and try again a couple of times.
260
- if (++retries < 3) persist();
266
+ if (++retries < 3) persist()
261
267
  }
262
- }, 50);
268
+ }, 50)
263
269
  // The index is a pure optimization; never hold the process open for it.
264
- flush.unref?.();
270
+ flush.unref?.()
265
271
  }
266
272
 
267
273
  // A short-lived process (a one-off compile, a build) can exit before the
@@ -270,35 +276,36 @@ function createDevModuleCache(
270
276
  // whoever copies or walks .pnext right after it returns (see flushDevModuleCaches).
271
277
  function flushPersistNow() {
272
278
  if (flush) {
273
- clearTimeout(flush);
274
- flush = undefined;
279
+ clearTimeout(flush)
280
+ flush = undefined
275
281
  }
276
- if (!dirty) return;
277
- dirty = false;
282
+ if (!dirty) return
283
+ dirty = false
278
284
  try {
279
- writeFileSync(indexFile, serializeIndex(key, records, workspaceRoot));
285
+ writeFileSync(indexFile, serializeIndex(key, records, workspaceRoot))
280
286
  } catch {
281
287
  // Best effort: a missing index only costs the next boot a rescan.
282
288
  }
283
289
  }
284
- persistFlushes.add(flushPersistNow);
285
- process.on('exit', flushPersistNow);
290
+ persistFlushes.add(flushPersistNow)
291
+ process.on('exit', flushPersistNow)
286
292
 
287
293
  async function sourceRecord(file: string): Promise<SourceRecord> {
288
- const inflight = pending.get(file) ?? (graphFast && passes > 0 ? settledThisPass.get(file) : undefined);
289
- if (inflight) return inflight;
290
- const previous = records.get(file);
294
+ const inflight =
295
+ pending.get(file) ?? (graphFast && passes > 0 ? settledThisPass.get(file) : undefined)
296
+ if (inflight) return inflight
297
+ const previous = records.get(file)
291
298
  const next = readSourceRecord(file, previous, options.edges).then(record => {
292
- pending.delete(file);
293
- if (record === previous) return record;
294
- records.set(file, record);
295
- dirty = true;
296
- persist();
297
- return record;
298
- });
299
- pending.set(file, next);
300
- if (graphFast && passes > 0) settledThisPass.set(file, next);
301
- return next;
299
+ pending.delete(file)
300
+ if (record === previous) return record
301
+ records.set(file, record)
302
+ dirty = true
303
+ persist()
304
+ return record
305
+ })
306
+ pending.set(file, next)
307
+ if (graphFast && passes > 0) settledThisPass.set(file, next)
308
+ return next
302
309
  }
303
310
 
304
311
  /**
@@ -307,208 +314,211 @@ function createDevModuleCache(
307
314
  * everywhere), and the disk is the only authority on what a module contains.
308
315
  */
309
316
  async function refresh(file: string) {
310
- if (watcherFreshnessTrusted(root)) return;
311
- const reachable = closures.get(file);
312
- if (!reachable) return;
313
- if (passes > 0 && refreshedThisPass.has(file)) return;
314
- refreshedThisPass.add(file);
315
- const changed: string[] = [];
317
+ if (watcherFreshnessTrusted(root)) return
318
+ const reachable = closures.get(file)
319
+ if (!reachable) return
320
+ if (passes > 0 && refreshedThisPass.has(file)) return
321
+ refreshedThisPass.add(file)
322
+ const changed: string[] = []
316
323
  await Promise.all(
317
324
  [...reachable].map(async source => {
318
- const previous = records.get(source);
319
- const record = await sourceRecord(source);
320
- if (previous?.srcHash !== record.srcHash) changed.push(source);
325
+ const previous = records.get(source)
326
+ const record = await sourceRecord(source)
327
+ if (previous?.srcHash !== record.srcHash) changed.push(source)
321
328
  }),
322
- );
323
- if (changed.length > 0) forget(changed);
329
+ )
330
+ if (changed.length > 0) forget(changed)
324
331
  }
325
332
 
326
333
  /** Drop the memoized graph of every module that reaches one of `changed`. */
327
334
  function forget(changed: string[]) {
328
335
  for (const [source, reachable] of closures) {
329
- if (!changed.some(target => reachable.has(target))) continue;
330
- closures.delete(source);
331
- hashes.delete(source);
336
+ if (!changed.some(target => reachable.has(target))) continue
337
+ closures.delete(source)
338
+ hashes.delete(source)
332
339
  }
333
340
  }
334
341
 
335
342
  /** The pre-batching walk and hash gather, kept behind PNEXT_DEV_GRAPH_FAST=0. */
336
343
  async function serialFrontier(frontier: string[], file: string) {
337
- const reached: { expand: boolean; targets: Iterable<string> }[] = [];
344
+ const reached: { expand: boolean; targets: Iterable<string> }[] = []
338
345
  for (const current of frontier) {
339
- const known = current === file ? undefined : closures.get(current);
346
+ const known = current === file ? undefined : closures.get(current)
340
347
  reached.push(
341
348
  known
342
349
  ? { expand: false, targets: known as Iterable<string> }
343
350
  : { expand: true, targets: (await sourceRecord(current)).imports },
344
- );
351
+ )
345
352
  }
346
- return reached;
353
+ return reached
347
354
  }
348
355
 
349
356
  async function serialRecords(reachable: string[]) {
350
- const records: SourceRecord[] = [];
351
- for (const source of reachable) records.push(await sourceRecord(source));
352
- return records;
357
+ const records: SourceRecord[] = []
358
+ for (const source of reachable) records.push(await sourceRecord(source))
359
+ return records
353
360
  }
354
361
 
355
362
  /** Every source reachable from `file`, itself included. Cycle-safe by construction. */
356
363
  async function closure(file: string): Promise<Set<string>> {
357
- const cached = closures.get(file);
358
- if (cached) return cached;
359
- const seen = new Set<string>([file]);
364
+ const cached = closures.get(file)
365
+ if (cached) return cached
366
+ const seen = new Set<string>([file])
360
367
  // A whole frontier per turn, not one module per turn: the walk is pure I/O latency (one stat per
361
368
  // module), so taking it in series turns a route graph into that many sequential round trips, on
362
369
  // the request the response is blocked on.
363
- let frontier = [file];
370
+ let frontier = [file]
364
371
  while (frontier.length > 0) {
365
372
  const reached = graphFast
366
373
  ? await Promise.all(
367
- frontier.map(async current => {
368
- // A complete reachable set stays complete however it was reached, so a
369
- // memoized child is merged instead of re-expanded.
370
- const known = current === file ? undefined : closures.get(current);
371
- return known
372
- ? { expand: false, targets: known as Iterable<string> }
373
- : { expand: true, targets: (await sourceRecord(current)).imports };
374
- }),
374
+ frontier.map(async current => {
375
+ // A complete reachable set stays complete however it was reached, so a
376
+ // memoized child is merged instead of re-expanded.
377
+ const known = current === file ? undefined : closures.get(current)
378
+ return known
379
+ ? { expand: false, targets: known as Iterable<string> }
380
+ : { expand: true, targets: (await sourceRecord(current)).imports }
381
+ }),
375
382
  )
376
- : await serialFrontier(frontier, file);
377
- const next: string[] = [];
383
+ : await serialFrontier(frontier, file)
384
+ const next: string[] = []
378
385
  for (const { expand, targets } of reached) {
379
386
  for (const target of targets) {
380
- if (seen.has(target)) continue;
381
- seen.add(target);
382
- if (expand) next.push(target);
387
+ if (seen.has(target)) continue
388
+ seen.add(target)
389
+ if (expand) next.push(target)
383
390
  }
384
391
  }
385
- frontier = next;
392
+ frontier = next
386
393
  }
387
- closures.set(file, seen);
388
- return seen;
394
+ closures.set(file, seen)
395
+ return seen
389
396
  }
390
397
 
391
398
  async function computeGraphHash(file: string) {
392
- const reachable = [...(await closure(file))].sort();
399
+ const reachable = [...(await closure(file))].sort()
393
400
  // Same reason as the walk above: gather every record first, then fold them
394
401
  // in sorted order, instead of one awaited stat between each hash update.
395
402
  const records: SourceRecord[] = graphFast
396
403
  ? await Promise.all(reachable.map(source => sourceRecord(source)))
397
- : await serialRecords(reachable);
398
- const hash = createHash('sha256').update(key).update('\0');
399
- const moved: string[] = [];
404
+ : await serialRecords(reachable)
405
+ const hash = createHash('sha256').update(key).update('\0')
406
+ const moved: string[] = []
400
407
  for (const [index, source] of reachable.entries()) {
401
- const record = records[index]!;
408
+ const record = records[index]!
402
409
  // A generated module (materialized pages wrapper, bundled config) can
403
410
  // live at a path that changes between boots while its content does not;
404
411
  // naming it by content alone keeps its dependents' names stable.
405
- hash.update(generated(source) ? '' : portableSourcePath(source, workspaceRoot));
406
- hash.update('\0');
407
- hash.update(record.srcHash);
408
- hash.update('\0');
409
- if (booted && booted.get(source) !== record.srcHash) moved.push(source);
412
+ hash.update(generated(source) ? '' : portableSourcePath(source, workspaceRoot))
413
+ hash.update('\0')
414
+ hash.update(record.srcHash)
415
+ hash.update('\0')
416
+ if (booted && booted.get(source) !== record.srcHash) moved.push(source)
410
417
  }
411
418
  if (booted && moved.length > 0) {
412
419
  console.log(
413
420
  `dev-cache ${path.basename(file)} renamed: ${moved.length} source(s) differ from the persisted graph\n ${moved
414
421
  .slice(0, 10)
415
422
  .join('\n ')}`,
416
- );
423
+ )
417
424
  }
418
- return hash.digest('hex').slice(0, 16);
425
+ return hash.digest('hex').slice(0, 16)
419
426
  }
420
427
 
421
428
  return {
422
429
  root,
423
430
  graphKey: key,
424
431
  async graphSources(files) {
425
- const reachable = new Set<string>();
426
- for (const file of files) for (const source of await closure(path.resolve(file))) reachable.add(source);
432
+ const reachable = new Set<string>()
433
+ for (const file of files)
434
+ for (const source of await closure(path.resolve(file))) reachable.add(source)
427
435
  return Promise.all(
428
436
  [...reachable].sort().map(async (source): Promise<DevGraphSource> => {
429
- const record = await sourceRecord(source);
430
- return [source, record.mtimeMs, record.size, record.srcHash];
437
+ const record = await sourceRecord(source)
438
+ return [source, record.mtimeMs, record.size, record.srcHash]
431
439
  }),
432
- );
440
+ )
433
441
  },
434
442
  async graphHash(file) {
435
- const resolved = path.resolve(file);
436
- await refresh(resolved);
437
- const existing = hashes.get(resolved);
438
- if (existing) return existing;
443
+ const resolved = path.resolve(file)
444
+ await refresh(resolved)
445
+ const existing = hashes.get(resolved)
446
+ if (existing) return existing
439
447
  const next = computeGraphHash(resolved).catch(error => {
440
- hashes.delete(resolved);
441
- throw error;
442
- });
443
- hashes.set(resolved, next);
444
- return next;
448
+ hashes.delete(resolved)
449
+ throw error
450
+ })
451
+ hashes.set(resolved, next)
452
+ return next
445
453
  },
446
454
  knows(file) {
447
- return records.has(path.resolve(file));
455
+ return records.has(path.resolve(file))
448
456
  },
449
457
  async isClientSource(file) {
450
- return (await sourceRecord(path.resolve(file))).client;
458
+ return (await sourceRecord(path.resolve(file))).client
451
459
  },
452
460
  async packageDemand(file, prune) {
453
- const resolved = path.resolve(file);
454
- const demand = new Set<string>();
461
+ const resolved = path.resolve(file)
462
+ const demand = new Set<string>()
455
463
  if (!prune) {
456
464
  for (const source of await closure(resolved)) {
457
- for (const name of (await sourceRecord(source)).packages) demand.add(name);
465
+ for (const name of (await sourceRecord(source)).packages) demand.add(name)
458
466
  }
459
- return [...demand];
467
+ return [...demand]
460
468
  }
461
- const seen = new Set<string>([resolved]);
462
- let frontier = [resolved];
469
+ const seen = new Set<string>([resolved])
470
+ let frontier = [resolved]
463
471
  while (frontier.length > 0) {
464
472
  const kept = (
465
- await Promise.all(frontier.map(async source => ((await prune(source)) ? undefined : source)))
466
- ).filter((source): source is string => source !== undefined);
467
- const reached = await Promise.all(kept.map(source => sourceRecord(source)));
468
- const next: string[] = [];
473
+ await Promise.all(
474
+ frontier.map(async source => ((await prune(source)) ? undefined : source)),
475
+ )
476
+ ).filter((source): source is string => source !== undefined)
477
+ const reached = await Promise.all(kept.map(source => sourceRecord(source)))
478
+ const next: string[] = []
469
479
  for (const record of reached) {
470
- for (const name of record.packages) demand.add(name);
480
+ for (const name of record.packages) demand.add(name)
471
481
  for (const target of record.imports) {
472
- if (seen.has(target)) continue;
473
- seen.add(target);
474
- next.push(target);
482
+ if (seen.has(target)) continue
483
+ seen.add(target)
484
+ next.push(target)
475
485
  }
476
486
  }
477
- frontier = next;
487
+ frontier = next
478
488
  }
479
- return [...demand];
489
+ return [...demand]
480
490
  },
481
491
  hold() {
482
492
  if (passes++ === 0) {
483
- refreshedThisPass.clear();
484
- settledThisPass.clear();
493
+ refreshedThisPass.clear()
494
+ settledThisPass.clear()
485
495
  }
486
- let released = false;
496
+ let released = false
487
497
  return () => {
488
- if (released) return;
489
- released = true;
498
+ if (released) return
499
+ released = true
490
500
  // Outside a pass the disk is the only authority again, so nothing may
491
501
  // survive the last release.
492
- if (--passes === 0) settledThisPass.clear();
493
- };
502
+ if (--passes === 0) settledThisPass.clear()
503
+ }
494
504
  },
495
505
  invalidate(files) {
496
- const changed = new Set([...files].map(file => path.resolve(file)));
497
- if (changed.size === 0) return;
506
+ const changed = new Set([...files].map(file => path.resolve(file)))
507
+ if (changed.size === 0) return
498
508
  for (const file of changed) {
499
- records.delete(file);
500
- pending.delete(file);
509
+ records.delete(file)
510
+ pending.delete(file)
501
511
  // A save lands mid-pass often enough to matter: the pass must not go on
502
512
  // reusing the record the watcher just invalidated.
503
- settledThisPass.delete(file);
504
- dirty = true;
513
+ settledThisPass.delete(file)
514
+ dirty = true
505
515
  }
506
516
  // Dependents are exactly the modules whose reachable set holds a changed
507
517
  // file: drop their memoized graph, keep every unrelated module's.
508
- forget([...changed]);
509
- persist();
518
+ forget([...changed])
519
+ persist()
510
520
  },
511
- };
521
+ }
512
522
  }
513
523
 
514
524
  const missingRecord = (): SourceRecord => ({
@@ -518,7 +528,7 @@ const missingRecord = (): SourceRecord => ({
518
528
  imports: [],
519
529
  packages: [],
520
530
  client: false,
521
- });
531
+ })
522
532
 
523
533
  // Sync reads skip the fs thread-pool queue the vendor preloads own - the walk is cheap standing
524
534
  // alone but slow once queued behind them.
@@ -527,15 +537,21 @@ async function readSourceRecord(
527
537
  previous: SourceRecord | undefined,
528
538
  edges: DevModuleCacheOptions['edges'],
529
539
  ): Promise<SourceRecord> {
530
- const sync = devHeadTrimEnabled();
540
+ const sync = devHeadTrimEnabled()
531
541
  const stats = sync
532
- ? (() => { try { return statSync(file); } catch { return undefined; } })()
533
- : await stat(file).catch(() => undefined);
534
- if (!stats) return missingRecord();
542
+ ? (() => {
543
+ try {
544
+ return statSync(file)
545
+ } catch {
546
+ return undefined
547
+ }
548
+ })()
549
+ : await stat(file).catch(() => undefined)
550
+ if (!stats) return missingRecord()
535
551
  // Fast path: an untouched file keeps the indexed hash and edge list, so a
536
552
  // restart pays one stat per source instead of a read + scan + resolve.
537
553
  if (previous?.mtimeMs === stats.mtimeMs && previous.size === stats.size) {
538
- return previous;
554
+ return previous
539
555
  }
540
556
  if (isBinaryDevSource(file)) {
541
557
  return {
@@ -545,16 +561,22 @@ async function readSourceRecord(
545
561
  imports: [],
546
562
  packages: [],
547
563
  client: false,
548
- };
564
+ }
549
565
  }
550
566
  const source = sync
551
- ? (() => { try { return readFileSync(file, 'utf8'); } catch { return undefined; } })()
552
- : await readFile(file, 'utf8').catch(() => undefined);
553
- if (source === undefined) return missingRecord();
554
- const srcHash = devTextSourceHash(source);
567
+ ? (() => {
568
+ try {
569
+ return readFileSync(file, 'utf8')
570
+ } catch {
571
+ return undefined
572
+ }
573
+ })()
574
+ : await readFile(file, 'utf8').catch(() => undefined)
575
+ if (source === undefined) return missingRecord()
576
+ const srcHash = devTextSourceHash(source)
555
577
  // Content decides: a touched-but-unchanged file keeps its edges (and so its
556
578
  // dependents keep their names) even though its mtime moved.
557
- const scanned = previous?.srcHash === srcHash ? previous : edges(file, source);
579
+ const scanned = previous?.srcHash === srcHash ? previous : edges(file, source)
558
580
  return {
559
581
  mtimeMs: stats.mtimeMs,
560
582
  size: stats.size,
@@ -562,7 +584,7 @@ async function readSourceRecord(
562
584
  imports: scanned.imports,
563
585
  packages: scanned.packages,
564
586
  client: scanned.client,
565
- };
587
+ }
566
588
  }
567
589
 
568
590
  // --------------------------------------------------------------------------
@@ -575,13 +597,13 @@ async function readSourceRecord(
575
597
  // edges, the folder carries a uuid marker: while it still reads back as the value this process adopted at
576
598
  // boot, the folder was never wiped and every artifact still in it still has its edges. Once it does not -
577
599
  // or once there is no marker at all - nothing on disk is trusted until this process rewrites it.
578
- const markers = new Map<string, string>();
579
- const wiped = new Set<string>();
580
- const written = new Set<string>();
581
- const markerChecks = new Map<string, number>();
600
+ const markers = new Map<string, string>()
601
+ const wiped = new Set<string>()
602
+ const written = new Set<string>()
603
+ const markerChecks = new Map<string, number>()
582
604
 
583
605
  function markerFile(root: string) {
584
- return path.join(root, '.cache-id');
606
+ return path.join(root, '.cache-id')
585
607
  }
586
608
 
587
609
  /**
@@ -590,28 +612,28 @@ function markerFile(root: string) {
590
612
  * there still has its edges; a folder without one starts untrusted.
591
613
  */
592
614
  function establishCacheTrust(root: string) {
593
- const current = readMarker(root);
594
- const value = current ?? randomUUID();
615
+ const current = readMarker(root)
616
+ const value = current ?? randomUUID()
595
617
  if (!current) {
596
- writeMarker(root, value);
597
- wiped.add(root);
618
+ writeMarker(root, value)
619
+ wiped.add(root)
598
620
  }
599
- markers.set(root, value);
600
- return value;
621
+ markers.set(root, value)
622
+ return value
601
623
  }
602
624
 
603
625
  function readMarker(root: string) {
604
626
  try {
605
- return readFileSync(markerFile(root), 'utf8').trim() || undefined;
627
+ return readFileSync(markerFile(root), 'utf8').trim() || undefined
606
628
  } catch {
607
- return undefined;
629
+ return undefined
608
630
  }
609
631
  }
610
632
 
611
633
  function writeMarker(root: string, value: string) {
612
634
  try {
613
- mkdirSync(root, { recursive: true });
614
- writeFileSync(markerFile(root), value);
635
+ mkdirSync(root, { recursive: true })
636
+ writeFileSync(markerFile(root), value)
615
637
  } catch {
616
638
  // Non-fatal: an unwritable marker only costs a recompile.
617
639
  }
@@ -622,25 +644,25 @@ function writeMarker(root: string, value: string) {
622
644
  * window it leaves is a wipe racing an in-flight compile, which recompiles into place anyway.
623
645
  */
624
646
  function cacheMarkerIntact(root: string) {
625
- const recorded = markers.get(root);
626
- if (recorded === undefined || wiped.has(root)) return false;
627
- const now = performance.now();
628
- if (now - (markerChecks.get(root) ?? -Infinity) < 100) return true;
629
- markerChecks.set(root, now);
630
- const current = readMarker(root);
631
- if (current === recorded) return true;
647
+ const recorded = markers.get(root)
648
+ if (recorded === undefined || wiped.has(root)) return false
649
+ const now = performance.now()
650
+ if (now - (markerChecks.get(root) ?? -Infinity) < 100) return true
651
+ markerChecks.set(root, now)
652
+ const current = readMarker(root)
653
+ if (current === recorded) return true
632
654
  // Wiped under us: re-establish the marker so the rebuilt cache is reusable
633
655
  // next boot, but distrust everything still on disk for the rest of this run.
634
- if (!current) writeMarker(root, recorded);
635
- else markers.set(root, current);
636
- wiped.add(root);
637
- return false;
656
+ if (!current) writeMarker(root, recorded)
657
+ else markers.set(root, current)
658
+ wiped.add(root)
659
+ return false
638
660
  }
639
661
 
640
662
  /** Record an artifact this process just wrote — trusted even after a wipe. */
641
663
  export function noteDevArtifactWritten(file: string) {
642
- const root = artifactCacheRoot(file);
643
- if (root && wiped.has(root)) written.add(file);
664
+ const root = artifactCacheRoot(file)
665
+ if (root && wiped.has(root)) written.add(file)
644
666
  }
645
667
 
646
668
  /**
@@ -649,18 +671,18 @@ export function noteDevArtifactWritten(file: string) {
649
671
  * module, per boot.
650
672
  */
651
673
  export function devArtifactUsable(file: string) {
652
- if (!cachedExistsSync(file)) return false;
653
- const root = artifactCacheRoot(file);
674
+ if (!cachedExistsSync(file)) return false
675
+ const root = artifactCacheRoot(file)
654
676
  // No dev module cache for this folder — a prod build writes compiled output
655
677
  // here too, and it owns the directory it just emptied.
656
- if (!root || !markers.has(root)) return true;
657
- return cacheMarkerIntact(root) || written.has(file);
678
+ if (!root || !markers.has(root)) return true
679
+ return cacheMarkerIntact(root) || written.has(file)
658
680
  }
659
681
 
660
682
  function artifactCacheRoot(file: string) {
661
- const marker = `${path.sep}cache${path.sep}server${path.sep}`;
662
- const index = file.lastIndexOf(marker);
663
- return index === -1 ? undefined : file.slice(0, index + marker.length - 1);
683
+ const marker = `${path.sep}cache${path.sep}server${path.sep}`
684
+ const index = file.lastIndexOf(marker)
685
+ return index === -1 ? undefined : file.slice(0, index + marker.length - 1)
664
686
  }
665
687
 
666
688
  // --------------------------------------------------------------------------
@@ -668,19 +690,19 @@ function artifactCacheRoot(file: string) {
668
690
  // --------------------------------------------------------------------------
669
691
 
670
692
  interface IndexFile {
671
- version: number;
672
- key: string;
673
- files: Record<string, [number, number, string, string[], string[], 0 | 1]>;
693
+ version: number
694
+ key: string
695
+ files: Record<string, [number, number, string, string[], string[], 0 | 1]>
674
696
  }
675
697
 
676
698
  /** Bumped whenever a record's shape changes; an older index is simply ignored. */
677
- const INDEX_VERSION = 4;
699
+ const INDEX_VERSION = 4
678
700
 
679
701
  function readIndexFile(file: string): IndexFile | undefined {
680
702
  try {
681
- return JSON.parse(readFileSync(file, 'utf8')) as IndexFile;
703
+ return JSON.parse(readFileSync(file, 'utf8')) as IndexFile
682
704
  } catch {
683
- return undefined;
705
+ return undefined
684
706
  }
685
707
  }
686
708
 
@@ -689,8 +711,8 @@ function readIndexFile(file: string): IndexFile | undefined {
689
711
  // Paths are stored portably (see `portableSourcePath`) so an index written on
690
712
  // the build machine still describes the same sources under the deployment root.
691
713
  function indexRecords(parsed: IndexFile | undefined, key: string, workspaceRoot: string) {
692
- const records = new Map<string, SourceRecord>();
693
- if (parsed?.key !== key || parsed.version !== INDEX_VERSION) return records;
714
+ const records = new Map<string, SourceRecord>()
715
+ if (parsed?.key !== key || parsed.version !== INDEX_VERSION) return records
694
716
  for (const [source, entry] of Object.entries(parsed.files ?? {})) {
695
717
  records.set(absoluteSourcePath(source, workspaceRoot), {
696
718
  mtimeMs: entry[0],
@@ -699,13 +721,13 @@ function indexRecords(parsed: IndexFile | undefined, key: string, workspaceRoot:
699
721
  imports: entry[3].map(file => absoluteSourcePath(file, workspaceRoot)),
700
722
  packages: entry[4] ?? [],
701
723
  client: entry[5] === 1,
702
- });
724
+ })
703
725
  }
704
- return records;
726
+ return records
705
727
  }
706
728
 
707
729
  function serializeIndex(key: string, records: Map<string, SourceRecord>, workspaceRoot: string) {
708
- const files: IndexFile['files'] = {};
730
+ const files: IndexFile['files'] = {}
709
731
  for (const [source, record] of records) {
710
732
  files[portableSourcePath(source, workspaceRoot)] = [
711
733
  record.mtimeMs,
@@ -714,9 +736,9 @@ function serializeIndex(key: string, records: Map<string, SourceRecord>, workspa
714
736
  record.imports.map(file => portableSourcePath(file, workspaceRoot)),
715
737
  record.packages,
716
738
  record.client ? 1 : 0,
717
- ];
739
+ ]
718
740
  }
719
- return JSON.stringify({ version: INDEX_VERSION, key, files } satisfies IndexFile);
741
+ return JSON.stringify({ version: INDEX_VERSION, key, files } satisfies IndexFile)
720
742
  }
721
743
 
722
744
  // pnext's own compiler sources shape every artifact, so editing the framework must invalidate the cache the
@@ -724,31 +746,31 @@ function serializeIndex(key: string, records: Map<string, SourceRecord>, workspa
724
746
  // whenever a file was merely touched (a reinstall, a checkout, a staged copy, a no-op save), so this reads
725
747
  // the files that actually emit compiled output. A few dozen small files, once per process, off the boot
726
748
  // path - the first compile pays it.
727
- let fingerprint: string | undefined;
749
+ let fingerprint: string | undefined
728
750
 
729
751
  function frameworkFingerprint() {
730
- if (fingerprint !== undefined) return fingerprint;
731
- const hash = createHash('sha256');
752
+ if (fingerprint !== undefined) return fingerprint
753
+ const hash = createHash('sha256')
732
754
  for (const dir of ['.', '..', '../runtime', '../compat/bundler', '../resolve']) {
733
- const root = new URL(`${dir}/`, import.meta.url).pathname;
734
- let entries: string[];
755
+ const root = new URL(`${dir}/`, import.meta.url).pathname
756
+ let entries: string[]
735
757
  try {
736
- entries = readdirSync(root).sort();
758
+ entries = readdirSync(root).sort()
737
759
  } catch {
738
- continue;
760
+ continue
739
761
  }
740
762
  for (const entry of entries) {
741
763
  // Declarations emit nothing, and a deployed build ships without them
742
764
  // (the vercel adapter drops `.d.ts`) — hashing one would key the cache to
743
765
  // a file that is provably absent where the artifacts are replayed.
744
- if (!entry.endsWith('.ts') || /\.d\.[cm]?ts$/.test(entry)) continue;
766
+ if (!entry.endsWith('.ts') || /\.d\.[cm]?ts$/.test(entry)) continue
745
767
  try {
746
- hash.update(`${entry}:${Bun.hash(readFileSync(path.join(root, entry))).toString(36)}\0`);
768
+ hash.update(`${entry}:${Bun.hash(readFileSync(path.join(root, entry))).toString(36)}\0`)
747
769
  } catch {
748
770
  // ignore unreadable entries
749
771
  }
750
772
  }
751
773
  }
752
- fingerprint = hash.digest('hex').slice(0, 16);
753
- return fingerprint;
774
+ fingerprint = hash.digest('hex').slice(0, 16)
775
+ return fingerprint
754
776
  }