@wular/pnext 0.0.3 → 0.0.6

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 (392) hide show
  1. package/README.md +11 -11
  2. package/bin/pnext +1 -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 +1 -1
  8. package/reference/config.md +2 -2
  9. package/reference/css.md +13 -9
  10. package/reference/dev.md +1 -1
  11. package/reference/metadata.md +7 -7
  12. package/reference/navigation.md +35 -28
  13. package/reference/performance.md +70 -70
  14. package/reference/rendering.md +17 -17
  15. package/reference/routing.md +17 -17
  16. package/reference/typegen.md +12 -8
  17. package/src/api/cache.ts +36 -37
  18. package/src/api/client-cache.ts +2 -2
  19. package/src/api/client-navigation.ts +113 -109
  20. package/src/api/dynamic.tsx +58 -55
  21. package/src/api/link.tsx +49 -52
  22. package/src/api/navigation.ts +59 -58
  23. package/src/api/server.ts +152 -144
  24. package/src/api/suspense.ts +4 -4
  25. package/src/cli/adapters/vercel-warm.ts +126 -121
  26. package/src/cli/adapters/vercel.ts +437 -443
  27. package/src/cli/analyze.ts +333 -144
  28. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  29. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  30. package/src/cli/build.ts +976 -978
  31. package/src/cli/create.ts +71 -63
  32. package/src/cli/dev.ts +116 -116
  33. package/src/cli/index.ts +88 -85
  34. package/src/cli/migrate/package-json.ts +73 -71
  35. package/src/cli/migrate/report.ts +32 -33
  36. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  37. package/src/cli/migrate/scan.ts +46 -46
  38. package/src/cli/migrate/spinner.ts +9 -9
  39. package/src/cli/migrate/tsconfig.ts +35 -35
  40. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  41. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  42. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  43. package/src/cli/start.ts +65 -66
  44. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  45. package/src/client/build.ts +794 -768
  46. package/src/client/chunk-fold.ts +245 -240
  47. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  48. package/src/client/entry.ts +162 -147
  49. package/src/client/prebuilt.ts +231 -223
  50. package/src/client/profile.ts +29 -29
  51. package/src/client/react-compiler.ts +20 -15
  52. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  53. package/src/client/reference-stub.ts +51 -51
  54. package/src/client/reference.ts +19 -19
  55. package/src/{api → client}/router/events.ts +17 -17
  56. package/src/{api → client}/router/history.ts +16 -16
  57. package/src/{api → client}/router/hub.ts +52 -53
  58. package/src/{api/router.ts → client/router/index.ts} +61 -60
  59. package/src/{api → client}/router/policies.ts +24 -24
  60. package/src/{api → client}/router/runtime.ts +1988 -1961
  61. package/src/{api → client}/router/types.ts +93 -98
  62. package/src/compat/actions/client-plugin.ts +42 -43
  63. package/src/compat/actions/client-stub.ts +14 -14
  64. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  65. package/src/compat/actions/config.ts +63 -64
  66. package/src/compat/actions/detect.ts +68 -68
  67. package/src/compat/actions/discovery.ts +105 -105
  68. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  69. package/src/compat/actions/early-submit.ts +1 -1
  70. package/src/compat/actions/endpoint.ts +198 -198
  71. package/src/compat/actions/extensions.ts +811 -0
  72. package/src/compat/actions/flight.ts +23 -23
  73. package/src/compat/actions/form-state.ts +34 -34
  74. package/src/compat/actions/hoist.ts +382 -236
  75. package/src/compat/actions/ids.ts +7 -12
  76. package/src/compat/actions/index.ts +8 -8
  77. package/src/compat/actions/instances.ts +46 -46
  78. package/src/compat/actions/origin.ts +47 -48
  79. package/src/compat/actions/protocol.ts +22 -22
  80. package/src/compat/actions/registry.ts +19 -19
  81. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  82. package/src/compat/actions/rewrite.ts +125 -125
  83. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  84. package/src/compat/actions/serve.ts +132 -136
  85. package/src/compat/actions/server-tag.ts +4 -4
  86. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  87. package/src/compat/actions/unrecognized-error.ts +4 -4
  88. package/src/compat/{index.ts → aliases.ts} +112 -109
  89. package/src/compat/bundler/bun-externals.ts +18 -18
  90. package/src/compat/bundler/cjs-exports.ts +271 -223
  91. package/src/compat/bundler/config.ts +116 -111
  92. package/src/compat/bundler/externals.ts +12 -12
  93. package/src/compat/bundler/import-meta-url.ts +19 -19
  94. package/src/compat/bundler/modularize-imports.ts +36 -33
  95. package/src/compat/bundler/new-url-asset.ts +22 -24
  96. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  97. package/src/compat/bundler/polyfill.ts +28 -28
  98. package/src/compat/bundler/react-compiler.ts +35 -29
  99. package/src/compat/bundler/react-profiler.tsx +11 -11
  100. package/src/compat/bundler/relay-transform.ts +48 -47
  101. package/src/compat/bundler/require-context.ts +119 -113
  102. package/src/compat/bundler/resolve-extensions.ts +19 -19
  103. package/src/compat/bundler/source-cache.ts +25 -25
  104. package/src/compat/bundler/static-imports.ts +7 -7
  105. package/src/compat/bundler/symlink-imports.ts +46 -46
  106. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  107. package/src/compat/bundler/wasm.ts +58 -60
  108. package/src/compat/bundler/webpack-loaders.ts +254 -241
  109. package/src/compat/bundler/worker.ts +101 -104
  110. package/src/compat/cache/build-flags.ts +29 -29
  111. package/src/compat/cache/build-prerender-errors.ts +40 -44
  112. package/src/compat/cache/custom-handler.ts +60 -53
  113. package/src/compat/cache/fetch-patch.ts +240 -240
  114. package/src/compat/cache/handler.ts +27 -31
  115. package/src/compat/cache/modern-handler.ts +148 -126
  116. package/src/compat/cache/resume-data-cache.ts +47 -45
  117. package/src/compat/cache/revalidate.ts +233 -232
  118. package/src/compat/cache/runtime-error.ts +35 -35
  119. package/src/compat/cache/use-cache-transform.ts +442 -417
  120. package/src/compat/cache/use-cache.ts +650 -614
  121. package/src/compat/cache-control.ts +140 -142
  122. package/src/compat/client/base-path.ts +21 -20
  123. package/src/compat/client/css-order.ts +18 -18
  124. package/src/compat/client/errors/bare-boundary.ts +11 -11
  125. package/src/compat/client/errors/control-flow.ts +23 -23
  126. package/src/compat/client/errors/error-boundary.ts +61 -61
  127. package/src/compat/client/errors/global-error.ts +101 -91
  128. package/src/compat/client/errors/install.ts +71 -72
  129. package/src/compat/client/errors/lazy.ts +23 -23
  130. package/src/compat/client/errors/primitive-throw.ts +47 -45
  131. package/src/compat/client/errors/soft-refresh.ts +4 -4
  132. package/src/compat/client/link-status.ts +33 -33
  133. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  134. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  135. package/src/compat/client/navigation-scroll.ts +63 -63
  136. package/src/compat/client/optimistic-routing.ts +93 -88
  137. package/src/compat/client/prefetch-cache.ts +23 -24
  138. package/src/compat/client/route-announcer.ts +35 -35
  139. package/src/compat/client/segment-cache-policy.ts +20 -20
  140. package/src/compat/client/segment-cache.ts +309 -315
  141. package/src/compat/client/segment-prefetch.ts +127 -132
  142. package/src/compat/client/trailing-slash.ts +5 -4
  143. package/src/compat/css/chunking.ts +113 -116
  144. package/src/compat/css/inline-css.ts +21 -21
  145. package/src/compat/css/lightningcss.ts +37 -38
  146. package/src/compat/css/modules.ts +161 -175
  147. package/src/compat/css/nonce.ts +7 -7
  148. package/src/compat/css/sass-plugin.ts +18 -21
  149. package/src/compat/css/sass.ts +150 -152
  150. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  151. package/src/compat/css/styled-jsx.ts +21 -21
  152. package/src/compat/edge-runtime.ts +27 -27
  153. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  154. package/src/compat/export/client.ts +29 -31
  155. package/src/compat/export/{index.ts → emit.ts} +111 -110
  156. package/src/compat/export/standalone.ts +62 -54
  157. package/src/compat/image-optimizer/cache.ts +51 -49
  158. package/src/compat/image-optimizer/detect.ts +52 -52
  159. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  160. package/src/compat/image-optimizer/source.ts +77 -80
  161. package/src/compat/lifecycle/after-scope.ts +26 -26
  162. package/src/compat/lifecycle/after.ts +48 -45
  163. package/src/compat/lifecycle/error-funnel.ts +98 -102
  164. package/src/compat/lifecycle/error-serialize.ts +30 -26
  165. package/src/compat/lifecycle/error-ui.ts +67 -32
  166. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  167. package/src/compat/lifecycle/instrumentation.ts +85 -85
  168. package/src/compat/lifecycle/node-console.ts +11 -11
  169. package/src/compat/lifecycle/testmode.ts +160 -132
  170. package/src/compat/mdx/compile.ts +60 -57
  171. package/src/compat/mdx/plugin.ts +11 -11
  172. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  173. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  174. package/src/compat/metadata.ts +75 -81
  175. package/src/compat/next/cache.ts +68 -69
  176. package/src/compat/next/canonical-url.ts +9 -9
  177. package/src/compat/next/client-cache.ts +19 -19
  178. package/src/compat/next/client-navigation.ts +112 -116
  179. package/src/compat/next/client-only.ts +1 -1
  180. package/src/compat/next/client-script.tsx +99 -93
  181. package/src/compat/next/client-server.ts +10 -10
  182. package/src/compat/next/config-loader.ts +176 -173
  183. package/src/compat/next/config.ts +7 -7
  184. package/src/compat/next/constants.cjs +6 -6
  185. package/src/compat/next/constants.ts +6 -6
  186. package/src/compat/next/custom-server.ts +26 -24
  187. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  188. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  189. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  190. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  191. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  192. package/src/compat/next/dynamic.tsx +21 -18
  193. package/src/compat/next/error.tsx +52 -52
  194. package/src/compat/next/font/cache.ts +74 -74
  195. package/src/compat/next/font/google.ts +2 -2
  196. package/src/compat/next/font/index.ts +1 -1
  197. package/src/compat/next/font/local.ts +3 -3
  198. package/src/compat/next/font/runtime-client.ts +17 -15
  199. package/src/compat/next/font/runtime.ts +443 -408
  200. package/src/compat/next/font/shared.ts +120 -108
  201. package/src/compat/next/form.tsx +63 -63
  202. package/src/compat/next/head.tsx +2 -2
  203. package/src/compat/next/headers.ts +103 -95
  204. package/src/compat/next/image/client.tsx +220 -0
  205. package/src/compat/next/image/config.ts +56 -58
  206. package/src/compat/next/image/optimizer.ts +40 -36
  207. package/src/compat/next/image/patterns.ts +37 -40
  208. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  209. package/src/compat/next/image/shared.ts +65 -57
  210. package/src/compat/next/image/static-metadata.ts +98 -107
  211. package/src/compat/next/image/validate.ts +79 -88
  212. package/src/compat/next/image.tsx +19 -23
  213. package/src/compat/next/index.ts +1 -1
  214. package/src/compat/next/legacy-image.tsx +59 -60
  215. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  216. package/src/compat/next/link.tsx +158 -158
  217. package/src/compat/next/navigation.cjs +12 -3
  218. package/src/compat/next/navigation.ts +48 -50
  219. package/src/compat/next/offline.ts +27 -27
  220. package/src/compat/next/og.ts +121 -124
  221. package/src/compat/next/preferred-region.ts +13 -14
  222. package/src/compat/next/redirects.ts +58 -56
  223. package/src/compat/next/resource-hints.ts +73 -76
  224. package/src/compat/next/rewrites.ts +130 -133
  225. package/src/compat/next/root-params.ts +45 -45
  226. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  227. package/src/compat/next/router.cjs +4 -2
  228. package/src/compat/next/router.ts +58 -61
  229. package/src/compat/next/script.tsx +108 -108
  230. package/src/compat/next/server-only.ts +1 -1
  231. package/src/compat/next/server.ts +19 -19
  232. package/src/compat/next/svgr.ts +18 -17
  233. package/src/compat/next/telemetry.ts +24 -24
  234. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  235. package/src/compat/next/user-agent.ts +53 -49
  236. package/src/compat/next/web-vitals.ts +22 -24
  237. package/src/compat/otel/api.ts +41 -41
  238. package/src/compat/otel/client-trace-metadata.ts +25 -27
  239. package/src/compat/otel/fetch-span.ts +29 -29
  240. package/src/compat/otel/tracer.ts +331 -331
  241. package/src/compat/pages/api.ts +456 -0
  242. package/src/compat/pages/client-plugin.ts +36 -36
  243. package/src/compat/pages/router-state.ts +34 -34
  244. package/src/compat/pages/{index.ts → router.ts} +130 -135
  245. package/src/compat/ppr/io.ts +12 -12
  246. package/src/compat/ppr/missing-root-params.ts +34 -38
  247. package/src/compat/ppr/root-params-scan.ts +66 -66
  248. package/src/compat/ppr/root-params-transform.ts +24 -26
  249. package/src/compat/ppr/root-params.ts +30 -30
  250. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  251. package/src/compat/protocol.ts +71 -70
  252. package/src/compat/react/action-state.ts +47 -48
  253. package/src/compat/react/client-lite.ts +15 -15
  254. package/src/compat/react/client.ts +4 -4
  255. package/src/compat/react/compiler-runtime.ts +11 -11
  256. package/src/compat/react/dom-client.ts +44 -44
  257. package/src/compat/react/dom-react-server.ts +10 -16
  258. package/src/compat/react/dom-server.ts +10 -10
  259. package/src/compat/react/dom.ts +52 -52
  260. package/src/compat/react/hooks-extra.ts +34 -35
  261. package/src/compat/react/parity.ts +64 -61
  262. package/src/compat/react/preact.ts +81 -82
  263. package/src/compat/react/react-server.ts +28 -28
  264. package/src/compat/react/router-shim.ts +1 -1
  265. package/src/compat/react/server-component-use.ts +8 -8
  266. package/src/compat/react/server-inserted-html.ts +30 -31
  267. package/src/compat/react/server.ts +53 -55
  268. package/src/compat/react/use.ts +32 -32
  269. package/src/compat/react/view-transition.ts +20 -20
  270. package/src/compat/register/actions.ts +35 -824
  271. package/src/compat/register/boot.ts +41 -41
  272. package/src/compat/register/build-tier.ts +5 -5
  273. package/src/compat/register/build.ts +74 -70
  274. package/src/compat/register/bundler.ts +161 -156
  275. package/src/compat/register/cache.ts +20 -20
  276. package/src/compat/register/client-errors.ts +3 -3
  277. package/src/compat/register/config.ts +6 -6
  278. package/src/compat/register/css-extras.ts +30 -34
  279. package/src/compat/register/edge-runtime.ts +3 -3
  280. package/src/compat/register/errors.ts +10 -12
  281. package/src/compat/register/export.ts +16 -16
  282. package/src/compat/register/font.ts +11 -11
  283. package/src/compat/register/hooks.ts +2 -2
  284. package/src/compat/register/image.ts +40 -40
  285. package/src/compat/register/index.ts +59 -62
  286. package/src/compat/register/instrumentation-client.ts +10 -10
  287. package/src/compat/register/lifecycle.ts +25 -28
  288. package/src/compat/register/mdx.ts +10 -10
  289. package/src/compat/register/middleware.ts +226 -16
  290. package/src/compat/register/otel.ts +80 -88
  291. package/src/compat/register/pages-api.ts +10 -463
  292. package/src/compat/register/ppr.ts +16 -16
  293. package/src/compat/register/protocol.ts +17 -18
  294. package/src/compat/register/proxy.ts +49 -51
  295. package/src/compat/register/render.ts +79 -76
  296. package/src/compat/register/routing.ts +162 -159
  297. package/src/compat/register/segment.ts +24 -1887
  298. package/src/compat/register/static-image.ts +3 -3
  299. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  300. package/src/compat/register/typed-routes.ts +14 -14
  301. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  302. package/src/compat/register/validation.ts +18 -18
  303. package/src/compat/segment/loading-boundary.ts +43 -45
  304. package/src/compat/segment/page-slot.ts +69 -69
  305. package/src/compat/segment/serve.ts +1884 -0
  306. package/src/compat/segment/tree.ts +113 -112
  307. package/src/compat/segment/vary-key.ts +38 -38
  308. package/src/compat/segment/vary-params.ts +138 -142
  309. package/src/compat/static-params.ts +14 -12
  310. package/src/compat/tsconfig-defaults.ts +87 -91
  311. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  312. package/src/compat/typecheck/worker.ts +15 -12
  313. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  314. package/src/compat/typed-routes/manifest.ts +174 -170
  315. package/src/compat/typed-routes/typegen.ts +127 -110
  316. package/src/compat/validation/errors.ts +16 -19
  317. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  318. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  319. package/src/compat-bootstrap.ts +16 -16
  320. package/src/config.ts +69 -69
  321. package/src/css/build.ts +230 -227
  322. package/src/css/postcss.ts +79 -80
  323. package/src/css/worker.ts +14 -15
  324. package/src/dev/client-actions.ts +10 -10
  325. package/src/dev/client-chunk-store.ts +27 -27
  326. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  327. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  328. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  329. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  330. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  331. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  332. package/src/dev/server.ts +804 -820
  333. package/src/env.ts +46 -43
  334. package/src/extensions.ts +491 -478
  335. package/src/index.ts +8 -8
  336. package/src/internal.ts +20 -23
  337. package/src/{islands → render}/boundary-error.ts +3 -3
  338. package/src/render/hooks.ts +71 -71
  339. package/src/render/island-context.ts +14 -14
  340. package/src/render/metadata.ts +310 -310
  341. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  342. package/src/{ppr.ts → render/ppr.ts} +244 -245
  343. package/src/render/renderer.ts +2076 -2082
  344. package/src/render/resource-hints.ts +16 -17
  345. package/src/render/slots.tsx +224 -235
  346. package/src/{islands → render}/static-children.ts +9 -12
  347. package/src/{islands → render}/static-slots.ts +37 -37
  348. package/src/{cache/context.ts → request/cache.ts} +20 -20
  349. package/src/request/context.ts +107 -107
  350. package/src/{dynamic/source.ts → resolve/dynamic.ts} +152 -143
  351. package/src/resolve/engine.ts +90 -77
  352. package/src/resolve/imports.ts +475 -463
  353. package/src/resolve/scan-facts.ts +442 -186
  354. package/src/resolve/source-text.ts +37 -37
  355. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  356. package/src/routing/forwarded.ts +19 -19
  357. package/src/routing/handler.ts +84 -91
  358. package/src/routing/href.ts +69 -70
  359. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  360. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  361. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  362. package/src/routing/routes.ts +833 -848
  363. package/src/routing/slots.ts +164 -160
  364. package/src/runtime/loader.ts +952 -0
  365. package/src/{dev → runtime}/module-cache.ts +309 -287
  366. package/src/{dev → runtime}/module-generations.ts +9 -9
  367. package/src/{dev → runtime}/module-transform.ts +81 -72
  368. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  369. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  370. package/src/runtime/vendor.ts +425 -404
  371. package/src/styles.d.ts +9 -0
  372. package/src/types.ts +320 -335
  373. package/src/utils/ansi.ts +5 -5
  374. package/src/utils/{source.ts → code.ts} +15 -12
  375. package/src/utils/content-type.ts +3 -3
  376. package/src/utils/decode.ts +2 -2
  377. package/src/utils/dev-profile.ts +13 -13
  378. package/src/utils/error-log.ts +6 -6
  379. package/src/utils/esbuild.ts +18 -18
  380. package/src/utils/fs-cache.ts +13 -13
  381. package/src/utils/fs.ts +57 -49
  382. package/src/utils/html.ts +20 -24
  383. package/src/utils/native-require.ts +8 -8
  384. package/src/utils/serialize.ts +139 -146
  385. package/src/utils/verbose.ts +18 -18
  386. package/src/cli/analyze-print.ts +0 -181
  387. package/src/compat/middleware/manifest.ts +0 -210
  388. package/src/compat/next/image-client.tsx +0 -215
  389. package/src/compat/next/link-usage.ts +0 -29
  390. package/src/css/index.ts +0 -2
  391. package/src/render/index.ts +0 -1
  392. package/src/style-modules.d.ts +0 -9
package/src/css/build.ts CHANGED
@@ -1,80 +1,80 @@
1
- import { existsSync, readFileSync } from 'node:fs';
2
- import { writeFile } from 'node:fs/promises';
3
- import path from 'node:path';
4
- import type { Plugin } from 'esbuild';
5
- import { build } from '../utils/esbuild';
6
- import { ensureDir, readText } from '../utils/fs';
7
- import { postcssConfigFile, runPostcss } from './postcss';
8
- import { extraPageExtensions, getCssExtensions } from '../extensions';
9
- import { resolveImport } from '../resolve/imports';
10
- import { createVerboseLogger } from '../utils/verbose';
11
- import type { ClientReference } from '../client/reference';
12
- import type { ResolvedConfig } from '../config';
13
- import type { RouteManifestEntry } from '../types';
14
-
15
- import type { CssWorkerRequest, CssWorkerResponse } from './worker';
1
+ import { existsSync, readFileSync } from 'node:fs'
2
+ import { writeFile } from 'node:fs/promises'
3
+ import path from 'node:path'
4
+ import type { Plugin } from 'esbuild'
5
+ import { build } from '../utils/esbuild'
6
+ import { ensureDir, readText } from '../utils/fs'
7
+ import { postcssConfigFile, runPostcss } from './postcss'
8
+ import { extraPageExtensions, getCssExtensions } from '../extensions'
9
+ import { resolveImport } from '../resolve/imports'
10
+ import { createVerboseLogger } from '../utils/verbose'
11
+ import type { ClientReference } from '../client/reference'
12
+ import type { ResolvedConfig } from '../config'
13
+ import type { RouteManifestEntry } from '../types'
14
+
15
+ import type { CssWorkerRequest, CssWorkerResponse } from './worker'
16
16
 
17
17
  interface Pending {
18
- resolve: () => void;
19
- reject: (error: Error) => void;
18
+ resolve: () => void
19
+ reject: (error: Error) => void
20
20
  }
21
21
 
22
22
  // Bun's Worker carries node's ref/unref; the DOM lib typing does not.
23
23
  // Bun's terminate() resolves once the thread is gone (lib.dom types it void).
24
- type CssWorker = Worker & { ref(): void; unref(): void; terminate(): Promise<void> };
24
+ type CssWorker = Worker & { ref(): void; unref(): void; terminate(): Promise<void> }
25
25
 
26
- let worker: CssWorker | undefined;
27
- let workerFailed = false;
28
- let nextId = 0;
29
- const pending = new Map<number, Pending>();
26
+ let worker: CssWorker | undefined
27
+ let workerFailed = false
28
+ let nextId = 0
29
+ const pending = new Map<number, Pending>()
30
30
 
31
31
  function ensureWorker() {
32
- if (worker || workerFailed) return worker;
32
+ if (worker || workerFailed) return worker
33
33
  try {
34
34
  worker = new Worker(new URL('./worker.ts', import.meta.url).href, {
35
35
  type: 'module',
36
- }) as CssWorker;
36
+ }) as CssWorker
37
37
  } catch {
38
- workerFailed = true;
39
- return undefined;
38
+ workerFailed = true
39
+ return undefined
40
40
  }
41
41
  // Idle workers must not hold `pnext build` (or a test run) open; each in-flight
42
42
  // request refs it again.
43
- worker.unref();
43
+ worker.unref()
44
44
  worker.onmessage = (event: MessageEvent<CssWorkerResponse>) => {
45
- const { id, error } = event.data;
46
- const entry = pending.get(id);
47
- if (!entry) return;
48
- pending.delete(id);
49
- if (pending.size === 0) worker?.unref();
50
- if (!error) return entry.resolve();
51
- const failure = new Error(error.message);
52
- failure.name = error.name;
53
- if (error.stack) failure.stack = error.stack;
54
- entry.reject(failure);
55
- };
56
- worker.onerror = () => failWorker(new Error('pnext CSS worker crashed'));
57
- return worker;
45
+ const { id, error } = event.data
46
+ const entry = pending.get(id)
47
+ if (!entry) return
48
+ pending.delete(id)
49
+ if (pending.size === 0) worker?.unref()
50
+ if (!error) return entry.resolve()
51
+ const failure = new Error(error.message)
52
+ failure.name = error.name
53
+ if (error.stack) failure.stack = error.stack
54
+ entry.reject(failure)
55
+ }
56
+ worker.onerror = () => failWorker(new Error('pnext CSS worker crashed'))
57
+ return worker
58
58
  }
59
59
 
60
60
  // A dead worker takes its processor cache with it; drop it and let the next
61
61
  // request fall back in-process rather than hanging every pending build.
62
62
  function failWorker(error: Error) {
63
- workerFailed = true;
64
- worker = undefined;
65
- for (const entry of pending.values()) entry.reject(error);
66
- pending.clear();
63
+ workerFailed = true
64
+ worker = undefined
65
+ for (const entry of pending.values()) entry.reject(error)
66
+ pending.clear()
67
67
  }
68
68
 
69
69
  function send(request: (id: number) => CssWorkerRequest) {
70
- const active = ensureWorker();
71
- if (!active) return undefined;
72
- const id = nextId++;
70
+ const active = ensureWorker()
71
+ if (!active) return undefined
72
+ const id = nextId++
73
73
  return new Promise<void>((resolve, reject) => {
74
- pending.set(id, { resolve, reject });
75
- active.ref();
76
- active.postMessage(request(id));
77
- });
74
+ pending.set(id, { resolve, reject })
75
+ active.ref()
76
+ active.postMessage(request(id))
77
+ })
78
78
  }
79
79
 
80
80
  /** Run the app's postcss pipeline over a built stylesheet, off the event loop. */
@@ -82,9 +82,9 @@ function send(request: (id: number) => CssWorkerRequest) {
82
82
  // exit code to 1 on Linux after a fully successful build); one-shot commands
83
83
  // terminate it explicitly before exiting.
84
84
  export async function stopCssWorker(): Promise<void> {
85
- const active = worker;
86
- worker = undefined;
87
- if (active) await Promise.resolve(active.terminate());
85
+ const active = worker
86
+ worker = undefined
87
+ if (active) await Promise.resolve(active.terminate())
88
88
  }
89
89
 
90
90
  export async function runPostcssOffThread(
@@ -93,11 +93,11 @@ export async function runPostcssOffThread(
93
93
  options: { dev?: boolean },
94
94
  from?: string,
95
95
  ) {
96
- const { root, outPath: outDir } = config;
97
- const dev = options.dev ?? false;
98
- const sent = send(id => ({ kind: 'process', id, root, dev, outDir, cssFile, from }));
99
- if (sent) return sent;
100
- return runPostcss(root, cssFile, { dev, outDir }, from);
96
+ const { root, outPath: outDir } = config
97
+ const dev = options.dev ?? false
98
+ const sent = send(id => ({ kind: 'process', id, root, dev, outDir, cssFile, from }))
99
+ if (sent) return sent
100
+ return runPostcss(root, cssFile, { dev, outDir }, from)
101
101
  }
102
102
 
103
103
  /**
@@ -116,28 +116,28 @@ export function warmCssWorker(
116
116
  outDir: config.outPath,
117
117
  }))?.catch(() => {
118
118
  // A broken postcss config surfaces on the real build; warming stays quiet.
119
- });
119
+ })
120
120
  }
121
121
 
122
122
  interface CssBuildOptions {
123
- dev?: boolean;
124
- verbose?: boolean;
123
+ dev?: boolean
124
+ verbose?: boolean
125
125
  }
126
126
 
127
- let cssRuntimeRegistered = false;
127
+ let cssRuntimeRegistered = false
128
128
 
129
129
  export function globalCssSource(config: ResolvedConfig) {
130
- return globalCssSources(config)[0];
130
+ return globalCssSources(config)[0]
131
131
  }
132
132
 
133
133
  export function globalCssSources(config: Pick<ResolvedConfig, 'root' | 'appPath'>) {
134
- return globalCssSourcesForPaths(config.root, config.appPath);
134
+ return globalCssSourcesForPaths(config.root, config.appPath)
135
135
  }
136
136
 
137
137
  // The walk below reads and resolves the ROOT LAYOUT'S WHOLE IMPORT GRAPH, and every render asks it
138
138
  // (via globalCssHref) whether the app has global CSS at all. Memoize per root+appPath; dev clears
139
139
  // this on save (invalidateDevCaches), prod sources never change in-process.
140
- const globalCssSourceCache = new Map<string, string[]>();
140
+ const globalCssSourceCache = new Map<string, string[]>()
141
141
 
142
142
  /**
143
143
  * Restart half of the walk above: the store persists the answer keyed by every
@@ -145,45 +145,45 @@ const globalCssSourceCache = new Map<string, string[]>();
145
145
  * graph. `undefined` (prod, or the cache off) simply means always walk.
146
146
  */
147
147
  export interface GlobalCssSourceStore {
148
- load(key: string): string[] | undefined;
149
- save(key: string, sources: string[], visited: string[], missing: string[]): void;
148
+ load(key: string): string[] | undefined
149
+ save(key: string, sources: string[], visited: string[], missing: string[]): void
150
150
  }
151
151
 
152
- let globalCssStore: GlobalCssSourceStore | undefined;
152
+ let globalCssStore: GlobalCssSourceStore | undefined
153
153
 
154
154
  export function setGlobalCssSourceStore(store: GlobalCssSourceStore | undefined) {
155
- globalCssStore = store;
155
+ globalCssStore = store
156
156
  }
157
157
 
158
158
  export function globalCssSourcesForPaths(root: string, appPath: string) {
159
- const key = `${root}\0${appPath}`;
160
- const cached = globalCssSourceCache.get(key);
161
- if (cached) return cached;
159
+ const key = `${root}\0${appPath}`
160
+ const cached = globalCssSourceCache.get(key)
161
+ if (cached) return cached
162
162
 
163
- const stored = globalCssStore?.load(key);
163
+ const stored = globalCssStore?.load(key)
164
164
  if (stored) {
165
- globalCssSourceCache.set(key, stored);
166
- return stored;
165
+ globalCssSourceCache.set(key, stored)
166
+ return stored
167
167
  }
168
168
 
169
- const sources = new Set<string>();
170
- const visited = new Set<string>();
171
- const missing = new Set<string>();
172
- collectGlobalCssSources(root, rootLayoutFile(appPath, missing), sources, visited, missing);
169
+ const sources = new Set<string>()
170
+ const visited = new Set<string>()
171
+ const missing = new Set<string>()
172
+ collectGlobalCssSources(root, rootLayoutFile(appPath, missing), sources, visited, missing)
173
173
 
174
- const resolved = [...sources].map(file => path.resolve(file));
175
- globalCssSourceCache.set(key, resolved);
174
+ const resolved = [...sources].map(file => path.resolve(file))
175
+ globalCssSourceCache.set(key, resolved)
176
176
  // The answer depends on every file the walk READ (a new `import './x.css'`
177
177
  // anywhere in the layout graph changes its importer's bytes) and on every
178
178
  // path it found ABSENT (restoring one while the server was down would add an
179
179
  // edge no importer's bytes record).
180
- globalCssStore?.save(key, resolved, [...visited], [...missing]);
181
- return resolved;
180
+ globalCssStore?.save(key, resolved, [...visited], [...missing])
181
+ return resolved
182
182
  }
183
183
 
184
184
  /** Drop the memoized layout-graph walk (dev save invalidation). */
185
185
  export function clearGlobalCssSourceCache() {
186
- globalCssSourceCache.clear();
186
+ globalCssSourceCache.clear()
187
187
  }
188
188
 
189
189
  function collectGlobalCssSources(
@@ -193,49 +193,49 @@ function collectGlobalCssSources(
193
193
  visited: Set<string>,
194
194
  missing?: Set<string>,
195
195
  ) {
196
- const resolvedFile = path.resolve(file);
197
- if (visited.has(resolvedFile)) return;
196
+ const resolvedFile = path.resolve(file)
197
+ if (visited.has(resolvedFile)) return
198
198
  if (!existsSync(resolvedFile)) {
199
- missing?.add(resolvedFile);
200
- return;
199
+ missing?.add(resolvedFile)
200
+ return
201
201
  }
202
- visited.add(resolvedFile);
202
+ visited.add(resolvedFile)
203
203
 
204
204
  for (const specifier of moduleSpecifiers(readFileSync(resolvedFile, 'utf8'))) {
205
- const resolved = resolveImport(root, resolvedFile, specifier);
206
- if (!resolved) continue;
205
+ const resolved = resolveImport(root, resolvedFile, specifier)
206
+ if (!resolved) continue
207
207
  if (isCssFile(resolved)) {
208
- sources.add(resolved);
208
+ sources.add(resolved)
209
209
  } else {
210
- collectGlobalCssSources(root, resolved, sources, visited, missing);
210
+ collectGlobalCssSources(root, resolved, sources, visited, missing)
211
211
  }
212
212
  }
213
213
  }
214
214
 
215
215
  function rootLayoutFile(appPath: string, missing?: Set<string>) {
216
216
  for (const extension of ['tsx', 'ts', 'jsx', 'js', 'mjs', ...extraPageExtensions()]) {
217
- const file = path.join(appPath, `layout.${extension}`);
218
- if (existsSync(file)) return file;
219
- missing?.add(file);
217
+ const file = path.join(appPath, `layout.${extension}`)
218
+ if (existsSync(file)) return file
219
+ missing?.add(file)
220
220
  }
221
- return path.join(appPath, 'layout.tsx');
221
+ return path.join(appPath, 'layout.tsx')
222
222
  }
223
223
 
224
224
  export function globalCssHref(config: ResolvedConfig) {
225
225
  return globalCssSources(config).length > 0
226
226
  ? withAssetPrefix(config, '/assets/global.css')
227
- : undefined;
227
+ : undefined
228
228
  }
229
229
 
230
230
  export function routeCssHref(
231
231
  route: RouteManifestEntry,
232
232
  config?: Pick<ResolvedConfig, 'assetPrefix'>,
233
233
  ): string | string[] | undefined {
234
- if (route.cssImports.length === 0) return undefined;
234
+ if (route.cssImports.length === 0) return undefined
235
235
  if (route.cssAssets?.length) {
236
- return route.cssAssets.map(asset => withAssetPrefix(config, `/assets/${asset}`));
236
+ return route.cssAssets.map(asset => withAssetPrefix(config, `/assets/${asset}`))
237
237
  }
238
- return withAssetPrefix(config, `/assets/${route.id}.css`);
238
+ return withAssetPrefix(config, `/assets/${route.id}.css`)
239
239
  }
240
240
 
241
241
  // Prepend the configured assetPrefix (a CDN origin or path) to an app-absolute
@@ -244,9 +244,9 @@ export function withAssetPrefix(
244
244
  config: Pick<ResolvedConfig, 'assetPrefix'> | undefined,
245
245
  href: string,
246
246
  ) {
247
- const prefix = config?.assetPrefix;
248
- if (!prefix) return href;
249
- return `${prefix.replace(/\/$/, '')}${href}`;
247
+ const prefix = config?.assetPrefix
248
+ if (!prefix) return href
249
+ return `${prefix.replace(/\/$/, '')}${href}`
250
250
  }
251
251
 
252
252
  /**
@@ -258,29 +258,29 @@ export function warmCssPipeline(
258
258
  config: Pick<ResolvedConfig, 'root' | 'outPath'>,
259
259
  options: CssBuildOptions = {},
260
260
  ) {
261
- if (!hasPostcssConfig(config.root)) return;
262
- warmCssWorker(config, options);
261
+ if (!hasPostcssConfig(config.root)) return
262
+ warmCssWorker(config, options)
263
263
  }
264
264
 
265
265
  export async function buildGlobalCss(config: ResolvedConfig, options: CssBuildOptions = {}) {
266
- const log = createVerboseLogger(options.verbose ?? false, 'css');
267
- const sources = globalCssSources(config);
266
+ const log = createVerboseLogger(options.verbose ?? false, 'css')
267
+ const sources = globalCssSources(config)
268
268
  if (sources.length === 0) {
269
- log.log('global css: no side-effect css imports in root layout, skipping');
270
- return undefined;
269
+ log.log('global css: no side-effect css imports in root layout, skipping')
270
+ return undefined
271
271
  }
272
272
 
273
273
  const outDir = options.dev
274
274
  ? path.join(config.outPath, 'cache', 'assets')
275
- : path.join(config.outPath, 'public', 'assets');
276
- const outfile = path.join(outDir, 'global.css');
277
- await ensureDir(outDir);
275
+ : path.join(config.outPath, 'public', 'assets')
276
+ const outfile = path.join(outDir, 'global.css')
277
+ await ensureDir(outDir)
278
278
 
279
279
  log.log(
280
280
  `global css: ${sources.length} source${sources.length === 1 ? '' : 's'} — ${sources
281
281
  .map(file => path.relative(config.root, file))
282
282
  .join(', ')}`,
283
- );
283
+ )
284
284
 
285
285
  await log.step('global css: esbuild bundle', () =>
286
286
  build({
@@ -310,60 +310,60 @@ export async function buildGlobalCss(config: ResolvedConfig, options: CssBuildOp
310
310
  '.ico': 'file',
311
311
  },
312
312
  }),
313
- );
313
+ )
314
314
  if (hasPostcssConfig(config.root)) {
315
- await rewriteTailwindConfigDirectives(outfile, sources);
315
+ await rewriteTailwindConfigDirectives(outfile, sources)
316
316
  await log.step('global css: postcss', () =>
317
317
  runPostcssOffThread(config, outfile, options, sources[0]),
318
- );
318
+ )
319
319
  }
320
320
 
321
- log.log(`global css → ${path.relative(config.root, outfile)}`);
322
- return outfile;
321
+ log.log(`global css → ${path.relative(config.root, outfile)}`)
322
+ return outfile
323
323
  }
324
324
 
325
325
  function moduleSpecifiers(source: string) {
326
- const specifiers: string[] = [];
327
- const pattern = /(?:import|export)\s+(?:[^'";]+?\s+from\s+)?['"]([^'"]+)['"]/g;
328
- let match: RegExpExecArray | null;
326
+ const specifiers: string[] = []
327
+ const pattern = /(?:import|export)\s+(?:[^'";]+?\s+from\s+)?['"]([^'"]+)['"]/g
328
+ let match: RegExpExecArray | null
329
329
 
330
330
  while ((match = pattern.exec(source))) {
331
- if (match[1]) specifiers.push(match[1]);
331
+ if (match[1]) specifiers.push(match[1])
332
332
  }
333
333
 
334
- return specifiers;
334
+ return specifiers
335
335
  }
336
336
 
337
337
  function cssFileExtensions() {
338
- return ['.css', ...getCssExtensions().extraCssExtensions()];
338
+ return ['.css', ...getCssExtensions().extraCssExtensions()]
339
339
  }
340
340
 
341
341
  export function isCssFile(file: string) {
342
- return cssFileExtensions().some(extension => file.endsWith(extension));
342
+ return cssFileExtensions().some(extension => file.endsWith(extension))
343
343
  }
344
344
 
345
345
  function cssLoaderEntries() {
346
346
  return Object.fromEntries(
347
- getCssExtensions().extraCssExtensions().map(extension => [extension, 'css'] as const),
348
- );
347
+ getCssExtensions()
348
+ .extraCssExtensions()
349
+ .map(extension => [extension, 'css'] as const),
350
+ )
349
351
  }
350
352
 
351
353
  // The compat CSS-chunk plan (Next `experimental.cssChunking`), computed once
352
354
  // across all routes by prepareRouteCssChunks before the per-route build loop.
353
355
  // Pure-core apps never populate it, so every route keeps its single chunk.
354
- const routeCssPlan = new Map<string, string[][]>();
356
+ const routeCssPlan = new Map<string, string[][]>()
355
357
 
356
358
  // Partition each route's ordered cssImports into chunk slices via the compat
357
359
  // registry (Next's CSS chunking). Must run before buildRouteCss so the loop can
358
360
  // emit one asset per slice; a no-op for pure-core apps (empty plan).
359
- export function prepareRouteCssChunks(
360
- routes: Pick<RouteManifestEntry, 'id' | 'cssImports'>[],
361
- ) {
362
- routeCssPlan.clear();
361
+ export function prepareRouteCssChunks(routes: Pick<RouteManifestEntry, 'id' | 'cssImports'>[]) {
362
+ routeCssPlan.clear()
363
363
  const plan = getCssExtensions().planRouteCssChunks(
364
364
  routes.map(route => ({ id: route.id, cssImports: route.cssImports })),
365
- );
366
- for (const [id, segments] of plan) routeCssPlan.set(id, segments);
365
+ )
366
+ for (const [id, segments] of plan) routeCssPlan.set(id, segments)
367
367
  }
368
368
 
369
369
  export async function buildRouteCss(
@@ -371,12 +371,12 @@ export async function buildRouteCss(
371
371
  route: RouteManifestEntry,
372
372
  options: CssBuildOptions = {},
373
373
  ) {
374
- if (route.cssImports.length === 0) return undefined;
375
- const segments = routeCssPlan.get(route.id);
374
+ if (route.cssImports.length === 0) return undefined
375
+ const segments = routeCssPlan.get(route.id)
376
376
  if (segments && segments.length > 1) {
377
- const assetNames = segments.map((_, index) => `${route.id}-${index}.css`);
378
- route.cssAssets = assetNames;
379
- const files: string[] = [];
377
+ const assetNames = segments.map((_, index) => `${route.id}-${index}.css`)
378
+ route.cssAssets = assetNames
379
+ const files: string[] = []
380
380
  for (const [index, segment] of segments.entries()) {
381
381
  files.push(
382
382
  await bundleCssChunk(
@@ -386,12 +386,18 @@ export async function buildRouteCss(
386
386
  `route css ${route.route} #${index + 1}`,
387
387
  options,
388
388
  ),
389
- );
389
+ )
390
390
  }
391
- return files;
391
+ return files
392
392
  }
393
- route.cssAssets = undefined;
394
- return bundleCssChunk(config, route.cssImports, `${route.id}.css`, `route css ${route.route}`, options);
393
+ route.cssAssets = undefined
394
+ return bundleCssChunk(
395
+ config,
396
+ route.cssImports,
397
+ `${route.id}.css`,
398
+ `route css ${route.route}`,
399
+ options,
400
+ )
395
401
  }
396
402
 
397
403
  // The synthetic document conventions are created at render time and never
@@ -400,7 +406,7 @@ export async function buildRouteCss(
400
406
  // an unmatched URL rendering the root not-found ships `/assets/not-found.css`
401
407
  // (its own CSS) alongside `/assets/global.css` (the root layout's CSS).
402
408
  export async function buildNotFoundCss(config: ResolvedConfig, options: CssBuildOptions = {}) {
403
- const { collectFileCss } = await import('../routing/routes');
409
+ const { collectFileCss } = await import('../routing/routes')
404
410
  const targets: { id: string; files: string[]; includeGlobalCss?: boolean }[] = [
405
411
  {
406
412
  id: 'not-found',
@@ -418,18 +424,18 @@ export async function buildNotFoundCss(config: ResolvedConfig, options: CssBuild
418
424
  id: 'global-error',
419
425
  files: conventionCandidates(config.appPath, 'global-error'),
420
426
  },
421
- ];
427
+ ]
422
428
  for (const target of targets) {
423
429
  const cssImports = await collectFileCss(config.appPath, target.files, {
424
430
  includeGlobalCss: target.includeGlobalCss ?? false,
425
- });
426
- if (cssImports.length === 0) continue;
427
- await bundleCssChunk(config, cssImports, `${target.id}.css`, `route css /${target.id}`, options);
431
+ })
432
+ if (cssImports.length === 0) continue
433
+ await bundleCssChunk(config, cssImports, `${target.id}.css`, `route css /${target.id}`, options)
428
434
  }
429
435
  }
430
436
 
431
437
  function conventionCandidates(appPath: string, base: string) {
432
- return ['tsx', 'ts', 'jsx', 'js'].map(ext => path.join(appPath, `${base}.${ext}`));
438
+ return ['tsx', 'ts', 'jsx', 'js'].map(ext => path.join(appPath, `${base}.${ext}`))
433
439
  }
434
440
 
435
441
  // CSS reachable only through a non-SSR dynamic island ships as its own chunk
@@ -440,14 +446,14 @@ export async function buildClientReferenceCss(
440
446
  reference: ClientReference,
441
447
  options: CssBuildOptions = {},
442
448
  ) {
443
- if (!reference.cssImports || reference.cssImports.length === 0) return undefined;
449
+ if (!reference.cssImports || reference.cssImports.length === 0) return undefined
444
450
  return bundleCssChunk(
445
451
  config,
446
452
  reference.cssImports,
447
453
  `${reference.id}.css`,
448
454
  `island css ${reference.id}`,
449
455
  options,
450
- );
456
+ )
451
457
  }
452
458
 
453
459
  async function bundleCssChunk(
@@ -457,15 +463,15 @@ async function bundleCssChunk(
457
463
  label: string,
458
464
  options: CssBuildOptions,
459
465
  ) {
460
- const log = createVerboseLogger(options.verbose ?? false, 'css');
466
+ const log = createVerboseLogger(options.verbose ?? false, 'css')
461
467
 
462
468
  const outDir = options.dev
463
469
  ? path.join(config.outPath, 'cache', 'assets')
464
- : path.join(config.outPath, 'public', 'assets');
465
- const outfile = path.join(outDir, assetName);
466
- await ensureDir(outDir);
470
+ : path.join(config.outPath, 'public', 'assets')
471
+ const outfile = path.join(outDir, assetName)
472
+ await ensureDir(outDir)
467
473
 
468
- log.log(`${label}: ${cssImports.length} import${cssImports.length === 1 ? '' : 's'}`);
474
+ log.log(`${label}: ${cssImports.length} import${cssImports.length === 1 ? '' : 's'}`)
469
475
 
470
476
  await build({
471
477
  stdin: {
@@ -496,19 +502,19 @@ async function bundleCssChunk(
496
502
  '.svg': 'file',
497
503
  '.ico': 'file',
498
504
  },
499
- });
505
+ })
500
506
 
501
507
  if (hasPostcssConfig(config.root)) {
502
- await rewriteTailwindConfigDirectives(outfile, cssImports);
503
- await log.step(`${label}: postcss`, () => runPostcssOffThread(config, outfile, options));
508
+ await rewriteTailwindConfigDirectives(outfile, cssImports)
509
+ await log.step(`${label}: postcss`, () => runPostcssOffThread(config, outfile, options))
504
510
  }
505
511
 
506
- return outfile;
512
+ return outfile
507
513
  }
508
514
 
509
515
  export function registerCssRuntime() {
510
- if (cssRuntimeRegistered) return;
511
- cssRuntimeRegistered = true;
516
+ if (cssRuntimeRegistered) return
517
+ cssRuntimeRegistered = true
512
518
 
513
519
  Bun.plugin({
514
520
  name: 'pnext-css-runtime',
@@ -523,15 +529,15 @@ export function registerCssRuntime() {
523
529
  contents: 'export default undefined;',
524
530
  loader: 'js',
525
531
  },
526
- );
532
+ )
527
533
  },
528
- });
534
+ })
529
535
  }
530
536
 
531
537
  // Sass is unsupported (no compiler); its imports still must not break the
532
538
  // bundle, so .scss/.sass load as empty modules with module class-name maps.
533
539
  function isCssModuleFile(file: string) {
534
- return /\.module\.(?:css|scss|sass)$/.test(file);
540
+ return /\.module\.(?:css|scss|sass)$/.test(file)
535
541
  }
536
542
 
537
543
  export function cssModuleClientPlugin(): Plugin {
@@ -543,9 +549,9 @@ export function cssModuleClientPlugin(): Plugin {
543
549
  ? `export default ${JSON.stringify(await cssModuleMapping(file))};`
544
550
  : 'export default undefined;',
545
551
  loader: 'js',
546
- }));
552
+ }))
547
553
  },
548
- };
554
+ }
549
555
  }
550
556
 
551
557
  function cssModuleBuildPlugin(): Plugin {
@@ -555,43 +561,43 @@ function cssModuleBuildPlugin(): Plugin {
555
561
  plugin.onLoad({ filter: /\.module\.css$/ }, async ({ path: file }) => ({
556
562
  contents: transformCssModule(await readText(file), file),
557
563
  loader: 'css',
558
- }));
564
+ }))
559
565
  },
560
- };
566
+ }
561
567
  }
562
568
 
563
569
  async function cssModuleMapping(file: string) {
564
570
  // Compat handles *.module.{scss,sass} (sass compiles + scopes); core keeps
565
571
  // plain .module.css. Lazy read: the registry populates at compat bootstrap.
566
- const compatMapping = getCssExtensions().resolveCssModule(file);
567
- if (compatMapping) return compatMapping;
568
- const source = await readText(file);
569
- const classNames = cssClassNames(source);
572
+ const compatMapping = getCssExtensions().resolveCssModule(file)
573
+ if (compatMapping) return compatMapping
574
+ const source = await readText(file)
575
+ const classNames = cssClassNames(source)
570
576
 
571
577
  return Object.fromEntries(
572
578
  [...classNames].map(className => [className, cssModuleClassName(file, className)]),
573
- );
579
+ )
574
580
  }
575
581
 
576
582
  function cssClassNames(source: string) {
577
- const classNames = new Set<string>();
578
- const classPattern = /(^|[^\\])\.(-?[_a-zA-Z][\w-]*)/g;
579
- let match: RegExpExecArray | null;
583
+ const classNames = new Set<string>()
584
+ const classPattern = /(^|[^\\])\.(-?[_a-zA-Z][\w-]*)/g
585
+ let match: RegExpExecArray | null
580
586
 
581
587
  while ((match = classPattern.exec(source))) {
582
- if (match[2]) classNames.add(match[2]);
588
+ if (match[2]) classNames.add(match[2])
583
589
  }
584
590
 
585
- return classNames;
591
+ return classNames
586
592
  }
587
593
 
588
594
  function transformCssModule(source: string, file: string) {
589
- let css = source;
595
+ let css = source
590
596
  for (const className of cssClassNames(source)) {
591
- css = rewriteClassSelector(css, cssModuleClassName(file, className), className);
597
+ css = rewriteClassSelector(css, cssModuleClassName(file, className), className)
592
598
  }
593
- css = scopeKeyframes(css, file);
594
- return css;
599
+ css = scopeKeyframes(css, file)
600
+ return css
595
601
  }
596
602
 
597
603
  // CSS-module `@keyframes` names are scoped like class names, so a local animation `example` from two
@@ -599,25 +605,22 @@ function transformCssModule(source: string, file: string) {
599
605
  // reference to it are rewritten to the scoped name. `grid-area` names are NOT identifiers of this kind and
600
606
  // are left untouched.
601
607
  function scopeKeyframes(css: string, file: string) {
602
- const names = keyframeNames(css);
603
- if (names.size === 0) return css;
608
+ const names = keyframeNames(css)
609
+ if (names.size === 0) return css
604
610
 
605
- let out = css;
611
+ let out = css
606
612
  for (const name of names) {
607
- const scoped = cssModuleClassName(file, name);
613
+ const scoped = cssModuleClassName(file, name)
608
614
  // Rename the @keyframes / @-webkit-keyframes block name.
609
615
  out = out.replace(
610
616
  new RegExp(`(@(?:-webkit-)?keyframes\\s+)${escapeRegex(name)}(?![-_a-zA-Z0-9])`, 'g'),
611
617
  `$1${scoped}`,
612
- );
618
+ )
613
619
  // Rename `animation-name: name`.
614
620
  out = out.replace(
615
- new RegExp(
616
- `(animation-name\\s*:\\s*)${escapeRegex(name)}(?![-_a-zA-Z0-9])`,
617
- 'g',
618
- ),
621
+ new RegExp(`(animation-name\\s*:\\s*)${escapeRegex(name)}(?![-_a-zA-Z0-9])`, 'g'),
619
622
  `$1${scoped}`,
620
- );
623
+ )
621
624
  // Rename the name inside an `animation:` shorthand (token-bounded).
622
625
  out = out.replace(
623
626
  new RegExp(
@@ -625,84 +628,84 @@ function scopeKeyframes(css: string, file: string) {
625
628
  'g',
626
629
  ),
627
630
  `$1${scoped}`,
628
- );
631
+ )
629
632
  }
630
- return out;
633
+ return out
631
634
  }
632
635
 
633
636
  function keyframeNames(css: string) {
634
- const names = new Set<string>();
635
- const pattern = /@(?:-webkit-)?keyframes\s+(-?[_a-zA-Z][\w-]*)/g;
636
- let match: RegExpExecArray | null;
637
+ const names = new Set<string>()
638
+ const pattern = /@(?:-webkit-)?keyframes\s+(-?[_a-zA-Z][\w-]*)/g
639
+ let match: RegExpExecArray | null
637
640
  while ((match = pattern.exec(css))) {
638
- if (match[1]) names.add(match[1]);
641
+ if (match[1]) names.add(match[1])
639
642
  }
640
- return names;
643
+ return names
641
644
  }
642
645
 
643
646
  function cssModuleClassName(file: string, className: string) {
644
647
  const base = path
645
648
  .basename(file)
646
649
  .replace(/\.module\.css$/, '')
647
- .replace(/[^_a-zA-Z0-9]/g, '_');
648
- const hash = pathHash(cssModuleScopePath(file));
649
- return `${base}_${className}_${hash}`;
650
+ .replace(/[^_a-zA-Z0-9]/g, '_')
651
+ const hash = pathHash(cssModuleScopePath(file))
652
+ return `${base}_${className}_${hash}`
650
653
  }
651
654
 
652
655
  function cssModuleScopePath(file: string) {
653
- const normalized = file.split(path.sep).join('/');
654
- const appIndex = normalized.lastIndexOf('/app/');
655
- if (appIndex !== -1) return normalized.slice(appIndex + 1);
656
- return normalized.split('/').slice(-3).join('/');
656
+ const normalized = file.split(path.sep).join('/')
657
+ const appIndex = normalized.lastIndexOf('/app/')
658
+ if (appIndex !== -1) return normalized.slice(appIndex + 1)
659
+ return normalized.split('/').slice(-3).join('/')
657
660
  }
658
661
 
659
662
  function rewriteClassSelector(css: string, scopedClassName: string, originalClassName: string) {
660
663
  const esbuildNamePattern = new RegExp(
661
664
  `\\.${escapeRegex(originalClassName)}(?![-_a-zA-Z0-9])`,
662
665
  'g',
663
- );
664
- return css.replace(esbuildNamePattern, `.${scopedClassName}`);
666
+ )
667
+ return css.replace(esbuildNamePattern, `.${scopedClassName}`)
665
668
  }
666
669
 
667
670
  function pathHash(file: string) {
668
- let hash = 5381;
669
- for (const char of file) hash = ((hash << 5) + hash) ^ char.charCodeAt(0);
670
- return (hash >>> 0).toString(36).slice(0, 5);
671
+ let hash = 5381
672
+ for (const char of file) hash = ((hash << 5) + hash) ^ char.charCodeAt(0)
673
+ return (hash >>> 0).toString(36).slice(0, 5)
671
674
  }
672
675
 
673
676
  function escapeRegex(value: string) {
674
- return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
677
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
675
678
  }
676
679
 
677
680
  function hasPostcssConfig(root: string) {
678
- return postcssConfigFile(root) !== undefined;
681
+ return postcssConfigFile(root) !== undefined
679
682
  }
680
683
 
681
684
  // Bundling moves the CSS away from the directory its `@config` paths were
682
685
  // written against, and Tailwind resolves them against postcss's `from` (the
683
686
  // original source, not the outfile). Absolute paths sidestep both bases.
684
687
  export async function rewriteTailwindConfigDirectives(cssFile: string, sources: string[]) {
685
- const candidateDirs = [path.dirname(cssFile), ...sources.map(file => path.dirname(file))];
686
- let css = await readText(cssFile);
687
- let changed = false;
688
+ const candidateDirs = [path.dirname(cssFile), ...sources.map(file => path.dirname(file))]
689
+ let css = await readText(cssFile)
690
+ let changed = false
688
691
 
689
692
  css = css.replace(
690
693
  /@config\s+(['"])([^'"]+)\1\s*;/g,
691
694
  (match, quote: string, specifier: string) => {
692
- if (!isRelativeSpecifier(specifier)) return match;
695
+ if (!isRelativeSpecifier(specifier)) return match
693
696
  const resolved = candidateDirs
694
697
  .map(dir => path.resolve(dir, specifier))
695
- .find(file => existsSync(file));
696
- if (!resolved) return match;
698
+ .find(file => existsSync(file))
699
+ if (!resolved) return match
697
700
 
698
- changed = true;
699
- return `@config ${quote}${resolved.split(path.sep).join('/')}${quote};`;
701
+ changed = true
702
+ return `@config ${quote}${resolved.split(path.sep).join('/')}${quote};`
700
703
  },
701
- );
704
+ )
702
705
 
703
- if (changed) await writeFile(cssFile, css);
706
+ if (changed) await writeFile(cssFile, css)
704
707
  }
705
708
 
706
709
  function isRelativeSpecifier(specifier: string) {
707
- return specifier.startsWith('./') || specifier.startsWith('../');
710
+ return specifier.startsWith('./') || specifier.startsWith('../')
708
711
  }