@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
@@ -33,50 +33,50 @@
33
33
  * on "No matching export in X for import Y", so a clean re-bundle proves the facade resolves and
34
34
  * exports everything it claims. Packages left with no names (default-only) stay inlined.
35
35
  */
36
- import { appendFileSync, existsSync, mkdirSync } from 'node:fs';
37
- import { copyFile, mkdir, readFile } from 'node:fs/promises';
38
- import path from 'node:path';
39
- import { fileURLToPath } from 'node:url';
40
- import { build } from '../utils/esbuild';
41
- import { isCommonJsModuleSource } from '../resolve/imports';
42
- import { outputSpecifiers } from '../dev/module-transform';
43
- import { escapeRegex, isIdentifier, uniqueIdentifier } from '../utils/source';
44
- import { cachedExistsSync } from '../utils/fs-cache';
45
- import { writeFileAtomic } from '../utils/fs';
36
+ import { appendFileSync, existsSync, mkdirSync } from 'node:fs'
37
+ import { copyFile, mkdir, readFile } from 'node:fs/promises'
38
+ import path from 'node:path'
39
+ import { fileURLToPath } from 'node:url'
40
+ import { build } from '../utils/esbuild'
41
+ import { isCommonJsModuleSource } from '../resolve/imports'
42
+ import { outputSpecifiers } from './module-transform'
43
+ import { escapeRegex, isIdentifier, uniqueIdentifier } from '../utils/code'
44
+ import { cachedExistsSync } from '../utils/fs-cache'
45
+ import { writeFileAtomic } from '../utils/fs'
46
46
 
47
47
  // --------------------------------------------------------------------------
48
48
  // demand scheduling
49
49
  // --------------------------------------------------------------------------
50
50
 
51
51
  export interface VendorGroupMember {
52
- specifier: string;
52
+ specifier: string
53
53
  /** Resolved entry file for this subpath. */
54
- entry: string;
54
+ entry: string
55
55
  /** Artifact path, identical to the one a solo build of this subpath writes. */
56
- file: string;
56
+ file: string
57
57
  }
58
58
 
59
59
  export interface VendorGroupPlan {
60
- key: string;
60
+ key: string
61
61
  /** The demanded subpath — the only thing a group is ever allowed to compile. */
62
- member: VendorGroupMember;
62
+ member: VendorGroupMember
63
63
  /** `external` are siblings already on disk: referenced, never re-parsed. */
64
64
  build: (
65
65
  members: VendorGroupMember[],
66
66
  external: readonly VendorGroupMember[],
67
67
  nested: boolean,
68
- ) => Promise<void>;
68
+ ) => Promise<void>
69
69
  }
70
70
 
71
71
  export interface VendorBuildPlan {
72
72
  /** Artifact key — the ONLY dedup identity. Importer directories never enter it. */
73
- key: string;
74
- file: string;
73
+ key: string
74
+ file: string
75
75
  /** Disk cache / no-compile fast paths; a returned path settles the demand. */
76
- prepare: () => Promise<string | undefined>;
76
+ prepare: () => Promise<string | undefined>
77
77
  /** `nested` marks a build raised from inside another: it may never wait. */
78
- buildOne: (nested: boolean) => Promise<string>;
79
- group?: VendorGroupPlan;
78
+ buildOne: (nested: boolean) => Promise<string>
79
+ group?: VendorGroupPlan
80
80
  }
81
81
 
82
82
  /**
@@ -85,31 +85,31 @@ export interface VendorBuildPlan {
85
85
  */
86
86
  interface VendorPromotable {
87
87
  /** Set only while the work sits in the queue; cleared once it runs. */
88
- promote?: () => void;
88
+ promote?: () => void
89
89
  }
90
90
 
91
91
  interface PendingBundle extends VendorPromotable {
92
- promise: Promise<string>;
93
- result?: string;
92
+ promise: Promise<string>
93
+ result?: string
94
94
  }
95
95
 
96
96
  interface VendorGroupState {
97
97
  /** Demanded, not yet compiled. */
98
- pending: Map<string, VendorGroupMember>;
98
+ pending: Map<string, VendorGroupMember>
99
99
  /** Compiled by this group. */
100
- produced: Map<string, VendorGroupMember>;
100
+ produced: Map<string, VendorGroupMember>
101
101
  /** Handed back to the single-bundle path — a round with nothing to share. */
102
- solo: Map<string, VendorGroupMember>;
103
- round?: Promise<void>;
102
+ solo: Map<string, VendorGroupMember>
103
+ round?: Promise<void>
104
104
  /** The current round's queue ticket, while it is still waiting for a slot. */
105
- ticket?: VendorPromotable;
106
- rounds: number;
105
+ ticket?: VendorPromotable
106
+ rounds: number
107
107
  /** Once closed (failed, or out of rounds) every member builds alone. */
108
- closed: boolean;
108
+ closed: boolean
109
109
  }
110
110
 
111
- const pendingBundles = new Map<string, PendingBundle>();
112
- const vendorGroups = new Map<string, VendorGroupState>();
111
+ const pendingBundles = new Map<string, PendingBundle>()
112
+ const vendorGroups = new Map<string, VendorGroupState>()
113
113
 
114
114
  // §4e worker sweep (1/2/4/6/7/8/12 → 3.96/2.03/1.68/1.62/1.55/1.94/1.98 s):
115
115
  // 6-7 concurrent builds is the optimum and going past core count regresses.
@@ -117,7 +117,7 @@ const VENDOR_CONCURRENCY = Math.max(
117
117
  1,
118
118
  // eslint-disable-next-line turbo/no-undeclared-env-vars
119
119
  Number(process.env.PNEXT_VENDOR_CONCURRENCY) || 7,
120
- );
120
+ )
121
121
  /**
122
122
  * Ceiling on builds that have STARTED and not finished, which is a different resource from the
123
123
  * compute slots: a build waiting on a dependency gives its slot back (see `outsideVendorSlot`) but
@@ -129,11 +129,11 @@ const VENDOR_OPEN_LIMIT = Math.max(
129
129
  VENDOR_CONCURRENCY,
130
130
  // eslint-disable-next-line turbo/no-undeclared-env-vars
131
131
  Number(process.env.PNEXT_VENDOR_OPEN_LIMIT) || VENDOR_CONCURRENCY * 8,
132
- );
133
- let vendorActive = 0;
134
- let vendorOpen = 0;
132
+ )
133
+ let vendorActive = 0
134
+ let vendorOpen = 0
135
135
  /** Queued starts; each returns whether it actually took the slot. */
136
- const vendorQueue: (() => boolean)[] = [];
136
+ const vendorQueue: (() => boolean)[] = []
137
137
 
138
138
  /**
139
139
  * Run a vendor build under the concurrency cap. Nested builds - raised by a running build's own resolve
@@ -149,31 +149,35 @@ function withVendorSlot<T>(
149
149
  task: () => Promise<T>,
150
150
  ticket?: VendorPromotable,
151
151
  ): Promise<T> {
152
- return watchVendorWait(nested ? 'slot-nested' : 'slot', label, new Promise<T>((resolve, reject) => {
153
- let started = false;
154
- const run = () => {
155
- if (started) return false;
156
- started = true;
157
- if (ticket) ticket.promote = undefined;
158
- vendorActive += 1;
159
- vendorOpen += 1;
160
- const span = openVendorSpan(label, nested);
161
- task()
162
- .then(resolve, reject)
163
- .finally(() => {
164
- vendorActive -= 1;
165
- vendorOpen -= 1;
166
- closeVendorSpan(span);
167
- drainVendorQueue();
168
- });
169
- return true;
170
- };
171
- if (nested || vendorActive < VENDOR_CONCURRENCY) run();
172
- else {
173
- if (ticket) ticket.promote = run;
174
- vendorQueue.push(run);
175
- }
176
- }));
152
+ return watchVendorWait(
153
+ nested ? 'slot-nested' : 'slot',
154
+ label,
155
+ new Promise<T>((resolve, reject) => {
156
+ let started = false
157
+ const run = () => {
158
+ if (started) return false
159
+ started = true
160
+ if (ticket) ticket.promote = undefined
161
+ vendorActive += 1
162
+ vendorOpen += 1
163
+ const span = openVendorSpan(label, nested)
164
+ task()
165
+ .then(resolve, reject)
166
+ .finally(() => {
167
+ vendorActive -= 1
168
+ vendorOpen -= 1
169
+ closeVendorSpan(span)
170
+ drainVendorQueue()
171
+ })
172
+ return true
173
+ }
174
+ if (nested || vendorActive < VENDOR_CONCURRENCY) run()
175
+ else {
176
+ if (ticket) ticket.promote = run
177
+ vendorQueue.push(run)
178
+ }
179
+ }),
180
+ )
177
181
  }
178
182
 
179
183
  /**
@@ -184,22 +188,22 @@ function withVendorSlot<T>(
184
188
  export async function outsideVendorSlot<T>(task: () => Promise<T>): Promise<T> {
185
189
  // At the open ceiling, keeping the slot IS the back-pressure: yielding it
186
190
  // would only admit one more bundler to sit open alongside this one.
187
- if (vendorOpen >= VENDOR_OPEN_LIMIT) return task();
188
- vendorActive -= 1;
189
- drainVendorQueue();
191
+ if (vendorOpen >= VENDOR_OPEN_LIMIT) return task()
192
+ vendorActive -= 1
193
+ drainVendorQueue()
190
194
  try {
191
- return await task();
195
+ return await task()
192
196
  } finally {
193
- vendorActive += 1;
197
+ vendorActive += 1
194
198
  }
195
199
  }
196
200
 
197
201
  /** Hand the freed slot to the first queued build that has not been promoted. */
198
202
  function drainVendorQueue() {
199
203
  while (vendorActive < VENDOR_CONCURRENCY) {
200
- const next = vendorQueue.shift();
201
- if (!next) return;
202
- if (next()) return;
204
+ const next = vendorQueue.shift()
205
+ if (!next) return
206
+ if (next()) return
203
207
  }
204
208
  }
205
209
 
@@ -209,7 +213,7 @@ function drainVendorQueue() {
209
213
  * to move.
210
214
  */
211
215
  function promoteVendorWork(work: VendorPromotable | undefined) {
212
- work?.promote?.();
216
+ work?.promote?.()
213
217
  }
214
218
 
215
219
  // --------------------------------------------------------------------------
@@ -218,26 +222,26 @@ function promoteVendorWork(work: VendorPromotable | undefined) {
218
222
 
219
223
  /** One build's occupancy of a worker slot — the active-builds-over-time input. */
220
224
  export interface VendorBuildSpan {
221
- label: string;
222
- nested: boolean;
223
- startMs: number;
224
- endMs?: number;
225
+ label: string
226
+ nested: boolean
227
+ startMs: number
228
+ endMs?: number
225
229
  }
226
230
 
227
- const vendorSpans: VendorBuildSpan[] = [];
231
+ const vendorSpans: VendorBuildSpan[] = []
228
232
 
229
233
  // eslint-disable-next-line turbo/no-undeclared-env-vars
230
- const vendorTracing = () => process.env.PNEXT_VENDOR_TRACE === '1';
234
+ const vendorTracing = () => process.env.PNEXT_VENDOR_TRACE === '1'
231
235
 
232
236
  function openVendorSpan(label: string, nested: boolean) {
233
- if (!vendorTracing()) return undefined;
234
- const span: VendorBuildSpan = { label, nested, startMs: performance.now() };
235
- vendorSpans.push(span);
236
- return span;
237
+ if (!vendorTracing()) return undefined
238
+ const span: VendorBuildSpan = { label, nested, startMs: performance.now() }
239
+ vendorSpans.push(span)
240
+ return span
237
241
  }
238
242
 
239
243
  function closeVendorSpan(span: VendorBuildSpan | undefined) {
240
- if (span) span.endMs = performance.now();
244
+ if (span) span.endMs = performance.now()
241
245
  }
242
246
 
243
247
  // --------------------------------------------------------------------------
@@ -248,27 +252,27 @@ function closeVendorSpan(span: VendorBuildSpan | undefined) {
248
252
  // bench/tools/vendor-analyze.ts; `=1` still means the in-memory spans only.
249
253
  const vendorTraceFile = (() => {
250
254
  // eslint-disable-next-line turbo/no-undeclared-env-vars
251
- const value = process.env.PNEXT_VENDOR_TRACE;
252
- if (!value || !(value.includes('/') || value.endsWith('.jsonl'))) return undefined;
253
- return path.resolve(value);
254
- })();
255
+ const value = process.env.PNEXT_VENDOR_TRACE
256
+ if (!value || !(value.includes('/') || value.endsWith('.jsonl'))) return undefined
257
+ return path.resolve(value)
258
+ })()
255
259
 
256
- let vendorTraceDirReady = false;
257
- let vendorTraceSeq = 0;
260
+ let vendorTraceDirReady = false
261
+ let vendorTraceSeq = 0
258
262
 
259
- export const vendorTraceEnabled = () => vendorTraceFile !== undefined;
263
+ export const vendorTraceEnabled = () => vendorTraceFile !== undefined
260
264
 
261
- export const nextVendorTraceSeq = () => vendorTraceSeq++;
265
+ export const nextVendorTraceSeq = () => vendorTraceSeq++
262
266
 
263
267
  /** Append one JSONL row, stamped with the wall clock the analyzer unions on. */
264
268
  export function vendorTraceRow(row: Record<string, unknown>) {
265
- if (!vendorTraceFile) return;
269
+ if (!vendorTraceFile) return
266
270
  try {
267
271
  if (!vendorTraceDirReady) {
268
- mkdirSync(path.dirname(vendorTraceFile), { recursive: true });
269
- vendorTraceDirReady = true;
272
+ mkdirSync(path.dirname(vendorTraceFile), { recursive: true })
273
+ vendorTraceDirReady = true
270
274
  }
271
- appendFileSync(vendorTraceFile, `${JSON.stringify({ ...row, w: Date.now() })}\n`);
275
+ appendFileSync(vendorTraceFile, `${JSON.stringify({ ...row, w: Date.now() })}\n`)
272
276
  } catch {
273
277
  // Tracing must never fail a build.
274
278
  }
@@ -276,7 +280,7 @@ export function vendorTraceRow(row: Record<string, unknown>) {
276
280
 
277
281
  /** Whether this artifact key already has an in-flight or settled build. */
278
282
  export function vendorBundleMemHit(key: string) {
279
- return pendingBundles.has(key);
283
+ return pendingBundles.has(key)
280
284
  }
281
285
 
282
286
  // --------------------------------------------------------------------------
@@ -288,39 +292,43 @@ export function vendorBundleMemHit(key: string) {
288
292
  * without it, a promise that never settles is indistinguishable from slow work. Entries are removed
289
293
  * on settle, so a healthy pipeline holds none.
290
294
  */
291
- const vendorWaits = new Map<number, { label: string; kind: string; startMs: number }>();
292
- let vendorWaitSeq = 0;
295
+ const vendorWaits = new Map<number, { label: string; kind: string; startMs: number }>()
296
+ let vendorWaitSeq = 0
293
297
 
294
298
  const VENDOR_STALL_MS = Math.max(
295
299
  0,
296
300
  // eslint-disable-next-line turbo/no-undeclared-env-vars
297
301
  Number(process.env.PNEXT_VENDOR_STALL_MS ?? 20_000),
298
- );
302
+ )
299
303
 
300
304
  /** Waits older than the threshold, oldest first — the stall report's payload. */
301
305
  function stalledVendorWaits(now: number) {
302
306
  return [...vendorWaits.values()]
303
307
  .filter(wait => now - wait.startMs >= VENDOR_STALL_MS)
304
- .sort((a, b) => a.startMs - b.startMs);
308
+ .sort((a, b) => a.startMs - b.startMs)
305
309
  }
306
310
 
307
- let stallTimer: ReturnType<typeof setInterval> | undefined;
311
+ let stallTimer: ReturnType<typeof setInterval> | undefined
308
312
 
309
313
  function reportVendorStall() {
310
- const now = performance.now();
311
- const stalled = stalledVendorWaits(now);
312
- if (stalled.length === 0) return;
313
- const trace = vendorPipelineTrace();
314
+ const now = performance.now()
315
+ const stalled = stalledVendorWaits(now)
316
+ if (stalled.length === 0) return
317
+ const trace = vendorPipelineTrace()
314
318
  console.error(
315
319
  [
316
320
  `PNext vendor pipeline stalled: ${stalled.length} wait(s) over ${Math.round(VENDOR_STALL_MS)}ms.`,
317
321
  ` slots active=${trace.active}/${trace.concurrency} open=${trace.open}/${trace.openLimit} queued=${trace.queued}`,
318
322
  ` groups=${vendorGroups.size} unsettled bundles=${trace.unsettled.length}`,
319
- ...stalled.slice(0, 20).map(w => ` [${w.kind}] ${w.label} waiting ${Math.round(now - w.startMs)}ms`),
320
- ...(trace.unsettled.length ? [` unsettled: ${trace.unsettled.slice(0, 20).join(', ')}`] : []),
323
+ ...stalled
324
+ .slice(0, 20)
325
+ .map(w => ` [${w.kind}] ${w.label} waiting ${Math.round(now - w.startMs)}ms`),
326
+ ...(trace.unsettled.length
327
+ ? [` unsettled: ${trace.unsettled.slice(0, 20).join(', ')}`]
328
+ : []),
321
329
  ` set PNEXT_VENDOR_STALL_MS=0 to silence, PNEXT_VENDOR_GROUP=0 to bisect grouping.`,
322
330
  ].join('\n'),
323
- );
331
+ )
324
332
  }
325
333
 
326
334
  /**
@@ -328,31 +336,31 @@ function reportVendorStall() {
328
336
  * changes what resolves — so arming it cannot itself introduce a stall.
329
337
  */
330
338
  function watchVendorWait<T>(kind: string, label: string, promise: Promise<T>): Promise<T> {
331
- if (VENDOR_STALL_MS <= 0) return promise;
332
- const id = vendorWaitSeq++;
333
- vendorWaits.set(id, { kind, label, startMs: performance.now() });
339
+ if (VENDOR_STALL_MS <= 0) return promise
340
+ const id = vendorWaitSeq++
341
+ vendorWaits.set(id, { kind, label, startMs: performance.now() })
334
342
  if (!stallTimer) {
335
- stallTimer = setInterval(reportVendorStall, Math.max(1000, VENDOR_STALL_MS));
343
+ stallTimer = setInterval(reportVendorStall, Math.max(1000, VENDOR_STALL_MS))
336
344
  // Never keep the process alive for the watchdog alone.
337
- stallTimer.unref?.();
345
+ stallTimer.unref?.()
338
346
  }
339
347
  return promise.finally(() => {
340
- vendorWaits.delete(id);
348
+ vendorWaits.delete(id)
341
349
  if (vendorWaits.size === 0 && stallTimer) {
342
- clearInterval(stallTimer);
343
- stallTimer = undefined;
350
+ clearInterval(stallTimer)
351
+ stallTimer = undefined
344
352
  }
345
- });
353
+ })
346
354
  }
347
355
 
348
356
  /** Scheduler waits currently outstanding — for tests and external diagnostics. */
349
357
  export function vendorWaitReport() {
350
- const now = performance.now();
358
+ const now = performance.now()
351
359
  return [...vendorWaits.values()].map(w => ({
352
360
  kind: w.kind,
353
361
  label: w.label,
354
362
  waitedMs: now - w.startMs,
355
- }));
363
+ }))
356
364
  }
357
365
 
358
366
  /**
@@ -371,40 +379,40 @@ export function vendorPipelineTrace() {
371
379
  unsettled: [...pendingBundles]
372
380
  .filter(([, pending]) => pending.result === undefined)
373
381
  .map(([key]) => key),
374
- };
382
+ }
375
383
  }
376
384
 
377
385
  /** The single funnel every vendor demand goes through. */
378
386
  export function vendorBundle(plan: VendorBuildPlan, nested = false): Promise<string> {
379
- const existing = pendingBundles.get(plan.key);
387
+ const existing = pendingBundles.get(plan.key)
380
388
  if (existing) {
381
389
  // Dedup is what makes this a deadlock risk: the demand we join may still be
382
390
  // queued, and a nested caller cannot afford to wait for the queue to move.
383
- if (nested) promoteVendorWork(existing);
384
- return watchVendorWait(nested ? 'dedup-nested' : 'dedup', plan.key, existing.promise);
391
+ if (nested) promoteVendorWork(existing)
392
+ return watchVendorWait(nested ? 'dedup-nested' : 'dedup', plan.key, existing.promise)
385
393
  }
386
- const pending: PendingBundle = { promise: undefined as unknown as Promise<string> };
394
+ const pending: PendingBundle = { promise: undefined as unknown as Promise<string> }
387
395
  pending.promise = runVendorDemand(plan, nested, pending).then(
388
396
  file => {
389
- pending.result = file;
390
- return file;
397
+ pending.result = file
398
+ return file
391
399
  },
392
400
  error => {
393
401
  // A write racing a concurrent `.pnext` wipe rejects; evict so the next
394
402
  // demand retries instead of re-awaiting a permanently failed promise.
395
- pendingBundles.delete(plan.key);
396
- throw error;
403
+ pendingBundles.delete(plan.key)
404
+ throw error
397
405
  },
398
- );
399
- pendingBundles.set(plan.key, pending);
400
- return pending.promise;
406
+ )
407
+ pendingBundles.set(plan.key, pending)
408
+ return pending.promise
401
409
  }
402
410
 
403
411
  async function runVendorDemand(plan: VendorBuildPlan, nested: boolean, ticket: VendorPromotable) {
404
- const ready = await plan.prepare();
405
- if (ready !== undefined) return ready;
406
- if (plan.group && (await joinVendorGroup(plan.group, nested, ticket))) return plan.file;
407
- return withVendorSlot(nested, plan.key, () => plan.buildOne(nested), ticket);
412
+ const ready = await plan.prepare()
413
+ if (ready !== undefined) return ready
414
+ if (plan.group && (await joinVendorGroup(plan.group, nested, ticket))) return plan.file
415
+ return withVendorSlot(nested, plan.key, () => plan.buildOne(nested), ticket)
408
416
  }
409
417
 
410
418
  /**
@@ -414,20 +422,21 @@ async function runVendorDemand(plan: VendorBuildPlan, nested: boolean, ticket: V
414
422
  *
415
423
  * Every demand pays it before it can even learn it has no siblings, and it is a timer on the one JS thread
416
424
  * the concurrent bundlers already saturate with plugin IPC, so the real wait is bounded below by this rather
417
- * than by it. `PNEXT_VENDOR_GROUP_WINDOW_MS` is the bisect seam.
425
+ * than by it. `PNEXT_VENDOR_GROUP_WINDOW_MS` is the bisect seam, read per round so
426
+ * tests can widen it after module load.
418
427
  */
419
- const VENDOR_GROUP_WINDOW_MS = (() => {
428
+ function vendorGroupWindowMs() {
420
429
  // eslint-disable-next-line turbo/no-undeclared-env-vars
421
- const override = Number(process.env.PNEXT_VENDOR_GROUP_WINDOW_MS);
422
- return Number.isFinite(override) && override >= 0 ? override : 5;
423
- })();
430
+ const override = Number(process.env.PNEXT_VENDOR_GROUP_WINDOW_MS)
431
+ return Number.isFinite(override) && override >= 0 ? override : 5
432
+ }
424
433
 
425
434
  /**
426
435
  * A group re-parses the package graph once per round, so an unbounded trickle
427
436
  * of stragglers must not turn into an unbounded chain of rebuilds. Past the
428
437
  * bound the package goes back to a build per subpath.
429
438
  */
430
- const MAX_VENDOR_GROUP_ROUNDS = 4;
439
+ const MAX_VENDOR_GROUP_ROUNDS = 4
431
440
 
432
441
  /**
433
442
  * Escape hatch: `PNEXT_VENDOR_GROUP=0` puts every subpath back on its own build. Grouping is the one part of
@@ -436,7 +445,7 @@ const MAX_VENDOR_GROUP_ROUNDS = 4;
436
445
  */
437
446
  function vendorGroupingEnabled() {
438
447
  // eslint-disable-next-line turbo/no-undeclared-env-vars
439
- return process.env.PNEXT_VENDOR_GROUP !== '0';
448
+ return process.env.PNEXT_VENDOR_GROUP !== '0'
440
449
  }
441
450
 
442
451
  /**
@@ -449,13 +458,9 @@ function vendorGroupingEnabled() {
449
458
  * artifacts from earlier rounds stay valid and are referenced as externals, so a growth round costs
450
459
  * what that sibling's own build would have cost.
451
460
  */
452
- async function joinVendorGroup(
453
- group: VendorGroupPlan,
454
- nested: boolean,
455
- ticket: VendorPromotable,
456
- ) {
457
- if (!vendorGroupingEnabled()) return false;
458
- let state = vendorGroups.get(group.key);
461
+ async function joinVendorGroup(group: VendorGroupPlan, nested: boolean, ticket: VendorPromotable) {
462
+ if (!vendorGroupingEnabled()) return false
463
+ let state = vendorGroups.get(group.key)
459
464
  if (!state) {
460
465
  state = {
461
466
  pending: new Map(),
@@ -463,37 +468,37 @@ async function joinVendorGroup(
463
468
  solo: new Map(),
464
469
  rounds: 0,
465
470
  closed: false,
466
- };
467
- vendorGroups.set(group.key, state);
471
+ }
472
+ vendorGroups.set(group.key, state)
468
473
  }
469
- const specifier = group.member.specifier;
470
- if (state.closed || state.solo.has(specifier)) return false;
474
+ const specifier = group.member.specifier
475
+ if (state.closed || state.solo.has(specifier)) return false
471
476
  // A demand raised from INSIDE a running round cannot wait for that round:
472
477
  // the round is waiting on this demand. Build it alone, and leave it on file
473
478
  // so the group externalizes the artifact instead of re-parsing it.
474
479
  if (nested && state.round && !state.pending.has(specifier)) {
475
- state.solo.set(specifier, group.member);
476
- return false;
480
+ state.solo.set(specifier, group.member)
481
+ return false
477
482
  }
478
- state.pending.set(specifier, group.member);
483
+ state.pending.set(specifier, group.member)
479
484
  // While this demand waits on a shared round, promoting IT has to promote the
480
485
  // round — that is the only work its result depends on.
481
- const groupState = state;
482
- ticket.promote = () => promoteVendorWork(groupState.ticket);
486
+ const groupState = state
487
+ ticket.promote = () => promoteVendorWork(groupState.ticket)
483
488
  try {
484
489
  while (!state.produced.has(specifier)) {
485
- if (state.closed || state.solo.has(specifier)) return false;
486
- if (nested) promoteVendorWork(state.ticket);
490
+ if (state.closed || state.solo.has(specifier)) return false
491
+ if (nested) promoteVendorWork(state.ticket)
487
492
  await watchVendorWait(
488
493
  nested ? 'group-round-nested' : 'group-round',
489
494
  `${group.key} :: ${specifier}`,
490
495
  runVendorGroupRound(group, state, nested),
491
- );
496
+ )
492
497
  }
493
498
  } finally {
494
- ticket.promote = undefined;
499
+ ticket.promote = undefined
495
500
  }
496
- return true;
501
+ return true
497
502
  }
498
503
 
499
504
  function runVendorGroupRound(
@@ -503,73 +508,69 @@ function runVendorGroupRound(
503
508
  ): Promise<void> {
504
509
  if (!state.round) {
505
510
  state.round = vendorGroupRound(group, state, nested).finally(() => {
506
- state.round = undefined;
507
- state.ticket = undefined;
508
- });
511
+ state.round = undefined
512
+ state.ticket = undefined
513
+ })
509
514
  }
510
- return state.round;
515
+ return state.round
511
516
  }
512
517
 
513
- async function vendorGroupRound(
514
- group: VendorGroupPlan,
515
- state: VendorGroupState,
516
- nested: boolean,
517
- ) {
518
- await new Promise(resolve => setTimeout(resolve, VENDOR_GROUP_WINDOW_MS));
519
- const members = [...state.pending.values()];
520
- if (members.length === 0) return;
518
+ async function vendorGroupRound(group: VendorGroupPlan, state: VendorGroupState, nested: boolean) {
519
+ await new Promise(resolve => setTimeout(resolve, vendorGroupWindowMs()))
520
+ const members = [...state.pending.values()]
521
+ if (members.length === 0) return
521
522
  // A lone subpath with no sibling to share a graph with is the single-bundle
522
523
  // path plus chunk plumbing. Hand it back — and keep it on file, so a sibling
523
524
  // demanded later externalizes its artifact instead of re-parsing it.
524
525
  if (members.length === 1 && state.produced.size === 0) {
525
- const only = members[0]!;
526
- state.pending.delete(only.specifier);
527
- state.solo.set(only.specifier, only);
528
- return;
526
+ const only = members[0]!
527
+ state.pending.delete(only.specifier)
528
+ state.solo.set(only.specifier, only)
529
+ return
529
530
  }
530
- state.rounds += 1;
531
- const external = [...state.produced.values(), ...state.solo.values()];
532
- const ticket: VendorPromotable = {};
533
- state.ticket = ticket;
531
+ state.rounds += 1
532
+ const external = [...state.produced.values(), ...state.solo.values()]
533
+ const ticket: VendorPromotable = {}
534
+ state.ticket = ticket
534
535
  try {
535
- await withVendorSlot(nested, group.key, () => group.build(members, external, nested), ticket);
536
+ await withVendorSlot(nested, group.key, () => group.build(members, external, nested), ticket)
536
537
  for (const member of members) {
537
- state.pending.delete(member.specifier);
538
- state.produced.set(member.specifier, member);
538
+ state.pending.delete(member.specifier)
539
+ state.produced.set(member.specifier, member)
539
540
  }
540
541
  } catch {
541
542
  // Grouping is an optimization: one bad sibling must not fail the demand,
542
543
  // so the group gives up and every member falls back to its own build.
543
- state.closed = true;
544
+ state.closed = true
544
545
  }
545
- if (state.rounds >= MAX_VENDOR_GROUP_ROUNDS) state.closed = true;
546
+ if (state.rounds >= MAX_VENDOR_GROUP_ROUNDS) state.closed = true
546
547
  }
547
548
 
548
549
  // --------------------------------------------------------------------------
549
550
  // pre-planned builds: enqueued without an awaiter, drained at route-module eval
550
551
  // --------------------------------------------------------------------------
551
552
 
552
- const preplanBuilds = new Set<Promise<unknown>>();
553
- let preplanBuildError: unknown;
553
+ const preplanBuilds = new Set<Promise<unknown>>()
554
+ let preplanBuildError: unknown
554
555
 
555
556
  export function trackPreplanBuild(promise: Promise<unknown>) {
556
557
  const tracked = promise.catch(error => {
557
- preplanBuildError ??= error;
558
- });
559
- preplanBuilds.add(tracked);
560
- void tracked.finally(() => preplanBuilds.delete(tracked));
558
+ preplanBuildError ??= error
559
+ })
560
+ preplanBuilds.add(tracked)
561
+ void tracked.finally(() => preplanBuilds.delete(tracked))
561
562
  }
562
563
 
563
564
  /** The one join point: nothing evaluates until every enqueued build settles. */
564
565
  export async function drainPreplanBuilds() {
565
566
  while (preplanBuilds.size > 0) {
566
- await watchVendorWait('preplan-drain', 'preplan', Promise.allSettled([...preplanBuilds]));
567
+ await watchVendorWait('preplan-drain', 'preplan', Promise.allSettled([...preplanBuilds]))
567
568
  }
568
569
  if (preplanBuildError !== undefined) {
569
- const error = preplanBuildError;
570
- preplanBuildError = undefined;
570
+ const error = preplanBuildError
571
+ preplanBuildError = undefined
571
572
  // eslint-disable-next-line @typescript-eslint/only-throw-error
572
- throw error;
573
+ throw error
573
574
  }
574
575
  }
575
576
 
@@ -579,29 +580,29 @@ export async function drainPreplanBuilds() {
579
580
  * evicts, so concurrent demands join the one rebuild instead of racing.
580
581
  */
581
582
  export function dropVendorBundle(key: string, stale: string) {
582
- const pending = pendingBundles.get(key);
583
- if (pending?.result === stale) pendingBundles.delete(key);
583
+ const pending = pendingBundles.get(key)
584
+ if (pending?.result === stale) pendingBundles.delete(key)
584
585
  }
585
586
 
586
587
  /** Forget a group whose artifacts were wiped, so the retry rebuilds them. */
587
588
  export function dropVendorGroup(key: string) {
588
- vendorGroups.delete(key);
589
+ vendorGroups.delete(key)
589
590
  }
590
591
 
591
592
  export function clearVendorPipeline() {
592
- pendingBundles.clear();
593
- vendorGroups.clear();
594
- preplanBuilds.clear();
595
- preplanBuildError = undefined;
596
- esmDistFiles.clear();
597
- vendorContentIds.clear();
598
- vendorSpans.length = 0;
593
+ pendingBundles.clear()
594
+ vendorGroups.clear()
595
+ preplanBuilds.clear()
596
+ preplanBuildError = undefined
597
+ esmDistFiles.clear()
598
+ vendorContentIds.clear()
599
+ vendorSpans.length = 0
599
600
  // In-flight waits belong to the pipeline that was just dropped; leaving them
600
601
  // registered would have the watchdog report a stall that no longer exists.
601
- vendorWaits.clear();
602
+ vendorWaits.clear()
602
603
  if (stallTimer) {
603
- clearInterval(stallTimer);
604
- stallTimer = undefined;
604
+ clearInterval(stallTimer)
605
+ stallTimer = undefined
605
606
  }
606
607
  }
607
608
 
@@ -610,14 +611,14 @@ export function clearVendorPipeline() {
610
611
  // --------------------------------------------------------------------------
611
612
 
612
613
  /** Artifact (or facade) file -> content id of the bytes it resolves to. */
613
- const vendorContentIds = new Map<string, string>();
614
+ const vendorContentIds = new Map<string, string>()
614
615
 
615
616
  export function vendorContentId(file: string) {
616
- return vendorContentIds.get(file);
617
+ return vendorContentIds.get(file)
617
618
  }
618
619
 
619
620
  export function noteVendorContentId(file: string, id: string) {
620
- vendorContentIds.set(file, id);
621
+ vendorContentIds.set(file, id)
621
622
  }
622
623
 
623
624
  /**
@@ -626,10 +627,10 @@ export function noteVendorContentId(file: string, id: string) {
626
627
  */
627
628
  export function canonicalVendorCode(code: string) {
628
629
  return rewriteEmittedRefs(code, ref => {
629
- if (!ref.specifier.startsWith('file:')) return undefined;
630
- const id = vendorContentIds.get(fileURLToPath(ref.specifier));
631
- return id ? `pnext-content:${id}` : undefined;
632
- });
630
+ if (!ref.specifier.startsWith('file:')) return undefined
631
+ const id = vendorContentIds.get(fileURLToPath(ref.specifier))
632
+ return id ? `pnext-content:${id}` : undefined
633
+ })
633
634
  }
634
635
 
635
636
  // --------------------------------------------------------------------------
@@ -642,61 +643,58 @@ export function canonicalVendorCode(code: string) {
642
643
  * and cost a JS bridge hop per file.
643
644
  */
644
645
  export interface EmittedRef {
645
- specifier: string;
646
+ specifier: string
646
647
  /** esbuild emits an external `require()` as `__require("x")`. */
647
- require: boolean;
648
+ require: boolean
648
649
  /** Offsets of the specifier text itself, quotes excluded. */
649
- start: number;
650
- end: number;
650
+ start: number
651
+ end: number
651
652
  }
652
653
 
653
654
  // Import/export statements, dynamic imports, and (esbuild's) require calls.
654
655
  const EMITTED_IMPORT_REF =
655
- /(?:^|[\s;}])(?:import|export)\s*(?:[\w$*{}\s,]*?\bfrom\s*)?(["'])([^"'\n]*)\1/g;
656
- const EMITTED_CALL_REF = /(?:^|[^\w$.])(?:(__)?require|import)\s*\(\s*(["'])([^"'\n]*)\2\s*\)/g;
656
+ /(?:^|[\s;}])(?:import|export)\s*(?:[\w$*{}\s,]*?\bfrom\s*)?(["'])([^"'\n]*)\1/g
657
+ const EMITTED_CALL_REF = /(?:^|[^\w$.])(?:(__)?require|import)\s*\(\s*(["'])([^"'\n]*)\2\s*\)/g
657
658
 
658
659
  /** Every bare-specifier reference the emitted code still carries. */
659
660
  export function emittedRefs(code: string): EmittedRef[] {
660
- const refs: EmittedRef[] = [];
661
- const seen = new Set<number>();
661
+ const refs: EmittedRef[] = []
662
+ const seen = new Set<number>()
662
663
  for (const match of code.matchAll(EMITTED_IMPORT_REF)) {
663
- const start = match.index + match[0].length - match[2]!.length - 1;
664
- if (seen.has(start)) continue;
665
- seen.add(start);
666
- refs.push({ specifier: match[2]!, require: false, start, end: start + match[2]!.length });
664
+ const start = match.index + match[0].length - match[2]!.length - 1
665
+ if (seen.has(start)) continue
666
+ seen.add(start)
667
+ refs.push({ specifier: match[2]!, require: false, start, end: start + match[2]!.length })
667
668
  }
668
669
  for (const match of code.matchAll(EMITTED_CALL_REF)) {
669
- const value = match[3]!;
670
- const start = match.index + match[0].lastIndexOf(value);
671
- if (seen.has(start)) continue;
672
- seen.add(start);
670
+ const value = match[3]!
671
+ const start = match.index + match[0].lastIndexOf(value)
672
+ if (seen.has(start)) continue
673
+ seen.add(start)
673
674
  refs.push({
674
675
  specifier: value,
675
676
  require: match[0].includes('require'),
676
677
  start,
677
678
  end: start + value.length,
678
- });
679
+ })
679
680
  }
680
- return refs.sort((a, b) => a.start - b.start);
681
+ return refs.sort((a, b) => a.start - b.start)
681
682
  }
682
683
 
683
684
  /**
684
685
  * Repoint emitted references. `replace` returns the new specifier, or undefined
685
686
  * to leave one alone — a bare package name the runtime resolves itself.
686
687
  */
687
- export function rewriteEmittedRefs(
688
- code: string,
689
- replace: (ref: EmittedRef) => string | undefined,
690
- ) {
691
- let out = '';
692
- let cursor = 0;
688
+ export function rewriteEmittedRefs(code: string, replace: (ref: EmittedRef) => string | undefined) {
689
+ let out = ''
690
+ let cursor = 0
693
691
  for (const ref of emittedRefs(code)) {
694
- const next = replace(ref);
695
- if (next === undefined || next === ref.specifier) continue;
696
- out += code.slice(cursor, ref.start) + next.replace(/\\/g, '\\\\');
697
- cursor = ref.end;
692
+ const next = replace(ref)
693
+ if (next === undefined || next === ref.specifier) continue
694
+ out += code.slice(cursor, ref.start) + next.replace(/\\/g, '\\\\')
695
+ cursor = ref.end
698
696
  }
699
- return cursor === 0 ? code : out + code.slice(cursor);
697
+ return cursor === 0 ? code : out + code.slice(cursor)
700
698
  }
701
699
 
702
700
  /**
@@ -705,21 +703,25 @@ export function rewriteEmittedRefs(
705
703
  * actually bind what its importer asks for.
706
704
  */
707
705
  export function emittedExportNames(code: string): Set<string> | undefined {
708
- const names = new Set<string>();
709
- if (/(^|\n)\s*export\s*\*\s*from/.test(code)) return undefined;
706
+ const names = new Set<string>()
707
+ if (/(^|\n)\s*export\s*\*\s*from/.test(code)) return undefined
710
708
  for (const match of code.matchAll(/(^|[\s;}])export\s*\{([^}]*)\}/g)) {
711
709
  for (const entry of match[2]!.split(',')) {
712
- const name = entry.trim().split(/\s+as\s+/).pop()?.trim();
713
- if (name) names.add(name);
710
+ const name = entry
711
+ .trim()
712
+ .split(/\s+as\s+/)
713
+ .pop()
714
+ ?.trim()
715
+ if (name) names.add(name)
714
716
  }
715
717
  }
716
718
  for (const match of code.matchAll(
717
719
  /(^|[\s;}])export\s+(?:declare\s+)?(?:async\s+)?(?:const|let|var|function\*?|class)\s+([A-Za-z_$][\w$]*)/g,
718
720
  )) {
719
- names.add(match[2]!);
721
+ names.add(match[2]!)
720
722
  }
721
- if (/(^|[\s;}])export\s+default[\s({[]/.test(code)) names.add('default');
722
- return names;
723
+ if (/(^|[\s;}])export\s+default[\s({[]/.test(code)) names.add('default')
724
+ return names
723
725
  }
724
726
 
725
727
  /**
@@ -727,34 +729,34 @@ export function emittedExportNames(code: string): Set<string> | undefined {
727
729
  * Applied to the bundle itself (not a separate module) for the inlined case.
728
730
  */
729
731
  export function addCommonJsNamedExports(code: string) {
730
- if (hasNonDefaultExport(code)) return code;
732
+ if (hasNonDefaultExport(code)) return code
731
733
  const names = [...new Set([...commonJsExportNames(code), ...esbuildEntryExportNames(code)])]
732
734
  .filter(name => name !== 'default' && name !== '__esModule')
733
- .sort();
734
- if (names.length === 0) return code;
735
- const defaultExport = /(^|\n)export default ([^;\n]+);/.exec(code);
736
- if (!defaultExport?.[2]) return code;
737
- const defaultName = uniqueIdentifier(code, '__pnext_cjs_default');
738
- const usedNames = [defaultName];
735
+ .sort()
736
+ if (names.length === 0) return code
737
+ const defaultExport = /(^|\n)export default ([^;\n]+);/.exec(code)
738
+ if (!defaultExport?.[2]) return code
739
+ const defaultName = uniqueIdentifier(code, '__pnext_cjs_default')
740
+ const usedNames = [defaultName]
739
741
  const bindings = names.map(name => {
740
- const localName = uniqueIdentifier(code, `__pnext_cjs_export_${name}`, ...usedNames);
741
- usedNames.push(localName);
742
- return { name, localName };
743
- });
742
+ const localName = uniqueIdentifier(code, `__pnext_cjs_export_${name}`, ...usedNames)
743
+ usedNames.push(localName)
744
+ return { name, localName }
745
+ })
744
746
  const namedBindings = bindings
745
747
  .map(({ name, localName }) => `const ${localName} = ${defaultName}[${JSON.stringify(name)}];`)
746
- .join('\n');
747
- const namedExports = bindings.map(({ name, localName }) => `${localName} as ${name}`).join(', ');
748
+ .join('\n')
749
+ const namedExports = bindings.map(({ name, localName }) => `${localName} as ${name}`).join(', ')
748
750
  return code.replace(
749
751
  defaultExport[0],
750
752
  `${defaultExport[1]}const ${defaultName} = ${defaultExport[2]};\nexport default ${defaultName};\n${namedBindings}\nexport { ${namedExports} };\n`,
751
- );
753
+ )
752
754
  }
753
755
 
754
756
  function hasNonDefaultExport(code: string) {
755
757
  return /(^|\n)export\s+(?:\*|\{[^}]*\b(?!default\b)[A-Za-z_$][\w$]*\b|(?:const|let|var|function|class)\s+)/.test(
756
758
  code,
757
- );
759
+ )
758
760
  }
759
761
 
760
762
  export function commonJsExportNames(code: string) {
@@ -767,7 +769,7 @@ export function commonJsExportNames(code: string) {
767
769
  ]),
768
770
  ]
769
771
  .filter((name): name is string => Boolean(name && name !== '__esModule' && isIdentifier(name)))
770
- .sort();
772
+ .sort()
771
773
  }
772
774
 
773
775
  // Recover named exports from esbuild's `__export(entry, {...}); module.exports =
@@ -775,16 +777,16 @@ export function commonJsExportNames(code: string) {
775
777
  function esbuildEntryExportNames(code: string) {
776
778
  const toCommonJs = [
777
779
  ...code.matchAll(/module\.exports\s*=\s*__toCommonJS\w*\(\s*([A-Za-z_$][\w$]*)\s*\)/g),
778
- ];
779
- const entryVar = toCommonJs.at(-1)?.[1];
780
- if (!entryVar) return [];
780
+ ]
781
+ const entryVar = toCommonJs.at(-1)?.[1]
782
+ if (!entryVar) return []
781
783
  const exportCall = new RegExp(
782
784
  `__export\\w*\\(\\s*${escapeRegex(entryVar)}\\s*,\\s*\\{([\\s\\S]*?)\\}\\s*\\)`,
783
- ).exec(code);
784
- if (!exportCall?.[1]) return [];
785
+ ).exec(code)
786
+ if (!exportCall?.[1]) return []
785
787
  return [...exportCall[1].matchAll(/(?:^|,)\s*([A-Za-z_$][\w$]*)\s*:/g)]
786
788
  .map(match => match[1])
787
- .filter((name): name is string => Boolean(name && isIdentifier(name)));
789
+ .filter((name): name is string => Boolean(name && isIdentifier(name)))
788
790
  }
789
791
 
790
792
  /**
@@ -802,16 +804,16 @@ export async function verifyVendorArtifact(
802
804
  chunks: ReadonlyMap<string, string> = new Map(),
803
805
  ) {
804
806
  for (const [specifier, target] of repointed) {
805
- if (!existsSync(target)) return `repointed ${specifier} -> missing ${target}`;
807
+ if (!existsSync(target)) return `repointed ${specifier} -> missing ${target}`
806
808
  }
807
809
  // Nothing was rewritten and nothing was split out: this is esbuild's own
808
810
  // output, unedited, and there is no claim left for a re-bundle to check.
809
- if (repointed.size === 0 && chunks.size === 0) return undefined;
811
+ if (repointed.size === 0 && chunks.size === 0) return undefined
810
812
  // eslint-disable-next-line turbo/no-undeclared-env-vars
811
- const mode = process.env.PNEXT_VENDOR_VERIFY;
812
- if (mode === 'off') return undefined;
813
- const deep = mode === 'deep';
814
- const outDir = path.dirname(file);
813
+ const mode = process.env.PNEXT_VENDOR_VERIFY
814
+ if (mode === 'off') return undefined
815
+ const deep = mode === 'deep'
816
+ const outDir = path.dirname(file)
815
817
  try {
816
818
  await build({
817
819
  stdin: { contents: code, resolveDir: outDir, sourcefile: file, loader: 'js' },
@@ -830,32 +832,34 @@ export async function verifyVendorArtifact(
830
832
  // before anything is published, so nothing half-written is ever
831
833
  // readable by a concurrent demand.
832
834
  verify.onResolve({ filter: /^\.\.?\// }, args => {
833
- const resolved = path.resolve(args.resolveDir, args.path);
835
+ const resolved = path.resolve(args.resolveDir, args.path)
834
836
  return chunks.has(resolved)
835
837
  ? { path: resolved, namespace: 'pnext-vendor-chunk' }
836
- : undefined;
837
- });
838
+ : undefined
839
+ })
838
840
  verify.onLoad({ filter: /.*/, namespace: 'pnext-vendor-chunk' }, args => ({
839
841
  contents: chunks.get(args.path),
840
842
  loader: 'js',
841
843
  resolveDir: path.dirname(args.path),
842
- }));
844
+ }))
843
845
  verify.onResolve({ filter: /^file:/ }, args => {
844
- const resolved = fileURLToPath(args.path);
846
+ const resolved = fileURLToPath(args.path)
845
847
  // Deep: one level in (esbuild then checks the export names), and
846
848
  // only from the artifact itself — its dependencies' own externals
847
849
  // stay external, so the closure cannot run away.
848
- const inside = args.importer === file || args.importer.startsWith(outDir);
849
- return { path: resolved, external: !deep || !inside };
850
- });
850
+ const inside = args.importer === file || args.importer.startsWith(outDir)
851
+ return { path: resolved, external: !deep || !inside }
852
+ })
851
853
  },
852
854
  },
853
855
  ],
854
- });
856
+ })
855
857
  } catch (error) {
856
- return (error instanceof Error ? error.message : String(error)).replace(/\s+/g, ' ').slice(0, 300);
858
+ return (error instanceof Error ? error.message : String(error))
859
+ .replace(/\s+/g, ' ')
860
+ .slice(0, 300)
857
861
  }
858
- return undefined;
862
+ return undefined
859
863
  }
860
864
 
861
865
  // --------------------------------------------------------------------------
@@ -872,46 +876,46 @@ export async function verifyVendorArtifact(
872
876
  * in the closure means a plugin could have fired, so the package goes back to the bundler. A false negative
873
877
  * costs a compile; a false positive ships wrong code.
874
878
  */
875
- const MAX_ESM_DIST_FILES = 4096;
876
- const ESM_DIST_ENTRY_MARKER = 'entry';
879
+ const MAX_ESM_DIST_FILES = 4096
880
+ const ESM_DIST_ENTRY_MARKER = 'entry'
877
881
 
878
- const esmDists = new Map<string, Promise<string | undefined>>();
882
+ const esmDists = new Map<string, Promise<string | undefined>>()
879
883
 
880
884
  export function copyBrowserReadyEsmDist(entry: string, distDir: string) {
881
885
  // One copy per destination: a second copier would rewrite files the first has
882
886
  // already published, and copyFile is not atomic against a concurrent import.
883
- let dist = esmDists.get(distDir);
887
+ let dist = esmDists.get(distDir)
884
888
  if (!dist) {
885
- dist = copyBrowserReadyEsmDistUncached(entry, distDir);
886
- esmDists.set(distDir, dist);
889
+ dist = copyBrowserReadyEsmDistUncached(entry, distDir)
890
+ esmDists.set(distDir, dist)
887
891
  }
888
- return dist;
892
+ return dist
889
893
  }
890
894
 
891
895
  async function copyBrowserReadyEsmDistUncached(entry: string, distDir: string) {
892
896
  const existing = await readFile(path.join(distDir, ESM_DIST_ENTRY_MARKER), 'utf8').catch(
893
897
  () => undefined,
894
- );
895
- if (existing !== undefined) return path.join(distDir, existing);
898
+ )
899
+ if (existing !== undefined) return path.join(distDir, existing)
896
900
 
897
- const closure = await collectBrowserReadyEsmClosure(entry);
898
- if (!closure) return undefined;
901
+ const closure = await collectBrowserReadyEsmClosure(entry)
902
+ if (!closure) return undefined
899
903
  // Copies keep their offsets relative to the shallowest directory the closure
900
904
  // touches, which is what makes every `../` in it still land on its target.
901
- const base = commonAncestorDir([...closure]);
902
- const relative = (file: string) => path.relative(base, file);
905
+ const base = commonAncestorDir([...closure])
906
+ const relative = (file: string) => path.relative(base, file)
903
907
  await Promise.all(
904
908
  [...closure].map(async file => {
905
- const target = path.join(distDir, relative(file));
906
- await mkdir(path.dirname(target), { recursive: true });
907
- await copyFile(file, target);
909
+ const target = path.join(distDir, relative(file))
910
+ await mkdir(path.dirname(target), { recursive: true })
911
+ await copyFile(file, target)
908
912
  }),
909
- );
910
- await writeFileAtomic(path.join(distDir, ESM_DIST_ENTRY_MARKER), relative(entry));
911
- return path.join(distDir, relative(entry));
913
+ )
914
+ await writeFileAtomic(path.join(distDir, ESM_DIST_ENTRY_MARKER), relative(entry))
915
+ return path.join(distDir, relative(entry))
912
916
  }
913
917
 
914
- const ESM_DIST_EXTENSIONS = new Set(['.js', '.mjs']);
918
+ const ESM_DIST_EXTENSIONS = new Set(['.js', '.mjs'])
915
919
 
916
920
  /**
917
921
  * Per-FILE verdict, memoized: the predicate runs once per demanded artifact and
@@ -919,85 +923,85 @@ const ESM_DIST_EXTENSIONS = new Set(['.js', '.mjs']);
919
923
  * thousand files were re-read, re-regexed and re-`existsSync`ed per subpath.
920
924
  * `undefined` means "not browser-ready", which fails the whole closure.
921
925
  */
922
- const esmDistFiles = new Map<string, Promise<string[] | undefined>>();
926
+ const esmDistFiles = new Map<string, Promise<string[] | undefined>>()
923
927
 
924
928
  function browserReadyEsmImports(file: string) {
925
- let entry = esmDistFiles.get(file);
929
+ let entry = esmDistFiles.get(file)
926
930
  if (!entry) {
927
- entry = browserReadyEsmImportsUncached(file);
928
- esmDistFiles.set(file, entry);
931
+ entry = browserReadyEsmImportsUncached(file)
932
+ esmDistFiles.set(file, entry)
929
933
  }
930
- return entry;
934
+ return entry
931
935
  }
932
936
 
933
937
  async function browserReadyEsmImportsUncached(file: string) {
934
- if (!ESM_DIST_EXTENSIONS.has(path.extname(file))) return undefined;
935
- const source = await readFile(file, 'utf8').catch(() => undefined);
936
- if (source === undefined) return undefined;
938
+ if (!ESM_DIST_EXTENSIONS.has(path.extname(file))) return undefined
939
+ const source = await readFile(file, 'utf8').catch(() => undefined)
940
+ if (source === undefined) return undefined
937
941
  // `use cache` would need the post-bundle source transforms this path skips.
938
942
  if (
939
943
  isCommonJsModuleSource(source, file) ||
940
944
  /\brequire\s*\(|\bmodule\.exports\b/.test(source) ||
941
945
  source.includes('use cache')
942
946
  ) {
943
- return undefined;
947
+ return undefined
944
948
  }
945
- const imports: string[] = [];
949
+ const imports: string[] = []
946
950
  for (const found of outputSpecifiers(source)) {
947
- const specifier = found.value;
948
- if (!specifier.startsWith('.')) return undefined;
949
- const resolved = resolveEsmDistImport(path.dirname(file), specifier);
950
- if (!resolved) return undefined;
951
- imports.push(resolved);
951
+ const specifier = found.value
952
+ if (!specifier.startsWith('.')) return undefined
953
+ const resolved = resolveEsmDistImport(path.dirname(file), specifier)
954
+ if (!resolved) return undefined
955
+ imports.push(resolved)
952
956
  }
953
- return imports;
957
+ return imports
954
958
  }
955
959
 
956
960
  async function collectBrowserReadyEsmClosure(entry: string) {
957
- const closure = new Set<string>();
958
- const queue = [entry];
961
+ const closure = new Set<string>()
962
+ const queue = [entry]
959
963
  while (queue.length > 0) {
960
- const file = queue.pop()!;
961
- if (closure.has(file)) continue;
962
- if (closure.size >= MAX_ESM_DIST_FILES) return undefined;
963
- closure.add(file);
964
- const imports = await browserReadyEsmImports(file);
965
- if (!imports) return undefined;
966
- queue.push(...imports);
964
+ const file = queue.pop()!
965
+ if (closure.has(file)) continue
966
+ if (closure.size >= MAX_ESM_DIST_FILES) return undefined
967
+ closure.add(file)
968
+ const imports = await browserReadyEsmImports(file)
969
+ if (!imports) return undefined
970
+ queue.push(...imports)
967
971
  }
968
972
  // A single-file "closure" is a re-export shim or a stub; bundling it is
969
973
  // already free, and the marker directory would cost more than it saves.
970
- return closure.size > 1 ? closure : undefined;
974
+ return closure.size > 1 ? closure : undefined
971
975
  }
972
976
 
973
977
  /** Relative resolution limited to what a browser would do: exact file first. */
974
978
  function resolveEsmDistImport(fromDir: string, specifier: string) {
975
- const base = path.resolve(fromDir, specifier);
979
+ const base = path.resolve(fromDir, specifier)
976
980
  for (const candidate of [base, `${base}.js`, `${base}.mjs`, path.join(base, 'index.js')]) {
977
981
  if (ESM_DIST_EXTENSIONS.has(path.extname(candidate)) && cachedExistsSync(candidate)) {
978
- return candidate;
982
+ return candidate
979
983
  }
980
984
  }
981
- return undefined;
985
+ return undefined
982
986
  }
983
987
 
984
988
  function commonAncestorDir(files: string[]) {
985
- const segmentLists = files.map(file => path.dirname(file).split(path.sep));
986
- const shortest = Math.min(...segmentLists.map(segments => segments.length));
987
- const common: string[] = [];
989
+ const segmentLists = files.map(file => path.dirname(file).split(path.sep))
990
+ const shortest = Math.min(...segmentLists.map(segments => segments.length))
991
+ const common: string[] = []
988
992
  for (let index = 0; index < shortest; index += 1) {
989
- const segment = segmentLists[0]![index]!;
990
- if (segmentLists.some(segments => segments[index] !== segment)) break;
991
- common.push(segment);
993
+ const segment = segmentLists[0]![index]!
994
+ if (segmentLists.some(segments => segments[index] !== segment)) break
995
+ common.push(segment)
992
996
  }
993
- return common.join(path.sep) || path.sep;
997
+ return common.join(path.sep) || path.sep
994
998
  }
995
999
 
996
1000
  // --------------------------------------------------------------------------
997
1001
  // facades
998
1002
  // --------------------------------------------------------------------------
999
1003
 
1000
- const REEXPORT_CALL = /\bmodule\.exports\s*=\s*require\(\s*(['"])([^'"]+)\1\s*\)/;
1004
+ const REEXPORT_CALL = /\bmodule\.exports\s*=\s*require\(\s*(['"])([^'"]+)\1\s*\)/
1001
1005
 
1002
1006
  /**
1003
1007
  * Cheap pre-filter: can this CommonJS entry plausibly carry named exports? A
@@ -1007,19 +1011,19 @@ const REEXPORT_CALL = /\bmodule\.exports\s*=\s*require\(\s*(['"])([^'"]+)\1\s*\)
1007
1011
  * level, which is where most packages' real export list lives.
1008
1012
  */
1009
1013
  export async function cjsEntryMayHaveNamedExports(entry: string) {
1010
- const source = await readFile(entry, 'utf8').catch(() => undefined);
1011
- if (source === undefined) return false;
1012
- if (commonJsExportNames(source).length > 0) return true;
1013
- const reexport = REEXPORT_CALL.exec(source);
1014
- const target = reexport?.[2];
1015
- if (!target?.startsWith('.')) return false;
1014
+ const source = await readFile(entry, 'utf8').catch(() => undefined)
1015
+ if (source === undefined) return false
1016
+ if (commonJsExportNames(source).length > 0) return true
1017
+ const reexport = REEXPORT_CALL.exec(source)
1018
+ const target = reexport?.[2]
1019
+ if (!target?.startsWith('.')) return false
1016
1020
  for (const candidate of [target, `${target}.js`, path.join(target, 'index.js')]) {
1017
- const file = path.resolve(path.dirname(entry), candidate);
1018
- if (!existsSync(file) || !file.endsWith('.js')) continue;
1019
- const inner = await readFile(file, 'utf8').catch(() => undefined);
1020
- if (inner && commonJsExportNames(inner).length > 0) return true;
1021
+ const file = path.resolve(path.dirname(entry), candidate)
1022
+ if (!existsSync(file) || !file.endsWith('.js')) continue
1023
+ const inner = await readFile(file, 'utf8').catch(() => undefined)
1024
+ if (inner && commonJsExportNames(inner).length > 0) return true
1021
1025
  }
1022
- return false;
1026
+ return false
1023
1027
  }
1024
1028
 
1025
1029
  /**
@@ -1029,28 +1033,32 @@ export async function cjsEntryMayHaveNamedExports(entry: string) {
1029
1033
  * full cache wipe recovers from. A new key sidesteps both: the old facade stays on disk for whoever still
1030
1034
  * imports it, and every new compile names the new one.
1031
1035
  */
1032
- const FACADE_GENERATOR = 'v2';
1036
+ const FACADE_GENERATOR = 'v2'
1033
1037
 
1034
1038
  /** Marker written beside a bundle whose export set turned out to be default-only. */
1035
- const NO_FACADE_SUFFIX = `.no-facade-${FACADE_GENERATOR}`;
1039
+ const NO_FACADE_SUFFIX = `.no-facade-${FACADE_GENERATOR}`
1036
1040
 
1037
1041
  /** The facade this generator writes for `bundleFile`. */
1038
1042
  export function cjsFacadePath(bundleFile: string) {
1039
- return `${bundleFile.replace(/\.mjs$/, '')}.facade-${FACADE_GENERATOR}.mjs`;
1043
+ return `${bundleFile.replace(/\.mjs$/, '')}.facade-${FACADE_GENERATOR}.mjs`
1040
1044
  }
1041
1045
 
1042
1046
  /** Where this generator records "no named exports" for `bundleFile`. */
1043
1047
  export function cjsNoFacadeMarker(bundleFile: string) {
1044
- return `${bundleFile}${NO_FACADE_SUFFIX}`;
1048
+ return `${bundleFile}${NO_FACADE_SUFFIX}`
1045
1049
  }
1046
1050
 
1047
- const facades = new Map<string, Promise<string | undefined>>();
1051
+ const facades = new Map<string, Promise<string | undefined>>()
1048
1052
 
1049
1053
  // eslint-disable-next-line turbo/no-undeclared-env-vars
1050
- const heavyProfFile = process.env.PNEXT_HEAVY_PROF;
1054
+ const heavyProfFile = process.env.PNEXT_HEAVY_PROF
1051
1055
  export function heavyProfRow(row: Record<string, unknown>) {
1052
- if (!heavyProfFile) return;
1053
- try { appendFileSync(heavyProfFile, `${JSON.stringify(row)}\n`); } catch { /* prof only */ }
1056
+ if (!heavyProfFile) return
1057
+ try {
1058
+ appendFileSync(heavyProfFile, `${JSON.stringify(row)}\n`)
1059
+ } catch {
1060
+ /* prof only */
1061
+ }
1054
1062
  }
1055
1063
 
1056
1064
  /**
@@ -1059,48 +1067,55 @@ export function heavyProfRow(row: Record<string, unknown>) {
1059
1067
  * inlines instead.
1060
1068
  */
1061
1069
  export function cjsNamedExportFacade(bundleFile: string, importHref: string) {
1062
- let facade = facades.get(bundleFile);
1070
+ let facade = facades.get(bundleFile)
1063
1071
  if (!facade) {
1064
- facade = writeCjsNamedExportFacade(bundleFile, importHref).catch(() => undefined);
1065
- facades.set(bundleFile, facade);
1072
+ facade = writeCjsNamedExportFacade(bundleFile, importHref).catch(() => undefined)
1073
+ facades.set(bundleFile, facade)
1066
1074
  }
1067
- return facade;
1075
+ return facade
1068
1076
  }
1069
1077
 
1070
1078
  async function writeCjsNamedExportFacade(bundleFile: string, importHref: string) {
1071
- const file = cjsFacadePath(bundleFile);
1079
+ const file = cjsFacadePath(bundleFile)
1072
1080
  // The facade's identity follows its bundle's, so an importer of one canonical-
1073
1081
  // izes the same way an importer of the other does.
1074
- const bundleId = vendorContentIds.get(bundleFile);
1075
- if (bundleId) vendorContentIds.set(file, `${bundleId}:facade`);
1082
+ const bundleId = vendorContentIds.get(bundleFile)
1083
+ if (bundleId) vendorContentIds.set(file, `${bundleId}:facade`)
1076
1084
  // Both outcomes persist: the facade is content-keyed with the bundle it wraps,
1077
1085
  // so a restart never re-executes the package to re-derive its names.
1078
- if (existsSync(file)) return file;
1079
- if (existsSync(cjsNoFacadeMarker(bundleFile))) return undefined;
1086
+ if (existsSync(file)) return file
1087
+ if (existsSync(cjsNoFacadeMarker(bundleFile))) return undefined
1080
1088
 
1081
- const t0 = performance.now();
1082
- const code = await readFile(bundleFile, 'utf8').catch(() => undefined);
1083
- if (code === undefined) return undefined;
1084
- const tRead = performance.now();
1089
+ const t0 = performance.now()
1090
+ const code = await readFile(bundleFile, 'utf8').catch(() => undefined)
1091
+ if (code === undefined) return undefined
1092
+ const tRead = performance.now()
1085
1093
  // The exec pass is REQUIRED, not a supplement: a statically-scanned set is routinely short, and an
1086
1094
  // incomplete facade is worse than inlining - it breaks an importer's named binding at build time.
1087
1095
  // A bundle that cannot be executed here simply gets no facade.
1088
- const executed = await executedExportNames(importHref);
1089
- const tExec = performance.now();
1096
+ const executed = await executedExportNames(importHref)
1097
+ const tExec = performance.now()
1090
1098
  const names =
1091
1099
  executed &&
1092
1100
  [...new Set([...commonJsExportNames(code), ...executed])]
1093
1101
  .filter(name => name !== 'default' && name !== '__esModule' && isIdentifier(name))
1094
- .sort();
1102
+ .sort()
1095
1103
  if (!names || names.length === 0) {
1096
- heavyProfRow({ k: 'facade', f: path.basename(bundleFile), readMs: tRead - t0, execMs: tExec - tRead, verifyMs: 0, out: 'no-names' });
1097
- await writeFileAtomic(cjsNoFacadeMarker(bundleFile), '');
1098
- return undefined;
1104
+ heavyProfRow({
1105
+ k: 'facade',
1106
+ f: path.basename(bundleFile),
1107
+ readMs: tRead - t0,
1108
+ execMs: tExec - tRead,
1109
+ verifyMs: 0,
1110
+ out: 'no-names',
1111
+ })
1112
+ await writeFileAtomic(cjsNoFacadeMarker(bundleFile), '')
1113
+ return undefined
1099
1114
  }
1100
1115
 
1101
- const binding = '__pnext_cjs';
1102
- const namespace = '__pnext_cjs_ns';
1103
- const holder = '__pnext_cjs_names';
1116
+ const binding = '__pnext_cjs'
1117
+ const namespace = '__pnext_cjs_ns'
1118
+ const holder = '__pnext_cjs_names'
1104
1119
  // A CommonJS entry marked `__esModule` but carrying no `default` (e.g.
1105
1120
  // bind-event-listener) leaves the default binding undefined, so reading names
1106
1121
  // off it threw at facade eval. Its names live on the bundle's own exports
@@ -1110,15 +1125,22 @@ async function writeCjsNamedExportFacade(bundleFile: string, importHref: string)
1110
1125
  `export default ${binding};\n` +
1111
1126
  `const ${holder} = ${binding} ?? ${namespace};\n` +
1112
1127
  names.map(name => `export const ${name} = ${holder}[${JSON.stringify(name)}];`).join('\n') +
1113
- '\n';
1114
- const ok = await facadeRebundles(file, source);
1115
- heavyProfRow({ k: 'facade', f: path.basename(bundleFile), readMs: tRead - t0, execMs: tExec - tRead, verifyMs: performance.now() - tExec, out: ok ? 'facade' : 'verify-fail' });
1128
+ '\n'
1129
+ const ok = await facadeRebundles(file, source)
1130
+ heavyProfRow({
1131
+ k: 'facade',
1132
+ f: path.basename(bundleFile),
1133
+ readMs: tRead - t0,
1134
+ execMs: tExec - tRead,
1135
+ verifyMs: performance.now() - tExec,
1136
+ out: ok ? 'facade' : 'verify-fail',
1137
+ })
1116
1138
  if (!ok) {
1117
- await writeFileAtomic(cjsNoFacadeMarker(bundleFile), '');
1118
- return undefined;
1139
+ await writeFileAtomic(cjsNoFacadeMarker(bundleFile), '')
1140
+ return undefined
1119
1141
  }
1120
- await writeFileAtomic(file, source);
1121
- return file;
1142
+ await writeFileAtomic(file, source)
1143
+ return file
1122
1144
  }
1123
1145
 
1124
1146
  /**
@@ -1128,12 +1150,12 @@ async function writeCjsNamedExportFacade(bundleFile: string, importHref: string)
1128
1150
  */
1129
1151
  async function executedExportNames(importHref: string) {
1130
1152
  try {
1131
- const module_ = (await import(importHref)) as Record<string, unknown>;
1132
- const source = (module_.default as Record<string, unknown> | undefined) ?? module_;
1133
- if (!source || (typeof source !== 'object' && typeof source !== 'function')) return undefined;
1134
- return Object.keys(source);
1153
+ const module_ = (await import(importHref)) as Record<string, unknown>
1154
+ const source = (module_.default as Record<string, unknown> | undefined) ?? module_
1155
+ if (!source || (typeof source !== 'object' && typeof source !== 'function')) return undefined
1156
+ return Object.keys(source)
1135
1157
  } catch {
1136
- return undefined;
1158
+ return undefined
1137
1159
  }
1138
1160
  }
1139
1161
 
@@ -1152,9 +1174,9 @@ async function facadeRebundles(file: string, source: string) {
1152
1174
  target: 'es2022',
1153
1175
  packages: 'external',
1154
1176
  logLevel: 'silent',
1155
- });
1156
- return true;
1177
+ })
1178
+ return true
1157
1179
  } catch {
1158
- return false;
1180
+ return false
1159
1181
  }
1160
1182
  }