@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
@@ -4,28 +4,28 @@
4
4
  * an esbuild re-bundle of a renaming barrel plus an import rewrite in every
5
5
  * chunk that referenced a member.
6
6
  */
7
- import { createHash } from 'node:crypto';
8
- import { existsSync } from 'node:fs';
9
- import { unlink } from 'node:fs/promises';
10
- import path from 'node:path';
11
- import { build, type BuildOptions, type Metafile } from 'esbuild';
12
- import { clientProfile } from './profile';
13
- import { readText, writeText } from '../utils/fs';
14
- import { escapeRegex } from '../utils/source';
7
+ import { createHash } from 'node:crypto'
8
+ import { existsSync } from 'node:fs'
9
+ import { unlink } from 'node:fs/promises'
10
+ import path from 'node:path'
11
+ import { build, type BuildOptions, type Metafile } from 'esbuild'
12
+ import { clientProfile } from './profile'
13
+ import { readText, writeText } from '../utils/fs'
14
+ import { escapeRegex } from '../utils/code'
15
15
 
16
16
  /** Unlinking an artifact a concurrent build already removed is not news. */
17
17
  function ignoreMissing() {
18
- return undefined;
18
+ return undefined
19
19
  }
20
20
 
21
21
  /** Import/export forms the rewrite understands; anything else vetoes the fold. */
22
- const namedClause = String.raw`\{([^}]*)\}`;
22
+ const namedClause = String.raw`\{([^}]*)\}`
23
23
 
24
24
  interface FoldOptions {
25
- outDir: string;
26
- metafile: Metafile;
25
+ outDir: string
26
+ metafile: Metafile
27
27
  /** esbuild flags of the build being folded, so the merge is emitted the same way. */
28
- buildOptions: Pick<BuildOptions, 'minify' | 'sourcemap' | 'target' | 'format'>;
28
+ buildOptions: Pick<BuildOptions, 'minify' | 'sourcemap' | 'target' | 'format'>
29
29
  }
30
30
 
31
31
  /**
@@ -34,11 +34,11 @@ interface FoldOptions {
34
34
  */
35
35
  export async function foldInitialChunks(options: FoldOptions): Promise<Metafile> {
36
36
  // eslint-disable-next-line turbo/no-undeclared-env-vars
37
- if (process.env.PNEXT_CHUNK_FOLD === '0') return options.metafile;
37
+ if (process.env.PNEXT_CHUNK_FOLD === '0') return options.metafile
38
38
  // The cost this removes is compressed-transfer only (a gzip header and
39
39
  // dictionary reset per chunk), so the pass rides the minify flag.
40
- if (!options.buildOptions.minify) return options.metafile;
41
- return foldChunks(options);
40
+ if (!options.buildOptions.minify) return options.metafile
41
+ return foldChunks(options)
42
42
  }
43
43
 
44
44
  /**
@@ -47,36 +47,36 @@ export async function foldInitialChunks(options: FoldOptions): Promise<Metafile>
47
47
  * disk have diverged, so it rethrows. `PNEXT_CHUNK_FOLD=strict` throws for both.
48
48
  */
49
49
  async function foldChunks({ outDir, metafile, buildOptions }: FoldOptions): Promise<Metafile> {
50
- let folded = metafile;
51
- const groups = clientProfile.time('foldGroups', () => coLoadGroups(metafile));
52
- clientProfile.count('foldGroup#', groups.length);
53
- clientProfile.count('foldOutput#', Object.keys(metafile.outputs).length);
54
- if (groups.length === 0) return folded;
50
+ let folded = metafile
51
+ const groups = clientProfile.time('foldGroups', () => coLoadGroups(metafile))
52
+ clientProfile.count('foldGroup#', groups.length)
53
+ clientProfile.count('foldOutput#', Object.keys(metafile.outputs).length)
54
+ if (groups.length === 0) return folded
55
55
  // Output text is read once and kept in step with what each group writes, and
56
56
  // the same state indexes specifiers — so a group visits only the files naming
57
57
  // one of its members instead of re-reading and re-filtering every output.
58
- const state = await clientProfile.timeAsync('foldRead', () => createFoldState(outDir, metafile));
58
+ const state = await clientProfile.timeAsync('foldRead', () => createFoldState(outDir, metafile))
59
59
  for (const group of groups) {
60
- const mutation = { started: false };
60
+ const mutation = { started: false }
61
61
  try {
62
- folded = await foldGroup({ outDir, metafile: folded, buildOptions }, group, mutation, state);
62
+ folded = await foldGroup({ outDir, metafile: folded, buildOptions }, group, mutation, state)
63
63
  } catch (error) {
64
- if (mutation.started) throw error;
65
- reportFoldFailure(group, error);
66
- break;
64
+ if (mutation.started) throw error
65
+ reportFoldFailure(group, error)
66
+ break
67
67
  }
68
68
  }
69
- return folded;
69
+ return folded
70
70
  }
71
71
 
72
72
  function reportFoldFailure(group: string[], error: unknown) {
73
- const names = group.map(member => path.basename(member)).join(', ');
74
- const message = error instanceof Error ? error.message : String(error);
73
+ const names = group.map(member => path.basename(member)).join(', ')
74
+ const message = error instanceof Error ? error.message : String(error)
75
75
  // eslint-disable-next-line turbo/no-undeclared-env-vars
76
76
  if (process.env.PNEXT_CHUNK_FOLD === 'strict') {
77
- throw new Error(`client chunk fold failed for [${names}]: ${message}`);
77
+ throw new Error(`client chunk fold failed for [${names}]: ${message}`)
78
78
  }
79
- console.warn(`pnext: client chunk fold skipped [${names}] — ${message}`);
79
+ console.warn(`pnext: client chunk fold skipped [${names}] — ${message}`)
80
80
  }
81
81
 
82
82
  async function foldGroup(
@@ -85,65 +85,65 @@ async function foldGroup(
85
85
  mutation: { started: boolean },
86
86
  state: FoldState,
87
87
  ): Promise<Metafile> {
88
- if (members.length < 2) return metafile;
88
+ if (members.length < 2) return metafile
89
89
 
90
- const chunksDir = path.join(outDir, 'chunks');
91
- const memberNames = members.map(output => path.basename(output));
92
- if (memberNames.some(name => !existsSync(path.join(chunksDir, name)))) return metafile;
90
+ const chunksDir = path.join(outDir, 'chunks')
91
+ const memberNames = members.map(output => path.basename(output))
92
+ if (memberNames.some(name => !existsSync(path.join(chunksDir, name)))) return metafile
93
93
 
94
- const sources = new Map<string, string>();
95
- for (const name of memberNames) sources.set(name, await readText(path.join(chunksDir, name)));
94
+ const sources = new Map<string, string>()
95
+ for (const name of memberNames) sources.set(name, await readText(path.join(chunksDir, name)))
96
96
 
97
- const aliases = memberAliases(memberNames, sources);
98
- if (!aliases) return metafile;
97
+ const aliases = memberAliases(memberNames, sources)
98
+ if (!aliases) return metafile
99
99
 
100
100
  // Every file that referenced a member has to be rewritten; if any of them
101
101
  // uses a form the rewriter can't express (`import * as`, `export *`, a bare
102
102
  // dynamic `import()` of a member), the fold is abandoned whole.
103
- const rewrites = new Map<string, string>();
104
- const rewrite = memberRewriter(memberNames, aliases, chunksDir);
103
+ const rewrites = new Map<string, string>()
104
+ const rewrite = memberRewriter(memberNames, aliases, chunksDir)
105
105
  const rewritable = clientProfile.time('foldRewrite', () => {
106
106
  for (const file of state.referrersOf(memberNames)) {
107
- if (memberNames.includes(path.basename(file))) continue;
108
- const output = state.outputs.get(file);
109
- if (!output) continue;
110
- const next = rewrite(output.text, file);
111
- if (next === null) return false;
112
- if (next !== output.text) rewrites.set(file, next);
107
+ if (memberNames.includes(path.basename(file))) continue
108
+ const output = state.outputs.get(file)
109
+ if (!output) continue
110
+ const next = rewrite(output.text, file)
111
+ if (next === null) return false
112
+ if (next !== output.text) rewrites.set(file, next)
113
113
  }
114
- return true;
115
- });
116
- if (!rewritable) return metafile;
114
+ return true
115
+ })
116
+ if (!rewritable) return metafile
117
117
 
118
118
  const merged = await clientProfile.timeAsync('foldBuild', () =>
119
119
  buildMergedChunk(chunksDir, memberNames, aliases, buildOptions),
120
- );
121
- if (!merged) return metafile;
120
+ )
121
+ if (!merged) return metafile
122
122
 
123
123
  // Read-only up to here, so failing is skippable. From the first rewrite on the
124
124
  // metafile and the files on disk must be kept in step, so failures are fatal.
125
- mutation.started = true;
125
+ mutation.started = true
126
126
 
127
127
  // The rewrites were produced against a placeholder href — the merged chunk's
128
128
  // name is its own content hash and so isn't known until it is built.
129
129
  await Promise.all(
130
130
  [...rewrites].map(([file, source]) => {
131
- const next = source.replaceAll(MERGED_PLACEHOLDER, merged.name);
132
- state.set(file, foldOutput(next));
133
- return writeText(file, next);
131
+ const next = source.replaceAll(MERGED_PLACEHOLDER, merged.name)
132
+ state.set(file, foldOutput(next))
133
+ return writeText(file, next)
134
134
  }),
135
- );
135
+ )
136
136
  await Promise.all(
137
137
  memberNames.flatMap(name => [
138
138
  unlink(path.join(chunksDir, name)).catch(ignoreMissing),
139
139
  unlink(path.join(chunksDir, `${name}.map`)).catch(ignoreMissing),
140
140
  ]),
141
- );
142
- for (const name of memberNames) state.remove(path.join(chunksDir, name));
141
+ )
142
+ for (const name of memberNames) state.remove(path.join(chunksDir, name))
143
143
  // A later group can reference this merged chunk, and it is what the previous
144
144
  // per-group re-walk picked up off disk; index it the same way.
145
- state.set(path.join(chunksDir, merged.name), foldOutput(merged.text));
146
- return mergeMetafile(metafile, members, path.join(chunksDir, merged.name), merged.bytes);
145
+ state.set(path.join(chunksDir, merged.name), foldOutput(merged.text))
146
+ return mergeMetafile(metafile, members, path.join(chunksDir, merged.name), merged.bytes)
147
147
  }
148
148
 
149
149
  /**
@@ -153,10 +153,10 @@ async function foldGroup(
153
153
  * what a lazily loaded island's CSS/runtime seam resolves against.
154
154
  */
155
155
  function coLoadGroups(metafile: Metafile) {
156
- const dynamicTargets = new Set<string>();
156
+ const dynamicTargets = new Set<string>()
157
157
  for (const output of Object.values(metafile.outputs)) {
158
158
  for (const imported of output.imports ?? []) {
159
- if (imported.kind === 'dynamic-import') dynamicTargets.add(imported.path);
159
+ if (imported.kind === 'dynamic-import') dynamicTargets.add(imported.path)
160
160
  }
161
161
  }
162
162
  // esbuild records an `entryPoint` for the chunk behind every `import()` too,
@@ -164,19 +164,19 @@ function coLoadGroups(metafile: Metafile) {
164
164
  // route entry.
165
165
  const entries = Object.keys(metafile.outputs).filter(
166
166
  file => metafile.outputs[file]?.entryPoint && !dynamicTargets.has(file),
167
- );
168
- if (entries.length === 0) return [];
167
+ )
168
+ if (entries.length === 0) return []
169
169
 
170
- const available = availability(metafile, entries, dynamicTargets);
171
- const roots = [...available.keys()];
172
- const groups = new Map<string, string[]>();
170
+ const available = availability(metafile, entries, dynamicTargets)
171
+ const roots = [...available.keys()]
172
+ const groups = new Map<string, string[]>()
173
173
  for (const file of Object.keys(metafile.outputs)) {
174
- if (!file.endsWith('.js')) continue;
175
- if (entries.includes(file) || dynamicTargets.has(file)) continue;
176
- const mask = roots.map(root => (available.get(root)!.has(file) ? '1' : '0')).join('');
174
+ if (!file.endsWith('.js')) continue
175
+ if (entries.includes(file) || dynamicTargets.has(file)) continue
176
+ const mask = roots.map(root => (available.get(root)!.has(file) ? '1' : '0')).join('')
177
177
  // A chunk no root can reach is dead output; leave it alone.
178
- if (!mask.includes('1')) continue;
179
- (groups.get(mask) ?? groups.set(mask, []).get(mask)!).push(file);
178
+ if (!mask.includes('1')) continue
179
+ ;(groups.get(mask) ?? groups.set(mask, []).get(mask)!).push(file)
180
180
  }
181
181
  return [...groups.values()]
182
182
  .filter(
@@ -187,7 +187,7 @@ function coLoadGroups(metafile: Metafile) {
187
187
  // nobody waits on.
188
188
  entries.some(entry => available.get(entry)!.has(group[0]!)),
189
189
  )
190
- .map(group => [...group].sort());
190
+ .map(group => [...group].sort())
191
191
  }
192
192
 
193
193
  /**
@@ -199,21 +199,21 @@ function availabilityAt(
199
199
  file: string,
200
200
  memo: Map<string, Set<string>>,
201
201
  ) {
202
- const own = available.get(file);
203
- if (own) return own;
202
+ const own = available.get(file)
203
+ if (own) return own
204
204
  // The fixpoint below asks for the same non-root importers once per round per
205
205
  // target, and each answer scans every root's availability set — quadratic in
206
206
  // roots. The memo is cleared whenever a round changes an answer.
207
- const cached = memo.get(file);
208
- if (cached) return cached;
209
- let acc: Set<string> | undefined;
207
+ const cached = memo.get(file)
208
+ if (cached) return cached
209
+ let acc: Set<string> | undefined
210
210
  for (const set of available.values()) {
211
- if (!set.has(file)) continue;
212
- acc = acc === undefined ? new Set(set) : intersect(acc, set);
211
+ if (!set.has(file)) continue
212
+ acc = acc === undefined ? new Set(set) : intersect(acc, set)
213
213
  }
214
- const answer = acc ?? new Set([file]);
215
- memo.set(file, answer);
216
- return answer;
214
+ const answer = acc ?? new Set([file])
215
+ memo.set(file, answer)
216
+ return answer
217
217
  }
218
218
 
219
219
  /**
@@ -222,97 +222,98 @@ function availabilityAt(
222
222
  * importers whose own availability is still an estimate, and lands pessimistic behind route chunks.
223
223
  */
224
224
  function entryFloors(metafile: Metafile, entries: string[], closures: Map<string, Set<string>>) {
225
- const dynamicEdges = new Map<string, string[]>();
225
+ const dynamicEdges = new Map<string, string[]>()
226
226
  for (const [file, output] of Object.entries(metafile.outputs)) {
227
227
  for (const imported of output.imports ?? []) {
228
- if (imported.kind !== 'dynamic-import') continue;
229
- (dynamicEdges.get(file) ?? dynamicEdges.set(file, []).get(file)!).push(imported.path);
228
+ if (imported.kind !== 'dynamic-import') continue
229
+ ;(dynamicEdges.get(file) ?? dynamicEdges.set(file, []).get(file)!).push(imported.path)
230
230
  }
231
231
  }
232
- const floors = new Map<string, Set<string>>();
232
+ const floors = new Map<string, Set<string>>()
233
233
  for (const entry of entries) {
234
- const own = closures.get(entry)!;
235
- const seen = new Set(own);
236
- const queue = [...own];
234
+ const own = closures.get(entry)!
235
+ const seen = new Set(own)
236
+ const queue = [...own]
237
237
  // `queue` grows inside the loop: every chunk this entry can reach by any
238
238
  // chain of static and dynamic imports.
239
239
  for (const current of queue) {
240
240
  for (const target of dynamicEdges.get(current) ?? []) {
241
- if (seen.has(target)) continue;
241
+ if (seen.has(target)) continue
242
242
  for (const file of closures.get(target) ?? staticClosure(metafile, target)) {
243
- if (seen.has(file)) continue;
244
- seen.add(file);
245
- queue.push(file);
243
+ if (seen.has(file)) continue
244
+ seen.add(file)
245
+ queue.push(file)
246
246
  }
247
- const floor = floors.get(target);
248
- floors.set(target, floor ? intersect(floor, own) : new Set(own));
247
+ const floor = floors.get(target)
248
+ floors.set(target, floor ? intersect(floor, own) : new Set(own))
249
249
  }
250
250
  }
251
251
  }
252
- return floors;
252
+ return floors
253
253
  }
254
254
 
255
255
  /** Per load root, the chunks guaranteed to be on the page once it runs. */
256
256
  function availability(metafile: Metafile, entries: string[], dynamicTargets: Set<string>) {
257
- const available = new Map<string, Set<string>>();
258
- const closures = new Map<string, Set<string>>();
257
+ const available = new Map<string, Set<string>>()
258
+ const closures = new Map<string, Set<string>>()
259
259
  for (const root of [...entries, ...dynamicTargets]) {
260
- closures.set(root, staticClosure(metafile, root));
261
- available.set(root, new Set(closures.get(root)));
260
+ closures.set(root, staticClosure(metafile, root))
261
+ available.set(root, new Set(closures.get(root)))
262
262
  }
263
263
  for (const [target, floor] of entryFloors(metafile, entries, closures)) {
264
- const set = available.get(target);
265
- if (set) for (const file of floor) set.add(file);
264
+ const set = available.get(target)
265
+ if (set) for (const file of floor) set.add(file)
266
266
  }
267
- const importersOf = new Map<string, string[]>();
267
+ const importersOf = new Map<string, string[]>()
268
268
  for (const [file, output] of Object.entries(metafile.outputs)) {
269
269
  for (const imported of output.imports ?? []) {
270
- if (imported.kind !== 'dynamic-import') continue;
271
- (importersOf.get(imported.path) ?? importersOf.set(imported.path, []).get(imported.path)!)
272
- .push(file);
270
+ if (imported.kind !== 'dynamic-import') continue
271
+ ;(
272
+ importersOf.get(imported.path) ?? importersOf.set(imported.path, []).get(imported.path)!
273
+ ).push(file)
273
274
  }
274
275
  }
275
276
  // `available` only ever grows, so iterating to a fixed point terminates; the
276
277
  // bound is the number of roots (each round settles at least one).
277
- const memo = new Map<string, Set<string>>();
278
+ const memo = new Map<string, Set<string>>()
278
279
  for (let round = 0; round < dynamicTargets.size + 1; round += 1) {
279
- let changed = false;
280
+ let changed = false
280
281
  for (const target of dynamicTargets) {
281
- const importers = importersOf.get(target) ?? [];
282
- let inherited: Set<string> | undefined;
282
+ const importers = importersOf.get(target) ?? []
283
+ let inherited: Set<string> | undefined
283
284
  for (const importer of importers) {
284
- const from = availabilityAt(available, importer, memo);
285
- inherited = inherited === undefined ? new Set(from) : intersect(inherited, from);
285
+ const from = availabilityAt(available, importer, memo)
286
+ inherited = inherited === undefined ? new Set(from) : intersect(inherited, from)
286
287
  }
287
- const next = new Set([...closures.get(target)!, ...(inherited ?? [])]);
288
+ const next = new Set([...closures.get(target)!, ...(inherited ?? [])])
288
289
  if (next.size !== available.get(target)!.size) {
289
- available.set(target, next);
290
- memo.clear();
291
- changed = true;
290
+ available.set(target, next)
291
+ memo.clear()
292
+ changed = true
292
293
  }
293
294
  }
294
- if (!changed) break;
295
+ if (!changed) break
295
296
  }
296
- return available;
297
+ return available
297
298
  }
298
299
 
299
300
  function staticClosure(metafile: Metafile, root: string) {
300
- const seen = new Set([root]);
301
- const queue = [root];
301
+ const seen = new Set([root])
302
+ const queue = [root]
302
303
  // `queue` grows inside the loop — a breadth-first walk of the chunk graph.
303
304
  for (const current of queue) {
304
305
  for (const imported of metafile.outputs[current]?.imports ?? []) {
305
- if (imported.kind !== 'import-statement') continue;
306
- if (seen.has(imported.path)) continue;
307
- seen.add(imported.path);
308
- queue.push(imported.path);
306
+ if (imported.kind !== 'import-statement') continue
307
+ if (seen.has(imported.path)) continue
308
+ seen.add(imported.path)
309
+ queue.push(imported.path)
309
310
  }
310
311
  }
311
- return seen;
312
+ return seen
312
313
  }
313
314
 
314
315
  function intersect(a: Set<string>, b: Set<string>) {
315
- return new Set([...a].filter(item => b.has(item)));
316
+ return new Set([...a].filter(item => b.has(item)))
316
317
  }
317
318
 
318
319
  /**
@@ -320,24 +321,26 @@ function intersect(a: Set<string>, b: Set<string>) {
320
321
  * collide across chunks, so the barrel renames them apart.
321
322
  */
322
323
  function memberAliases(memberNames: string[], sources: Map<string, string>) {
323
- const aliases = new Map<string, Map<string, string>>();
324
+ const aliases = new Map<string, Map<string, string>>()
324
325
  for (const [index, name] of memberNames.entries()) {
325
- const source = sources.get(name)!;
326
+ const source = sources.get(name)!
326
327
  // A star re-export can't be aliased name-by-name; nothing to fold safely.
327
- if (/export\s*\*/.test(source)) return undefined;
328
+ if (/export\s*\*/.test(source)) return undefined
328
329
  // The export clause is the chunk's last one — a linked sourcemap comment
329
330
  // can follow it, so anchor on "last `export{…}` that isn't a re-export"
330
331
  // rather than on end-of-file.
331
- const clauses = [...source.matchAll(new RegExp(String.raw`export\s*${namedClause}\s*(?!from)`, 'g'))];
332
- const clause = clauses.at(-1);
333
- const names = new Map<string, string>();
332
+ const clauses = [
333
+ ...source.matchAll(new RegExp(String.raw`export\s*${namedClause}\s*(?!from)`, 'g')),
334
+ ]
335
+ const clause = clauses.at(-1)
336
+ const names = new Map<string, string>()
334
337
  for (const part of clause?.[1]?.split(',') ?? []) {
335
- const exported = part.split(' as ').at(-1)?.trim();
336
- if (exported) names.set(exported, `$${index}${exported}`);
338
+ const exported = part.split(' as ').at(-1)?.trim()
339
+ if (exported) names.set(exported, `$${index}${exported}`)
337
340
  }
338
- aliases.set(name, names);
341
+ aliases.set(name, names)
339
342
  }
340
- return aliases;
343
+ return aliases
341
344
  }
342
345
 
343
346
  /**
@@ -350,53 +353,55 @@ function memberRewriter(
350
353
  aliases: Map<string, Map<string, string>>,
351
354
  chunksDir: string,
352
355
  ) {
353
- const memberPattern = memberNames.map(escapeRegex).join('|');
354
- const specifier = String.raw`"\.[^"]*(?:${memberPattern})"`;
356
+ const memberPattern = memberNames.map(escapeRegex).join('|')
357
+ const specifier = String.raw`"\.[^"]*(?:${memberPattern})"`
355
358
  // Forms that can't be expressed against a renaming barrel.
356
359
  const vetoes = [
357
360
  new RegExp(String.raw`import\s*\*\s*as\s*[\w$]+\s*from\s*${specifier}`),
358
361
  new RegExp(String.raw`export\s*\*[^;]*from\s*${specifier}`),
359
362
  new RegExp(String.raw`import\(\s*${specifier}`),
360
- ];
363
+ ]
361
364
  const namedImport = new RegExp(
362
365
  String.raw`(import|export)\s*${namedClause}\s*from\s*(${specifier})`,
363
366
  'g',
364
- );
365
- const sideEffectImport = new RegExp(String.raw`import\s*(${specifier})`, 'g');
367
+ )
368
+ const sideEffectImport = new RegExp(String.raw`import\s*(${specifier})`, 'g')
366
369
 
367
- const nameOf = (spec: string) => memberNames.find(name => spec.includes(name))!;
370
+ const nameOf = (spec: string) => memberNames.find(name => spec.includes(name))!
368
371
  const rename = (clause: string, member: string) =>
369
372
  clause
370
373
  .split(',')
371
374
  .map(part => {
372
- const [imported, local] = part.split(' as ').map(item => item.trim());
373
- if (!imported) return part;
374
- const alias = aliases.get(member)?.get(imported);
375
- if (!alias) return part;
376
- return `${alias} as ${local ?? imported}`;
375
+ const [imported, local] = part.split(' as ').map(item => item.trim())
376
+ if (!imported) return part
377
+ const alias = aliases.get(member)?.get(imported)
378
+ if (!alias) return part
379
+ return `${alias} as ${local ?? imported}`
377
380
  })
378
- .join(',');
381
+ .join(',')
379
382
 
380
383
  return (source: string, file: string) => {
381
- for (const veto of vetoes) if (veto.test(source)) return null;
384
+ for (const veto of vetoes) if (veto.test(source)) return null
382
385
  const mergedHref = path
383
386
  .relative(path.dirname(file), path.join(chunksDir, MERGED_PLACEHOLDER))
384
387
  .split(path.sep)
385
- .join('/');
386
- const href = mergedHref.startsWith('.') ? mergedHref : `./${mergedHref}`;
387
- return source
388
- .replace(
389
- namedImport,
390
- (_all, keyword: string, clause: string, spec: string) =>
391
- `${keyword}{${rename(clause, nameOf(spec))}}from"${href}"`,
392
- )
393
- // Side-effect-only imports of a member collapse onto the merged chunk.
394
- .replace(sideEffectImport, () => `import"${href}"`);
395
- };
388
+ .join('/')
389
+ const href = mergedHref.startsWith('.') ? mergedHref : `./${mergedHref}`
390
+ return (
391
+ source
392
+ .replace(
393
+ namedImport,
394
+ (_all, keyword: string, clause: string, spec: string) =>
395
+ `${keyword}{${rename(clause, nameOf(spec))}}from"${href}"`,
396
+ )
397
+ // Side-effect-only imports of a member collapse onto the merged chunk.
398
+ .replace(sideEffectImport, () => `import"${href}"`)
399
+ )
400
+ }
396
401
  }
397
402
 
398
403
  /** Stands in for the merged chunk's final (content-hashed) name. */
399
- const MERGED_PLACEHOLDER = 'pnext-merged';
404
+ const MERGED_PLACEHOLDER = 'pnext-merged'
400
405
 
401
406
  async function buildMergedChunk(
402
407
  chunksDir: string,
@@ -406,16 +411,16 @@ async function buildMergedChunk(
406
411
  ) {
407
412
  const barrel = memberNames
408
413
  .map(name => {
409
- const names = aliases.get(name)!;
410
- const spec = JSON.stringify(`./${name}`);
411
- if (names.size === 0) return `import ${spec};`;
412
- const clause = [...names].map(([exported, alias]) => `${exported} as ${alias}`).join(',');
413
- return `export {${clause}} from ${spec};`;
414
+ const names = aliases.get(name)!
415
+ const spec = JSON.stringify(`./${name}`)
416
+ if (names.size === 0) return `import ${spec};`
417
+ const clause = [...names].map(([exported, alias]) => `${exported} as ${alias}`).join(',')
418
+ return `export {${clause}} from ${spec};`
414
419
  })
415
- .join('\n');
420
+ .join('\n')
416
421
 
417
- const barrelPath = path.join(chunksDir, '__pnext-fold-barrel.js');
418
- await writeText(barrelPath, barrel);
422
+ const barrelPath = path.join(chunksDir, '__pnext-fold-barrel.js')
423
+ await writeText(barrelPath, barrel)
419
424
  try {
420
425
  const result = await build({
421
426
  minify: buildOptions.minify,
@@ -433,94 +438,94 @@ async function buildMergedChunk(
433
438
  name: 'pnext-fold-members',
434
439
  setup(builder) {
435
440
  builder.onResolve({ filter: /.*/ }, args => {
436
- if (args.kind === 'entry-point') return undefined;
437
- const base = path.basename(args.path);
438
- if (memberNames.includes(base)) return { path: path.join(chunksDir, base) };
439
- return { path: args.path, external: true };
440
- });
441
+ if (args.kind === 'entry-point') return undefined
442
+ const base = path.basename(args.path)
443
+ if (memberNames.includes(base)) return { path: path.join(chunksDir, base) }
444
+ return { path: args.path, external: true }
445
+ })
441
446
  },
442
447
  },
443
448
  ],
444
- });
445
- const js = result.outputFiles?.find(file => file.path.endsWith('.js'));
446
- if (!js) return undefined;
447
- const hash = createHash('sha256').update(js.contents).digest('hex').slice(0, 8).toUpperCase();
448
- const name = `shared-init-${hash}.js`;
449
- const map = result.outputFiles?.find(file => file.path.endsWith('.js.map'));
450
- let contents = new TextDecoder().decode(js.contents);
449
+ })
450
+ const js = result.outputFiles?.find(file => file.path.endsWith('.js'))
451
+ if (!js) return undefined
452
+ const hash = createHash('sha256').update(js.contents).digest('hex').slice(0, 8).toUpperCase()
453
+ const name = `shared-init-${hash}.js`
454
+ const map = result.outputFiles?.find(file => file.path.endsWith('.js.map'))
455
+ let contents = new TextDecoder().decode(js.contents)
451
456
  if (map) {
452
- contents = contents.replace(/# sourceMappingURL=.*$/m, `# sourceMappingURL=${name}.map`);
453
- await writeText(path.join(chunksDir, `${name}.map`), new TextDecoder().decode(map.contents));
457
+ contents = contents.replace(/# sourceMappingURL=.*$/m, `# sourceMappingURL=${name}.map`)
458
+ await writeText(path.join(chunksDir, `${name}.map`), new TextDecoder().decode(map.contents))
454
459
  }
455
- await writeText(path.join(chunksDir, name), contents);
456
- return { name, text: contents, bytes: Buffer.byteLength(contents) };
460
+ await writeText(path.join(chunksDir, name), contents)
461
+ return { name, text: contents, bytes: Buffer.byteLength(contents) }
457
462
  } finally {
458
- await unlink(barrelPath).catch(ignoreMissing);
463
+ await unlink(barrelPath).catch(ignoreMissing)
459
464
  }
460
465
  }
461
466
 
462
467
  /** An output file's text plus the relative specifiers it imports through. */
463
468
  interface FoldOutput {
464
- text: string;
465
- specifiers: string[];
469
+ text: string
470
+ specifiers: string[]
466
471
  }
467
472
 
468
473
  function foldOutput(text: string): FoldOutput {
469
- return { text, specifiers: [...text.matchAll(/"\.[^"]*"/g)].map(match => match[0]) };
474
+ return { text, specifiers: [...text.matchAll(/"\.[^"]*"/g)].map(match => match[0]) }
470
475
  }
471
476
 
472
477
  /** Every output's text plus a basename->referrers index, read once. Chunk names
473
478
  * are content-hashed, so a specifier naming a member spells it as its basename. */
474
479
  interface FoldState {
475
- outputs: Map<string, FoldOutput>;
480
+ outputs: Map<string, FoldOutput>
476
481
  /** Files whose specifiers name any of `names`, each visited once. */
477
- referrersOf(names: string[]): Iterable<string>;
478
- set(file: string, output: FoldOutput): void;
479
- remove(file: string): void;
482
+ referrersOf(names: string[]): Iterable<string>
483
+ set(file: string, output: FoldOutput): void
484
+ remove(file: string): void
480
485
  }
481
486
 
482
487
  async function createFoldState(outDir: string, metafile: Metafile): Promise<FoldState> {
483
- const outputs = new Map<string, FoldOutput>();
484
- const referrers = new Map<string, Set<string>>();
488
+ const outputs = new Map<string, FoldOutput>()
489
+ const referrers = new Map<string, Set<string>>()
485
490
 
486
491
  const index = (file: string, output: FoldOutput, add: boolean) => {
487
492
  for (const spec of output.specifiers) {
488
- const base = path.basename(spec.slice(1, -1));
489
- const set = referrers.get(base) ?? referrers.set(base, new Set()).get(base)!;
490
- if (add) set.add(file);
491
- else set.delete(file);
493
+ const base = path.basename(spec.slice(1, -1))
494
+ const set = referrers.get(base) ?? referrers.set(base, new Set()).get(base)!
495
+ if (add) set.add(file)
496
+ else set.delete(file)
492
497
  }
493
- };
498
+ }
494
499
 
495
500
  for (const name of Object.keys(metafile.outputs)) {
496
- if (!name.endsWith('.js')) continue;
497
- const resolved = path.resolve(name);
498
- const local = existsSync(resolved) ? resolved : path.join(outDir, path.basename(name));
499
- if (outputs.has(local) || !existsSync(local)) continue;
500
- const output = foldOutput(await readText(local));
501
- outputs.set(local, output);
502
- index(local, output, true);
501
+ if (!name.endsWith('.js')) continue
502
+ const resolved = path.resolve(name)
503
+ const local = existsSync(resolved) ? resolved : path.join(outDir, path.basename(name))
504
+ if (outputs.has(local) || !existsSync(local)) continue
505
+ const output = foldOutput(await readText(local))
506
+ outputs.set(local, output)
507
+ index(local, output, true)
503
508
  }
504
509
 
505
510
  return {
506
511
  outputs,
507
512
  referrersOf(names) {
508
- const seen = new Set<string>();
509
- for (const name of names) for (const file of referrers.get(name) ?? []) seen.add(file);
510
- return seen;
513
+ const seen = new Set<string>()
514
+ for (const name of names) for (const file of referrers.get(name) ?? []) seen.add(file)
515
+ return seen
511
516
  },
512
517
  set(file, output) {
513
- const previous = outputs.get(file);
514
- if (previous) index(file, previous, false);
515
- outputs.set(file, output);
516
- index(file, output, true);
518
+ const previous = outputs.get(file)
519
+ if (previous) index(file, previous, false)
520
+ outputs.set(file, output)
521
+ index(file, output, true)
517
522
  },
518
523
  remove(file) {
519
- const previous = outputs.get(file);
520
- if (previous) index(file, previous, false);
521
- outputs.delete(file);
524
+ const previous = outputs.get(file)
525
+ if (previous) index(file, previous, false)
526
+ outputs.delete(file)
522
527
  },
523
- };
528
+ }
524
529
  }
525
530
 
526
531
  /** The shipped graph, with the members collapsed into the merged chunk. */
@@ -530,37 +535,37 @@ function mergeMetafile(
530
535
  mergedPath: string,
531
536
  bytes: number,
532
537
  ): Metafile {
533
- const memberSet = new Set(members);
534
- const merged = path.relative(process.cwd(), mergedPath);
535
- const outputs: Metafile['outputs'] = {};
536
- const mergedImports: Metafile['outputs'][string]['imports'] = [];
537
- const mergedInputs: Metafile['outputs'][string]['inputs'] = {};
538
+ const memberSet = new Set(members)
539
+ const merged = path.relative(process.cwd(), mergedPath)
540
+ const outputs: Metafile['outputs'] = {}
541
+ const mergedImports: Metafile['outputs'][string]['imports'] = []
542
+ const mergedInputs: Metafile['outputs'][string]['inputs'] = {}
538
543
 
539
544
  for (const [file, output] of Object.entries(metafile.outputs)) {
540
545
  const remap = (list: typeof output.imports) => {
541
- const seen = new Set<string>();
546
+ const seen = new Set<string>()
542
547
  return list.flatMap(imported => {
543
- const target = memberSet.has(imported.path) ? merged : imported.path;
544
- const key = `${target}:${imported.kind}`;
545
- if (seen.has(key)) return [];
546
- seen.add(key);
547
- return [{ ...imported, path: target }];
548
- });
549
- };
548
+ const target = memberSet.has(imported.path) ? merged : imported.path
549
+ const key = `${target}:${imported.kind}`
550
+ if (seen.has(key)) return []
551
+ seen.add(key)
552
+ return [{ ...imported, path: target }]
553
+ })
554
+ }
550
555
  if (memberSet.has(file)) {
551
- Object.assign(mergedInputs, output.inputs);
556
+ Object.assign(mergedInputs, output.inputs)
552
557
  for (const imported of remap(output.imports ?? [])) {
553
- if (imported.path !== merged) mergedImports.push(imported);
558
+ if (imported.path !== merged) mergedImports.push(imported)
554
559
  }
555
- continue;
560
+ continue
556
561
  }
557
- outputs[file] = { ...output, imports: remap(output.imports ?? []) };
562
+ outputs[file] = { ...output, imports: remap(output.imports ?? []) }
558
563
  }
559
564
  outputs[merged] = {
560
565
  imports: mergedImports,
561
566
  exports: [],
562
567
  inputs: mergedInputs,
563
568
  bytes,
564
- };
565
- return { ...metafile, outputs };
569
+ }
570
+ return { ...metafile, outputs }
566
571
  }