@wular/pnext 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/README.md +11 -11
  2. package/bin/pnext +1 -1
  3. package/config/lint/base.js +7 -7
  4. package/config/ts/base.json +2 -4
  5. package/config/ts/react.json +2 -6
  6. package/package.json +23 -2
  7. package/reference/compat.md +1 -1
  8. package/reference/config.md +2 -2
  9. package/reference/css.md +13 -9
  10. package/reference/dev.md +1 -1
  11. package/reference/metadata.md +7 -7
  12. package/reference/navigation.md +35 -28
  13. package/reference/performance.md +70 -70
  14. package/reference/rendering.md +17 -17
  15. package/reference/routing.md +17 -17
  16. package/reference/typegen.md +12 -8
  17. package/src/api/cache.ts +36 -37
  18. package/src/api/client-cache.ts +2 -2
  19. package/src/api/client-navigation.ts +113 -109
  20. package/src/api/dynamic.tsx +58 -55
  21. package/src/api/link.tsx +49 -52
  22. package/src/api/navigation.ts +59 -58
  23. package/src/api/server.ts +152 -144
  24. package/src/api/suspense.ts +4 -4
  25. package/src/cli/adapters/vercel-warm.ts +126 -121
  26. package/src/cli/adapters/vercel.ts +437 -443
  27. package/src/cli/analyze.ts +333 -144
  28. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  29. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  30. package/src/cli/build.ts +976 -978
  31. package/src/cli/create.ts +71 -63
  32. package/src/cli/dev.ts +116 -116
  33. package/src/cli/index.ts +88 -85
  34. package/src/cli/migrate/package-json.ts +73 -71
  35. package/src/cli/migrate/report.ts +32 -33
  36. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  37. package/src/cli/migrate/scan.ts +46 -46
  38. package/src/cli/migrate/spinner.ts +9 -9
  39. package/src/cli/migrate/tsconfig.ts +35 -35
  40. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  41. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  42. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  43. package/src/cli/start.ts +65 -66
  44. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  45. package/src/client/build.ts +794 -768
  46. package/src/client/chunk-fold.ts +245 -240
  47. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  48. package/src/client/entry.ts +162 -147
  49. package/src/client/prebuilt.ts +231 -223
  50. package/src/client/profile.ts +29 -29
  51. package/src/client/react-compiler.ts +20 -15
  52. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  53. package/src/client/reference-stub.ts +51 -51
  54. package/src/client/reference.ts +19 -19
  55. package/src/{api → client}/router/events.ts +17 -17
  56. package/src/{api → client}/router/history.ts +16 -16
  57. package/src/{api → client}/router/hub.ts +52 -53
  58. package/src/{api/router.ts → client/router/index.ts} +61 -60
  59. package/src/{api → client}/router/policies.ts +24 -24
  60. package/src/{api → client}/router/runtime.ts +1988 -1992
  61. package/src/{api → client}/router/types.ts +93 -98
  62. package/src/compat/actions/client-plugin.ts +42 -43
  63. package/src/compat/actions/client-stub.ts +14 -14
  64. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  65. package/src/compat/actions/config.ts +63 -64
  66. package/src/compat/actions/detect.ts +68 -68
  67. package/src/compat/actions/discovery.ts +105 -105
  68. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  69. package/src/compat/actions/early-submit.ts +1 -1
  70. package/src/compat/actions/endpoint.ts +198 -198
  71. package/src/compat/actions/extensions.ts +811 -0
  72. package/src/compat/actions/flight.ts +23 -23
  73. package/src/compat/actions/form-state.ts +34 -34
  74. package/src/compat/actions/hoist.ts +382 -236
  75. package/src/compat/actions/ids.ts +7 -12
  76. package/src/compat/actions/index.ts +8 -8
  77. package/src/compat/actions/instances.ts +46 -46
  78. package/src/compat/actions/origin.ts +47 -48
  79. package/src/compat/actions/protocol.ts +22 -22
  80. package/src/compat/actions/registry.ts +19 -19
  81. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  82. package/src/compat/actions/rewrite.ts +125 -125
  83. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  84. package/src/compat/actions/serve.ts +132 -136
  85. package/src/compat/actions/server-tag.ts +4 -4
  86. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  87. package/src/compat/actions/unrecognized-error.ts +4 -4
  88. package/src/compat/{index.ts → aliases.ts} +112 -109
  89. package/src/compat/bundler/bun-externals.ts +18 -18
  90. package/src/compat/bundler/cjs-exports.ts +271 -223
  91. package/src/compat/bundler/config.ts +116 -111
  92. package/src/compat/bundler/externals.ts +12 -12
  93. package/src/compat/bundler/import-meta-url.ts +19 -19
  94. package/src/compat/bundler/modularize-imports.ts +36 -33
  95. package/src/compat/bundler/new-url-asset.ts +22 -24
  96. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  97. package/src/compat/bundler/polyfill.ts +28 -28
  98. package/src/compat/bundler/react-compiler.ts +35 -29
  99. package/src/compat/bundler/react-profiler.tsx +11 -11
  100. package/src/compat/bundler/relay-transform.ts +48 -47
  101. package/src/compat/bundler/require-context.ts +119 -113
  102. package/src/compat/bundler/resolve-extensions.ts +19 -19
  103. package/src/compat/bundler/source-cache.ts +25 -25
  104. package/src/compat/bundler/static-imports.ts +7 -7
  105. package/src/compat/bundler/symlink-imports.ts +46 -46
  106. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  107. package/src/compat/bundler/wasm.ts +58 -60
  108. package/src/compat/bundler/webpack-loaders.ts +254 -241
  109. package/src/compat/bundler/worker.ts +101 -104
  110. package/src/compat/cache/build-flags.ts +29 -29
  111. package/src/compat/cache/build-prerender-errors.ts +40 -44
  112. package/src/compat/cache/custom-handler.ts +60 -53
  113. package/src/compat/cache/fetch-patch.ts +240 -240
  114. package/src/compat/cache/handler.ts +27 -31
  115. package/src/compat/cache/modern-handler.ts +148 -126
  116. package/src/compat/cache/resume-data-cache.ts +47 -45
  117. package/src/compat/cache/revalidate.ts +233 -232
  118. package/src/compat/cache/runtime-error.ts +35 -35
  119. package/src/compat/cache/use-cache-transform.ts +442 -417
  120. package/src/compat/cache/use-cache.ts +650 -614
  121. package/src/compat/cache-control.ts +140 -142
  122. package/src/compat/client/base-path.ts +21 -20
  123. package/src/compat/client/css-order.ts +18 -18
  124. package/src/compat/client/errors/bare-boundary.ts +11 -11
  125. package/src/compat/client/errors/control-flow.ts +23 -23
  126. package/src/compat/client/errors/error-boundary.ts +61 -61
  127. package/src/compat/client/errors/global-error.ts +101 -91
  128. package/src/compat/client/errors/install.ts +71 -72
  129. package/src/compat/client/errors/lazy.ts +23 -23
  130. package/src/compat/client/errors/primitive-throw.ts +47 -45
  131. package/src/compat/client/errors/soft-refresh.ts +4 -4
  132. package/src/compat/client/link-status.ts +33 -33
  133. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  134. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  135. package/src/compat/client/navigation-scroll.ts +63 -63
  136. package/src/compat/client/optimistic-routing.ts +93 -88
  137. package/src/compat/client/prefetch-cache.ts +23 -24
  138. package/src/compat/client/route-announcer.ts +35 -35
  139. package/src/compat/client/segment-cache-policy.ts +20 -20
  140. package/src/compat/client/segment-cache.ts +309 -315
  141. package/src/compat/client/segment-prefetch.ts +127 -132
  142. package/src/compat/client/trailing-slash.ts +5 -4
  143. package/src/compat/css/chunking.ts +113 -116
  144. package/src/compat/css/inline-css.ts +21 -21
  145. package/src/compat/css/lightningcss.ts +37 -38
  146. package/src/compat/css/modules.ts +161 -175
  147. package/src/compat/css/nonce.ts +7 -7
  148. package/src/compat/css/sass-plugin.ts +18 -21
  149. package/src/compat/css/sass.ts +150 -152
  150. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  151. package/src/compat/css/styled-jsx.ts +21 -21
  152. package/src/compat/edge-runtime.ts +27 -27
  153. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  154. package/src/compat/export/client.ts +29 -31
  155. package/src/compat/export/{index.ts → emit.ts} +111 -110
  156. package/src/compat/export/standalone.ts +62 -54
  157. package/src/compat/image-optimizer/cache.ts +51 -49
  158. package/src/compat/image-optimizer/detect.ts +52 -52
  159. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  160. package/src/compat/image-optimizer/source.ts +77 -80
  161. package/src/compat/lifecycle/after-scope.ts +26 -26
  162. package/src/compat/lifecycle/after.ts +48 -45
  163. package/src/compat/lifecycle/error-funnel.ts +98 -102
  164. package/src/compat/lifecycle/error-serialize.ts +30 -26
  165. package/src/compat/lifecycle/error-ui.ts +67 -32
  166. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  167. package/src/compat/lifecycle/instrumentation.ts +85 -85
  168. package/src/compat/lifecycle/node-console.ts +11 -11
  169. package/src/compat/lifecycle/testmode.ts +160 -132
  170. package/src/compat/mdx/compile.ts +60 -57
  171. package/src/compat/mdx/plugin.ts +11 -11
  172. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  173. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  174. package/src/compat/metadata.ts +75 -81
  175. package/src/compat/next/cache.ts +68 -69
  176. package/src/compat/next/canonical-url.ts +9 -9
  177. package/src/compat/next/client-cache.ts +19 -19
  178. package/src/compat/next/client-navigation.ts +112 -116
  179. package/src/compat/next/client-only.ts +1 -1
  180. package/src/compat/next/client-script.tsx +99 -93
  181. package/src/compat/next/client-server.ts +10 -10
  182. package/src/compat/next/config-loader.ts +176 -173
  183. package/src/compat/next/config.ts +7 -7
  184. package/src/compat/next/constants.cjs +6 -6
  185. package/src/compat/next/constants.ts +6 -6
  186. package/src/compat/next/custom-server.ts +26 -24
  187. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  188. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  189. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  190. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  191. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  192. package/src/compat/next/dynamic.tsx +21 -18
  193. package/src/compat/next/error.tsx +52 -52
  194. package/src/compat/next/font/cache.ts +74 -74
  195. package/src/compat/next/font/google.ts +2 -2
  196. package/src/compat/next/font/index.ts +1 -1
  197. package/src/compat/next/font/local.ts +3 -3
  198. package/src/compat/next/font/runtime-client.ts +17 -15
  199. package/src/compat/next/font/runtime.ts +443 -408
  200. package/src/compat/next/font/shared.ts +120 -108
  201. package/src/compat/next/form.tsx +63 -63
  202. package/src/compat/next/head.tsx +2 -2
  203. package/src/compat/next/headers.ts +103 -95
  204. package/src/compat/next/image/client.tsx +220 -0
  205. package/src/compat/next/image/config.ts +56 -58
  206. package/src/compat/next/image/optimizer.ts +40 -36
  207. package/src/compat/next/image/patterns.ts +37 -40
  208. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  209. package/src/compat/next/image/shared.ts +65 -57
  210. package/src/compat/next/image/static-metadata.ts +98 -107
  211. package/src/compat/next/image/validate.ts +79 -88
  212. package/src/compat/next/image.tsx +19 -23
  213. package/src/compat/next/index.ts +1 -1
  214. package/src/compat/next/legacy-image.tsx +59 -60
  215. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  216. package/src/compat/next/link.tsx +158 -158
  217. package/src/compat/next/navigation.cjs +12 -3
  218. package/src/compat/next/navigation.ts +48 -50
  219. package/src/compat/next/offline.ts +27 -27
  220. package/src/compat/next/og.ts +121 -124
  221. package/src/compat/next/preferred-region.ts +13 -14
  222. package/src/compat/next/redirects.ts +58 -56
  223. package/src/compat/next/resource-hints.ts +73 -76
  224. package/src/compat/next/rewrites.ts +130 -133
  225. package/src/compat/next/root-params.ts +45 -45
  226. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  227. package/src/compat/next/router.cjs +4 -2
  228. package/src/compat/next/router.ts +58 -61
  229. package/src/compat/next/script.tsx +108 -108
  230. package/src/compat/next/server-only.ts +1 -1
  231. package/src/compat/next/server.ts +19 -19
  232. package/src/compat/next/svgr.ts +18 -17
  233. package/src/compat/next/telemetry.ts +24 -24
  234. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  235. package/src/compat/next/user-agent.ts +53 -49
  236. package/src/compat/next/web-vitals.ts +22 -24
  237. package/src/compat/otel/api.ts +41 -41
  238. package/src/compat/otel/client-trace-metadata.ts +25 -27
  239. package/src/compat/otel/fetch-span.ts +29 -29
  240. package/src/compat/otel/tracer.ts +331 -331
  241. package/src/compat/pages/api.ts +456 -0
  242. package/src/compat/pages/client-plugin.ts +36 -36
  243. package/src/compat/pages/router-state.ts +34 -34
  244. package/src/compat/pages/{index.ts → router.ts} +130 -135
  245. package/src/compat/ppr/io.ts +12 -12
  246. package/src/compat/ppr/missing-root-params.ts +34 -38
  247. package/src/compat/ppr/root-params-scan.ts +66 -66
  248. package/src/compat/ppr/root-params-transform.ts +24 -26
  249. package/src/compat/ppr/root-params.ts +30 -30
  250. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  251. package/src/compat/protocol.ts +71 -70
  252. package/src/compat/react/action-state.ts +47 -48
  253. package/src/compat/react/client-lite.ts +15 -15
  254. package/src/compat/react/client.ts +4 -4
  255. package/src/compat/react/compiler-runtime.ts +11 -11
  256. package/src/compat/react/dom-client.ts +44 -44
  257. package/src/compat/react/dom-react-server.ts +10 -16
  258. package/src/compat/react/dom-server.ts +10 -10
  259. package/src/compat/react/dom.ts +52 -52
  260. package/src/compat/react/hooks-extra.ts +34 -35
  261. package/src/compat/react/parity.ts +64 -61
  262. package/src/compat/react/preact.ts +81 -82
  263. package/src/compat/react/react-server.ts +28 -28
  264. package/src/compat/react/router-shim.ts +1 -1
  265. package/src/compat/react/server-component-use.ts +8 -8
  266. package/src/compat/react/server-inserted-html.ts +30 -31
  267. package/src/compat/react/server.ts +53 -55
  268. package/src/compat/react/use.ts +32 -32
  269. package/src/compat/react/view-transition.ts +20 -20
  270. package/src/compat/register/actions.ts +35 -824
  271. package/src/compat/register/boot.ts +41 -41
  272. package/src/compat/register/build-tier.ts +5 -5
  273. package/src/compat/register/build.ts +74 -70
  274. package/src/compat/register/bundler.ts +161 -156
  275. package/src/compat/register/cache.ts +20 -20
  276. package/src/compat/register/client-errors.ts +3 -3
  277. package/src/compat/register/config.ts +6 -6
  278. package/src/compat/register/css-extras.ts +30 -34
  279. package/src/compat/register/edge-runtime.ts +3 -3
  280. package/src/compat/register/errors.ts +10 -12
  281. package/src/compat/register/export.ts +16 -16
  282. package/src/compat/register/font.ts +11 -11
  283. package/src/compat/register/hooks.ts +2 -2
  284. package/src/compat/register/image.ts +40 -40
  285. package/src/compat/register/index.ts +59 -62
  286. package/src/compat/register/instrumentation-client.ts +10 -10
  287. package/src/compat/register/lifecycle.ts +25 -28
  288. package/src/compat/register/mdx.ts +10 -10
  289. package/src/compat/register/middleware.ts +226 -16
  290. package/src/compat/register/otel.ts +80 -88
  291. package/src/compat/register/pages-api.ts +10 -463
  292. package/src/compat/register/ppr.ts +16 -16
  293. package/src/compat/register/protocol.ts +17 -18
  294. package/src/compat/register/proxy.ts +49 -51
  295. package/src/compat/register/render.ts +79 -76
  296. package/src/compat/register/routing.ts +162 -159
  297. package/src/compat/register/segment.ts +24 -1897
  298. package/src/compat/register/static-image.ts +3 -3
  299. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  300. package/src/compat/register/typed-routes.ts +14 -14
  301. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  302. package/src/compat/register/validation.ts +18 -18
  303. package/src/compat/segment/loading-boundary.ts +43 -45
  304. package/src/compat/segment/page-slot.ts +69 -69
  305. package/src/compat/segment/serve.ts +1884 -0
  306. package/src/compat/segment/tree.ts +113 -112
  307. package/src/compat/segment/vary-key.ts +38 -38
  308. package/src/compat/segment/vary-params.ts +138 -142
  309. package/src/compat/static-params.ts +14 -12
  310. package/src/compat/tsconfig-defaults.ts +87 -91
  311. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  312. package/src/compat/typecheck/worker.ts +15 -12
  313. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  314. package/src/compat/typed-routes/manifest.ts +174 -170
  315. package/src/compat/typed-routes/typegen.ts +127 -110
  316. package/src/compat/validation/errors.ts +16 -19
  317. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  318. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  319. package/src/compat-bootstrap.ts +16 -16
  320. package/src/config.ts +69 -69
  321. package/src/css/build.ts +230 -227
  322. package/src/css/postcss.ts +79 -80
  323. package/src/css/worker.ts +14 -15
  324. package/src/dev/client-actions.ts +10 -10
  325. package/src/dev/client-chunk-store.ts +27 -27
  326. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  327. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  328. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  329. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  330. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  331. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  332. package/src/dev/server.ts +804 -820
  333. package/src/env.ts +46 -43
  334. package/src/extensions.ts +491 -478
  335. package/src/index.ts +8 -8
  336. package/src/internal.ts +20 -23
  337. package/src/{islands → render}/boundary-error.ts +3 -3
  338. package/src/render/hooks.ts +71 -71
  339. package/src/render/island-context.ts +14 -14
  340. package/src/render/metadata.ts +310 -310
  341. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  342. package/src/{ppr.ts → render/ppr.ts} +244 -245
  343. package/src/render/renderer.ts +2076 -2082
  344. package/src/render/resource-hints.ts +16 -17
  345. package/src/render/slots.tsx +224 -235
  346. package/src/{islands → render}/static-children.ts +9 -12
  347. package/src/{islands → render}/static-slots.ts +37 -37
  348. package/src/{cache/context.ts → request/cache.ts} +20 -20
  349. package/src/request/context.ts +107 -107
  350. package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
  351. package/src/resolve/engine.ts +90 -77
  352. package/src/resolve/imports.ts +475 -463
  353. package/src/resolve/scan-facts.ts +318 -296
  354. package/src/resolve/source-text.ts +37 -37
  355. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  356. package/src/routing/forwarded.ts +19 -19
  357. package/src/routing/handler.ts +84 -91
  358. package/src/routing/href.ts +69 -70
  359. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  360. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  361. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  362. package/src/routing/routes.ts +827 -815
  363. package/src/routing/slots.ts +164 -160
  364. package/src/runtime/loader.ts +952 -0
  365. package/src/{dev → runtime}/module-cache.ts +309 -287
  366. package/src/{dev → runtime}/module-generations.ts +9 -9
  367. package/src/{dev → runtime}/module-transform.ts +81 -72
  368. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  369. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  370. package/src/runtime/vendor.ts +425 -404
  371. package/src/styles.d.ts +9 -0
  372. package/src/types.ts +320 -335
  373. package/src/utils/ansi.ts +5 -5
  374. package/src/utils/{source.ts → code.ts} +15 -12
  375. package/src/utils/content-type.ts +3 -3
  376. package/src/utils/decode.ts +2 -2
  377. package/src/utils/dev-profile.ts +13 -13
  378. package/src/utils/error-log.ts +6 -6
  379. package/src/utils/esbuild.ts +18 -18
  380. package/src/utils/fs-cache.ts +13 -13
  381. package/src/utils/fs.ts +57 -49
  382. package/src/utils/html.ts +20 -24
  383. package/src/utils/native-require.ts +8 -8
  384. package/src/utils/serialize.ts +139 -146
  385. package/src/utils/verbose.ts +18 -18
  386. package/src/cli/analyze-print.ts +0 -181
  387. package/src/compat/middleware/manifest.ts +0 -210
  388. package/src/compat/next/image-client.tsx +0 -215
  389. package/src/compat/next/link-usage.ts +0 -29
  390. package/src/css/index.ts +0 -2
  391. package/src/render/index.ts +0 -1
  392. package/src/style-modules.d.ts +0 -9
@@ -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
  /**
@@ -418,16 +426,16 @@ async function runVendorDemand(plan: VendorBuildPlan, nested: boolean, ticket: V
418
426
  */
419
427
  const VENDOR_GROUP_WINDOW_MS = (() => {
420
428
  // 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
- })();
429
+ const override = Number(process.env.PNEXT_VENDOR_GROUP_WINDOW_MS)
430
+ return Number.isFinite(override) && override >= 0 ? override : 5
431
+ })()
424
432
 
425
433
  /**
426
434
  * A group re-parses the package graph once per round, so an unbounded trickle
427
435
  * of stragglers must not turn into an unbounded chain of rebuilds. Past the
428
436
  * bound the package goes back to a build per subpath.
429
437
  */
430
- const MAX_VENDOR_GROUP_ROUNDS = 4;
438
+ const MAX_VENDOR_GROUP_ROUNDS = 4
431
439
 
432
440
  /**
433
441
  * Escape hatch: `PNEXT_VENDOR_GROUP=0` puts every subpath back on its own build. Grouping is the one part of
@@ -436,7 +444,7 @@ const MAX_VENDOR_GROUP_ROUNDS = 4;
436
444
  */
437
445
  function vendorGroupingEnabled() {
438
446
  // eslint-disable-next-line turbo/no-undeclared-env-vars
439
- return process.env.PNEXT_VENDOR_GROUP !== '0';
447
+ return process.env.PNEXT_VENDOR_GROUP !== '0'
440
448
  }
441
449
 
442
450
  /**
@@ -449,13 +457,9 @@ function vendorGroupingEnabled() {
449
457
  * artifacts from earlier rounds stay valid and are referenced as externals, so a growth round costs
450
458
  * what that sibling's own build would have cost.
451
459
  */
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);
460
+ async function joinVendorGroup(group: VendorGroupPlan, nested: boolean, ticket: VendorPromotable) {
461
+ if (!vendorGroupingEnabled()) return false
462
+ let state = vendorGroups.get(group.key)
459
463
  if (!state) {
460
464
  state = {
461
465
  pending: new Map(),
@@ -463,37 +467,37 @@ async function joinVendorGroup(
463
467
  solo: new Map(),
464
468
  rounds: 0,
465
469
  closed: false,
466
- };
467
- vendorGroups.set(group.key, state);
470
+ }
471
+ vendorGroups.set(group.key, state)
468
472
  }
469
- const specifier = group.member.specifier;
470
- if (state.closed || state.solo.has(specifier)) return false;
473
+ const specifier = group.member.specifier
474
+ if (state.closed || state.solo.has(specifier)) return false
471
475
  // A demand raised from INSIDE a running round cannot wait for that round:
472
476
  // the round is waiting on this demand. Build it alone, and leave it on file
473
477
  // so the group externalizes the artifact instead of re-parsing it.
474
478
  if (nested && state.round && !state.pending.has(specifier)) {
475
- state.solo.set(specifier, group.member);
476
- return false;
479
+ state.solo.set(specifier, group.member)
480
+ return false
477
481
  }
478
- state.pending.set(specifier, group.member);
482
+ state.pending.set(specifier, group.member)
479
483
  // While this demand waits on a shared round, promoting IT has to promote the
480
484
  // round — that is the only work its result depends on.
481
- const groupState = state;
482
- ticket.promote = () => promoteVendorWork(groupState.ticket);
485
+ const groupState = state
486
+ ticket.promote = () => promoteVendorWork(groupState.ticket)
483
487
  try {
484
488
  while (!state.produced.has(specifier)) {
485
- if (state.closed || state.solo.has(specifier)) return false;
486
- if (nested) promoteVendorWork(state.ticket);
489
+ if (state.closed || state.solo.has(specifier)) return false
490
+ if (nested) promoteVendorWork(state.ticket)
487
491
  await watchVendorWait(
488
492
  nested ? 'group-round-nested' : 'group-round',
489
493
  `${group.key} :: ${specifier}`,
490
494
  runVendorGroupRound(group, state, nested),
491
- );
495
+ )
492
496
  }
493
497
  } finally {
494
- ticket.promote = undefined;
498
+ ticket.promote = undefined
495
499
  }
496
- return true;
500
+ return true
497
501
  }
498
502
 
499
503
  function runVendorGroupRound(
@@ -503,73 +507,69 @@ function runVendorGroupRound(
503
507
  ): Promise<void> {
504
508
  if (!state.round) {
505
509
  state.round = vendorGroupRound(group, state, nested).finally(() => {
506
- state.round = undefined;
507
- state.ticket = undefined;
508
- });
510
+ state.round = undefined
511
+ state.ticket = undefined
512
+ })
509
513
  }
510
- return state.round;
514
+ return state.round
511
515
  }
512
516
 
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;
517
+ async function vendorGroupRound(group: VendorGroupPlan, state: VendorGroupState, nested: boolean) {
518
+ await new Promise(resolve => setTimeout(resolve, VENDOR_GROUP_WINDOW_MS))
519
+ const members = [...state.pending.values()]
520
+ if (members.length === 0) return
521
521
  // A lone subpath with no sibling to share a graph with is the single-bundle
522
522
  // path plus chunk plumbing. Hand it back — and keep it on file, so a sibling
523
523
  // demanded later externalizes its artifact instead of re-parsing it.
524
524
  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;
525
+ const only = members[0]!
526
+ state.pending.delete(only.specifier)
527
+ state.solo.set(only.specifier, only)
528
+ return
529
529
  }
530
- state.rounds += 1;
531
- const external = [...state.produced.values(), ...state.solo.values()];
532
- const ticket: VendorPromotable = {};
533
- state.ticket = ticket;
530
+ state.rounds += 1
531
+ const external = [...state.produced.values(), ...state.solo.values()]
532
+ const ticket: VendorPromotable = {}
533
+ state.ticket = ticket
534
534
  try {
535
- await withVendorSlot(nested, group.key, () => group.build(members, external, nested), ticket);
535
+ await withVendorSlot(nested, group.key, () => group.build(members, external, nested), ticket)
536
536
  for (const member of members) {
537
- state.pending.delete(member.specifier);
538
- state.produced.set(member.specifier, member);
537
+ state.pending.delete(member.specifier)
538
+ state.produced.set(member.specifier, member)
539
539
  }
540
540
  } catch {
541
541
  // Grouping is an optimization: one bad sibling must not fail the demand,
542
542
  // so the group gives up and every member falls back to its own build.
543
- state.closed = true;
543
+ state.closed = true
544
544
  }
545
- if (state.rounds >= MAX_VENDOR_GROUP_ROUNDS) state.closed = true;
545
+ if (state.rounds >= MAX_VENDOR_GROUP_ROUNDS) state.closed = true
546
546
  }
547
547
 
548
548
  // --------------------------------------------------------------------------
549
549
  // pre-planned builds: enqueued without an awaiter, drained at route-module eval
550
550
  // --------------------------------------------------------------------------
551
551
 
552
- const preplanBuilds = new Set<Promise<unknown>>();
553
- let preplanBuildError: unknown;
552
+ const preplanBuilds = new Set<Promise<unknown>>()
553
+ let preplanBuildError: unknown
554
554
 
555
555
  export function trackPreplanBuild(promise: Promise<unknown>) {
556
556
  const tracked = promise.catch(error => {
557
- preplanBuildError ??= error;
558
- });
559
- preplanBuilds.add(tracked);
560
- void tracked.finally(() => preplanBuilds.delete(tracked));
557
+ preplanBuildError ??= error
558
+ })
559
+ preplanBuilds.add(tracked)
560
+ void tracked.finally(() => preplanBuilds.delete(tracked))
561
561
  }
562
562
 
563
563
  /** The one join point: nothing evaluates until every enqueued build settles. */
564
564
  export async function drainPreplanBuilds() {
565
565
  while (preplanBuilds.size > 0) {
566
- await watchVendorWait('preplan-drain', 'preplan', Promise.allSettled([...preplanBuilds]));
566
+ await watchVendorWait('preplan-drain', 'preplan', Promise.allSettled([...preplanBuilds]))
567
567
  }
568
568
  if (preplanBuildError !== undefined) {
569
- const error = preplanBuildError;
570
- preplanBuildError = undefined;
569
+ const error = preplanBuildError
570
+ preplanBuildError = undefined
571
571
  // eslint-disable-next-line @typescript-eslint/only-throw-error
572
- throw error;
572
+ throw error
573
573
  }
574
574
  }
575
575
 
@@ -579,29 +579,29 @@ export async function drainPreplanBuilds() {
579
579
  * evicts, so concurrent demands join the one rebuild instead of racing.
580
580
  */
581
581
  export function dropVendorBundle(key: string, stale: string) {
582
- const pending = pendingBundles.get(key);
583
- if (pending?.result === stale) pendingBundles.delete(key);
582
+ const pending = pendingBundles.get(key)
583
+ if (pending?.result === stale) pendingBundles.delete(key)
584
584
  }
585
585
 
586
586
  /** Forget a group whose artifacts were wiped, so the retry rebuilds them. */
587
587
  export function dropVendorGroup(key: string) {
588
- vendorGroups.delete(key);
588
+ vendorGroups.delete(key)
589
589
  }
590
590
 
591
591
  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;
592
+ pendingBundles.clear()
593
+ vendorGroups.clear()
594
+ preplanBuilds.clear()
595
+ preplanBuildError = undefined
596
+ esmDistFiles.clear()
597
+ vendorContentIds.clear()
598
+ vendorSpans.length = 0
599
599
  // In-flight waits belong to the pipeline that was just dropped; leaving them
600
600
  // registered would have the watchdog report a stall that no longer exists.
601
- vendorWaits.clear();
601
+ vendorWaits.clear()
602
602
  if (stallTimer) {
603
- clearInterval(stallTimer);
604
- stallTimer = undefined;
603
+ clearInterval(stallTimer)
604
+ stallTimer = undefined
605
605
  }
606
606
  }
607
607
 
@@ -610,14 +610,14 @@ export function clearVendorPipeline() {
610
610
  // --------------------------------------------------------------------------
611
611
 
612
612
  /** Artifact (or facade) file -> content id of the bytes it resolves to. */
613
- const vendorContentIds = new Map<string, string>();
613
+ const vendorContentIds = new Map<string, string>()
614
614
 
615
615
  export function vendorContentId(file: string) {
616
- return vendorContentIds.get(file);
616
+ return vendorContentIds.get(file)
617
617
  }
618
618
 
619
619
  export function noteVendorContentId(file: string, id: string) {
620
- vendorContentIds.set(file, id);
620
+ vendorContentIds.set(file, id)
621
621
  }
622
622
 
623
623
  /**
@@ -626,10 +626,10 @@ export function noteVendorContentId(file: string, id: string) {
626
626
  */
627
627
  export function canonicalVendorCode(code: string) {
628
628
  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
- });
629
+ if (!ref.specifier.startsWith('file:')) return undefined
630
+ const id = vendorContentIds.get(fileURLToPath(ref.specifier))
631
+ return id ? `pnext-content:${id}` : undefined
632
+ })
633
633
  }
634
634
 
635
635
  // --------------------------------------------------------------------------
@@ -642,61 +642,58 @@ export function canonicalVendorCode(code: string) {
642
642
  * and cost a JS bridge hop per file.
643
643
  */
644
644
  export interface EmittedRef {
645
- specifier: string;
645
+ specifier: string
646
646
  /** esbuild emits an external `require()` as `__require("x")`. */
647
- require: boolean;
647
+ require: boolean
648
648
  /** Offsets of the specifier text itself, quotes excluded. */
649
- start: number;
650
- end: number;
649
+ start: number
650
+ end: number
651
651
  }
652
652
 
653
653
  // Import/export statements, dynamic imports, and (esbuild's) require calls.
654
654
  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;
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
657
657
 
658
658
  /** Every bare-specifier reference the emitted code still carries. */
659
659
  export function emittedRefs(code: string): EmittedRef[] {
660
- const refs: EmittedRef[] = [];
661
- const seen = new Set<number>();
660
+ const refs: EmittedRef[] = []
661
+ const seen = new Set<number>()
662
662
  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 });
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 })
667
667
  }
668
668
  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);
669
+ const value = match[3]!
670
+ const start = match.index + match[0].lastIndexOf(value)
671
+ if (seen.has(start)) continue
672
+ seen.add(start)
673
673
  refs.push({
674
674
  specifier: value,
675
675
  require: match[0].includes('require'),
676
676
  start,
677
677
  end: start + value.length,
678
- });
678
+ })
679
679
  }
680
- return refs.sort((a, b) => a.start - b.start);
680
+ return refs.sort((a, b) => a.start - b.start)
681
681
  }
682
682
 
683
683
  /**
684
684
  * Repoint emitted references. `replace` returns the new specifier, or undefined
685
685
  * to leave one alone — a bare package name the runtime resolves itself.
686
686
  */
687
- export function rewriteEmittedRefs(
688
- code: string,
689
- replace: (ref: EmittedRef) => string | undefined,
690
- ) {
691
- let out = '';
692
- let cursor = 0;
687
+ export function rewriteEmittedRefs(code: string, replace: (ref: EmittedRef) => string | undefined) {
688
+ let out = ''
689
+ let cursor = 0
693
690
  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;
691
+ const next = replace(ref)
692
+ if (next === undefined || next === ref.specifier) continue
693
+ out += code.slice(cursor, ref.start) + next.replace(/\\/g, '\\\\')
694
+ cursor = ref.end
698
695
  }
699
- return cursor === 0 ? code : out + code.slice(cursor);
696
+ return cursor === 0 ? code : out + code.slice(cursor)
700
697
  }
701
698
 
702
699
  /**
@@ -705,21 +702,25 @@ export function rewriteEmittedRefs(
705
702
  * actually bind what its importer asks for.
706
703
  */
707
704
  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;
705
+ const names = new Set<string>()
706
+ if (/(^|\n)\s*export\s*\*\s*from/.test(code)) return undefined
710
707
  for (const match of code.matchAll(/(^|[\s;}])export\s*\{([^}]*)\}/g)) {
711
708
  for (const entry of match[2]!.split(',')) {
712
- const name = entry.trim().split(/\s+as\s+/).pop()?.trim();
713
- if (name) names.add(name);
709
+ const name = entry
710
+ .trim()
711
+ .split(/\s+as\s+/)
712
+ .pop()
713
+ ?.trim()
714
+ if (name) names.add(name)
714
715
  }
715
716
  }
716
717
  for (const match of code.matchAll(
717
718
  /(^|[\s;}])export\s+(?:declare\s+)?(?:async\s+)?(?:const|let|var|function\*?|class)\s+([A-Za-z_$][\w$]*)/g,
718
719
  )) {
719
- names.add(match[2]!);
720
+ names.add(match[2]!)
720
721
  }
721
- if (/(^|[\s;}])export\s+default[\s({[]/.test(code)) names.add('default');
722
- return names;
722
+ if (/(^|[\s;}])export\s+default[\s({[]/.test(code)) names.add('default')
723
+ return names
723
724
  }
724
725
 
725
726
  /**
@@ -727,34 +728,34 @@ export function emittedExportNames(code: string): Set<string> | undefined {
727
728
  * Applied to the bundle itself (not a separate module) for the inlined case.
728
729
  */
729
730
  export function addCommonJsNamedExports(code: string) {
730
- if (hasNonDefaultExport(code)) return code;
731
+ if (hasNonDefaultExport(code)) return code
731
732
  const names = [...new Set([...commonJsExportNames(code), ...esbuildEntryExportNames(code)])]
732
733
  .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];
734
+ .sort()
735
+ if (names.length === 0) return code
736
+ const defaultExport = /(^|\n)export default ([^;\n]+);/.exec(code)
737
+ if (!defaultExport?.[2]) return code
738
+ const defaultName = uniqueIdentifier(code, '__pnext_cjs_default')
739
+ const usedNames = [defaultName]
739
740
  const bindings = names.map(name => {
740
- const localName = uniqueIdentifier(code, `__pnext_cjs_export_${name}`, ...usedNames);
741
- usedNames.push(localName);
742
- return { name, localName };
743
- });
741
+ const localName = uniqueIdentifier(code, `__pnext_cjs_export_${name}`, ...usedNames)
742
+ usedNames.push(localName)
743
+ return { name, localName }
744
+ })
744
745
  const namedBindings = bindings
745
746
  .map(({ name, localName }) => `const ${localName} = ${defaultName}[${JSON.stringify(name)}];`)
746
- .join('\n');
747
- const namedExports = bindings.map(({ name, localName }) => `${localName} as ${name}`).join(', ');
747
+ .join('\n')
748
+ const namedExports = bindings.map(({ name, localName }) => `${localName} as ${name}`).join(', ')
748
749
  return code.replace(
749
750
  defaultExport[0],
750
751
  `${defaultExport[1]}const ${defaultName} = ${defaultExport[2]};\nexport default ${defaultName};\n${namedBindings}\nexport { ${namedExports} };\n`,
751
- );
752
+ )
752
753
  }
753
754
 
754
755
  function hasNonDefaultExport(code: string) {
755
756
  return /(^|\n)export\s+(?:\*|\{[^}]*\b(?!default\b)[A-Za-z_$][\w$]*\b|(?:const|let|var|function|class)\s+)/.test(
756
757
  code,
757
- );
758
+ )
758
759
  }
759
760
 
760
761
  export function commonJsExportNames(code: string) {
@@ -767,7 +768,7 @@ export function commonJsExportNames(code: string) {
767
768
  ]),
768
769
  ]
769
770
  .filter((name): name is string => Boolean(name && name !== '__esModule' && isIdentifier(name)))
770
- .sort();
771
+ .sort()
771
772
  }
772
773
 
773
774
  // Recover named exports from esbuild's `__export(entry, {...}); module.exports =
@@ -775,16 +776,16 @@ export function commonJsExportNames(code: string) {
775
776
  function esbuildEntryExportNames(code: string) {
776
777
  const toCommonJs = [
777
778
  ...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 [];
779
+ ]
780
+ const entryVar = toCommonJs.at(-1)?.[1]
781
+ if (!entryVar) return []
781
782
  const exportCall = new RegExp(
782
783
  `__export\\w*\\(\\s*${escapeRegex(entryVar)}\\s*,\\s*\\{([\\s\\S]*?)\\}\\s*\\)`,
783
- ).exec(code);
784
- if (!exportCall?.[1]) return [];
784
+ ).exec(code)
785
+ if (!exportCall?.[1]) return []
785
786
  return [...exportCall[1].matchAll(/(?:^|,)\s*([A-Za-z_$][\w$]*)\s*:/g)]
786
787
  .map(match => match[1])
787
- .filter((name): name is string => Boolean(name && isIdentifier(name)));
788
+ .filter((name): name is string => Boolean(name && isIdentifier(name)))
788
789
  }
789
790
 
790
791
  /**
@@ -802,16 +803,16 @@ export async function verifyVendorArtifact(
802
803
  chunks: ReadonlyMap<string, string> = new Map(),
803
804
  ) {
804
805
  for (const [specifier, target] of repointed) {
805
- if (!existsSync(target)) return `repointed ${specifier} -> missing ${target}`;
806
+ if (!existsSync(target)) return `repointed ${specifier} -> missing ${target}`
806
807
  }
807
808
  // Nothing was rewritten and nothing was split out: this is esbuild's own
808
809
  // output, unedited, and there is no claim left for a re-bundle to check.
809
- if (repointed.size === 0 && chunks.size === 0) return undefined;
810
+ if (repointed.size === 0 && chunks.size === 0) return undefined
810
811
  // 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);
812
+ const mode = process.env.PNEXT_VENDOR_VERIFY
813
+ if (mode === 'off') return undefined
814
+ const deep = mode === 'deep'
815
+ const outDir = path.dirname(file)
815
816
  try {
816
817
  await build({
817
818
  stdin: { contents: code, resolveDir: outDir, sourcefile: file, loader: 'js' },
@@ -830,32 +831,34 @@ export async function verifyVendorArtifact(
830
831
  // before anything is published, so nothing half-written is ever
831
832
  // readable by a concurrent demand.
832
833
  verify.onResolve({ filter: /^\.\.?\// }, args => {
833
- const resolved = path.resolve(args.resolveDir, args.path);
834
+ const resolved = path.resolve(args.resolveDir, args.path)
834
835
  return chunks.has(resolved)
835
836
  ? { path: resolved, namespace: 'pnext-vendor-chunk' }
836
- : undefined;
837
- });
837
+ : undefined
838
+ })
838
839
  verify.onLoad({ filter: /.*/, namespace: 'pnext-vendor-chunk' }, args => ({
839
840
  contents: chunks.get(args.path),
840
841
  loader: 'js',
841
842
  resolveDir: path.dirname(args.path),
842
- }));
843
+ }))
843
844
  verify.onResolve({ filter: /^file:/ }, args => {
844
- const resolved = fileURLToPath(args.path);
845
+ const resolved = fileURLToPath(args.path)
845
846
  // Deep: one level in (esbuild then checks the export names), and
846
847
  // only from the artifact itself — its dependencies' own externals
847
848
  // 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
- });
849
+ const inside = args.importer === file || args.importer.startsWith(outDir)
850
+ return { path: resolved, external: !deep || !inside }
851
+ })
851
852
  },
852
853
  },
853
854
  ],
854
- });
855
+ })
855
856
  } catch (error) {
856
- return (error instanceof Error ? error.message : String(error)).replace(/\s+/g, ' ').slice(0, 300);
857
+ return (error instanceof Error ? error.message : String(error))
858
+ .replace(/\s+/g, ' ')
859
+ .slice(0, 300)
857
860
  }
858
- return undefined;
861
+ return undefined
859
862
  }
860
863
 
861
864
  // --------------------------------------------------------------------------
@@ -872,46 +875,46 @@ export async function verifyVendorArtifact(
872
875
  * in the closure means a plugin could have fired, so the package goes back to the bundler. A false negative
873
876
  * costs a compile; a false positive ships wrong code.
874
877
  */
875
- const MAX_ESM_DIST_FILES = 4096;
876
- const ESM_DIST_ENTRY_MARKER = 'entry';
878
+ const MAX_ESM_DIST_FILES = 4096
879
+ const ESM_DIST_ENTRY_MARKER = 'entry'
877
880
 
878
- const esmDists = new Map<string, Promise<string | undefined>>();
881
+ const esmDists = new Map<string, Promise<string | undefined>>()
879
882
 
880
883
  export function copyBrowserReadyEsmDist(entry: string, distDir: string) {
881
884
  // One copy per destination: a second copier would rewrite files the first has
882
885
  // already published, and copyFile is not atomic against a concurrent import.
883
- let dist = esmDists.get(distDir);
886
+ let dist = esmDists.get(distDir)
884
887
  if (!dist) {
885
- dist = copyBrowserReadyEsmDistUncached(entry, distDir);
886
- esmDists.set(distDir, dist);
888
+ dist = copyBrowserReadyEsmDistUncached(entry, distDir)
889
+ esmDists.set(distDir, dist)
887
890
  }
888
- return dist;
891
+ return dist
889
892
  }
890
893
 
891
894
  async function copyBrowserReadyEsmDistUncached(entry: string, distDir: string) {
892
895
  const existing = await readFile(path.join(distDir, ESM_DIST_ENTRY_MARKER), 'utf8').catch(
893
896
  () => undefined,
894
- );
895
- if (existing !== undefined) return path.join(distDir, existing);
897
+ )
898
+ if (existing !== undefined) return path.join(distDir, existing)
896
899
 
897
- const closure = await collectBrowserReadyEsmClosure(entry);
898
- if (!closure) return undefined;
900
+ const closure = await collectBrowserReadyEsmClosure(entry)
901
+ if (!closure) return undefined
899
902
  // Copies keep their offsets relative to the shallowest directory the closure
900
903
  // 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);
904
+ const base = commonAncestorDir([...closure])
905
+ const relative = (file: string) => path.relative(base, file)
903
906
  await Promise.all(
904
907
  [...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);
908
+ const target = path.join(distDir, relative(file))
909
+ await mkdir(path.dirname(target), { recursive: true })
910
+ await copyFile(file, target)
908
911
  }),
909
- );
910
- await writeFileAtomic(path.join(distDir, ESM_DIST_ENTRY_MARKER), relative(entry));
911
- return path.join(distDir, relative(entry));
912
+ )
913
+ await writeFileAtomic(path.join(distDir, ESM_DIST_ENTRY_MARKER), relative(entry))
914
+ return path.join(distDir, relative(entry))
912
915
  }
913
916
 
914
- const ESM_DIST_EXTENSIONS = new Set(['.js', '.mjs']);
917
+ const ESM_DIST_EXTENSIONS = new Set(['.js', '.mjs'])
915
918
 
916
919
  /**
917
920
  * Per-FILE verdict, memoized: the predicate runs once per demanded artifact and
@@ -919,85 +922,85 @@ const ESM_DIST_EXTENSIONS = new Set(['.js', '.mjs']);
919
922
  * thousand files were re-read, re-regexed and re-`existsSync`ed per subpath.
920
923
  * `undefined` means "not browser-ready", which fails the whole closure.
921
924
  */
922
- const esmDistFiles = new Map<string, Promise<string[] | undefined>>();
925
+ const esmDistFiles = new Map<string, Promise<string[] | undefined>>()
923
926
 
924
927
  function browserReadyEsmImports(file: string) {
925
- let entry = esmDistFiles.get(file);
928
+ let entry = esmDistFiles.get(file)
926
929
  if (!entry) {
927
- entry = browserReadyEsmImportsUncached(file);
928
- esmDistFiles.set(file, entry);
930
+ entry = browserReadyEsmImportsUncached(file)
931
+ esmDistFiles.set(file, entry)
929
932
  }
930
- return entry;
933
+ return entry
931
934
  }
932
935
 
933
936
  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;
937
+ if (!ESM_DIST_EXTENSIONS.has(path.extname(file))) return undefined
938
+ const source = await readFile(file, 'utf8').catch(() => undefined)
939
+ if (source === undefined) return undefined
937
940
  // `use cache` would need the post-bundle source transforms this path skips.
938
941
  if (
939
942
  isCommonJsModuleSource(source, file) ||
940
943
  /\brequire\s*\(|\bmodule\.exports\b/.test(source) ||
941
944
  source.includes('use cache')
942
945
  ) {
943
- return undefined;
946
+ return undefined
944
947
  }
945
- const imports: string[] = [];
948
+ const imports: string[] = []
946
949
  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);
950
+ const specifier = found.value
951
+ if (!specifier.startsWith('.')) return undefined
952
+ const resolved = resolveEsmDistImport(path.dirname(file), specifier)
953
+ if (!resolved) return undefined
954
+ imports.push(resolved)
952
955
  }
953
- return imports;
956
+ return imports
954
957
  }
955
958
 
956
959
  async function collectBrowserReadyEsmClosure(entry: string) {
957
- const closure = new Set<string>();
958
- const queue = [entry];
960
+ const closure = new Set<string>()
961
+ const queue = [entry]
959
962
  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);
963
+ const file = queue.pop()!
964
+ if (closure.has(file)) continue
965
+ if (closure.size >= MAX_ESM_DIST_FILES) return undefined
966
+ closure.add(file)
967
+ const imports = await browserReadyEsmImports(file)
968
+ if (!imports) return undefined
969
+ queue.push(...imports)
967
970
  }
968
971
  // A single-file "closure" is a re-export shim or a stub; bundling it is
969
972
  // already free, and the marker directory would cost more than it saves.
970
- return closure.size > 1 ? closure : undefined;
973
+ return closure.size > 1 ? closure : undefined
971
974
  }
972
975
 
973
976
  /** Relative resolution limited to what a browser would do: exact file first. */
974
977
  function resolveEsmDistImport(fromDir: string, specifier: string) {
975
- const base = path.resolve(fromDir, specifier);
978
+ const base = path.resolve(fromDir, specifier)
976
979
  for (const candidate of [base, `${base}.js`, `${base}.mjs`, path.join(base, 'index.js')]) {
977
980
  if (ESM_DIST_EXTENSIONS.has(path.extname(candidate)) && cachedExistsSync(candidate)) {
978
- return candidate;
981
+ return candidate
979
982
  }
980
983
  }
981
- return undefined;
984
+ return undefined
982
985
  }
983
986
 
984
987
  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[] = [];
988
+ const segmentLists = files.map(file => path.dirname(file).split(path.sep))
989
+ const shortest = Math.min(...segmentLists.map(segments => segments.length))
990
+ const common: string[] = []
988
991
  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);
992
+ const segment = segmentLists[0]![index]!
993
+ if (segmentLists.some(segments => segments[index] !== segment)) break
994
+ common.push(segment)
992
995
  }
993
- return common.join(path.sep) || path.sep;
996
+ return common.join(path.sep) || path.sep
994
997
  }
995
998
 
996
999
  // --------------------------------------------------------------------------
997
1000
  // facades
998
1001
  // --------------------------------------------------------------------------
999
1002
 
1000
- const REEXPORT_CALL = /\bmodule\.exports\s*=\s*require\(\s*(['"])([^'"]+)\1\s*\)/;
1003
+ const REEXPORT_CALL = /\bmodule\.exports\s*=\s*require\(\s*(['"])([^'"]+)\1\s*\)/
1001
1004
 
1002
1005
  /**
1003
1006
  * Cheap pre-filter: can this CommonJS entry plausibly carry named exports? A
@@ -1007,19 +1010,19 @@ const REEXPORT_CALL = /\bmodule\.exports\s*=\s*require\(\s*(['"])([^'"]+)\1\s*\)
1007
1010
  * level, which is where most packages' real export list lives.
1008
1011
  */
1009
1012
  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;
1013
+ const source = await readFile(entry, 'utf8').catch(() => undefined)
1014
+ if (source === undefined) return false
1015
+ if (commonJsExportNames(source).length > 0) return true
1016
+ const reexport = REEXPORT_CALL.exec(source)
1017
+ const target = reexport?.[2]
1018
+ if (!target?.startsWith('.')) return false
1016
1019
  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;
1020
+ const file = path.resolve(path.dirname(entry), candidate)
1021
+ if (!existsSync(file) || !file.endsWith('.js')) continue
1022
+ const inner = await readFile(file, 'utf8').catch(() => undefined)
1023
+ if (inner && commonJsExportNames(inner).length > 0) return true
1021
1024
  }
1022
- return false;
1025
+ return false
1023
1026
  }
1024
1027
 
1025
1028
  /**
@@ -1029,28 +1032,32 @@ export async function cjsEntryMayHaveNamedExports(entry: string) {
1029
1032
  * full cache wipe recovers from. A new key sidesteps both: the old facade stays on disk for whoever still
1030
1033
  * imports it, and every new compile names the new one.
1031
1034
  */
1032
- const FACADE_GENERATOR = 'v2';
1035
+ const FACADE_GENERATOR = 'v2'
1033
1036
 
1034
1037
  /** Marker written beside a bundle whose export set turned out to be default-only. */
1035
- const NO_FACADE_SUFFIX = `.no-facade-${FACADE_GENERATOR}`;
1038
+ const NO_FACADE_SUFFIX = `.no-facade-${FACADE_GENERATOR}`
1036
1039
 
1037
1040
  /** The facade this generator writes for `bundleFile`. */
1038
1041
  export function cjsFacadePath(bundleFile: string) {
1039
- return `${bundleFile.replace(/\.mjs$/, '')}.facade-${FACADE_GENERATOR}.mjs`;
1042
+ return `${bundleFile.replace(/\.mjs$/, '')}.facade-${FACADE_GENERATOR}.mjs`
1040
1043
  }
1041
1044
 
1042
1045
  /** Where this generator records "no named exports" for `bundleFile`. */
1043
1046
  export function cjsNoFacadeMarker(bundleFile: string) {
1044
- return `${bundleFile}${NO_FACADE_SUFFIX}`;
1047
+ return `${bundleFile}${NO_FACADE_SUFFIX}`
1045
1048
  }
1046
1049
 
1047
- const facades = new Map<string, Promise<string | undefined>>();
1050
+ const facades = new Map<string, Promise<string | undefined>>()
1048
1051
 
1049
1052
  // eslint-disable-next-line turbo/no-undeclared-env-vars
1050
- const heavyProfFile = process.env.PNEXT_HEAVY_PROF;
1053
+ const heavyProfFile = process.env.PNEXT_HEAVY_PROF
1051
1054
  export function heavyProfRow(row: Record<string, unknown>) {
1052
- if (!heavyProfFile) return;
1053
- try { appendFileSync(heavyProfFile, `${JSON.stringify(row)}\n`); } catch { /* prof only */ }
1055
+ if (!heavyProfFile) return
1056
+ try {
1057
+ appendFileSync(heavyProfFile, `${JSON.stringify(row)}\n`)
1058
+ } catch {
1059
+ /* prof only */
1060
+ }
1054
1061
  }
1055
1062
 
1056
1063
  /**
@@ -1059,48 +1066,55 @@ export function heavyProfRow(row: Record<string, unknown>) {
1059
1066
  * inlines instead.
1060
1067
  */
1061
1068
  export function cjsNamedExportFacade(bundleFile: string, importHref: string) {
1062
- let facade = facades.get(bundleFile);
1069
+ let facade = facades.get(bundleFile)
1063
1070
  if (!facade) {
1064
- facade = writeCjsNamedExportFacade(bundleFile, importHref).catch(() => undefined);
1065
- facades.set(bundleFile, facade);
1071
+ facade = writeCjsNamedExportFacade(bundleFile, importHref).catch(() => undefined)
1072
+ facades.set(bundleFile, facade)
1066
1073
  }
1067
- return facade;
1074
+ return facade
1068
1075
  }
1069
1076
 
1070
1077
  async function writeCjsNamedExportFacade(bundleFile: string, importHref: string) {
1071
- const file = cjsFacadePath(bundleFile);
1078
+ const file = cjsFacadePath(bundleFile)
1072
1079
  // The facade's identity follows its bundle's, so an importer of one canonical-
1073
1080
  // izes the same way an importer of the other does.
1074
- const bundleId = vendorContentIds.get(bundleFile);
1075
- if (bundleId) vendorContentIds.set(file, `${bundleId}:facade`);
1081
+ const bundleId = vendorContentIds.get(bundleFile)
1082
+ if (bundleId) vendorContentIds.set(file, `${bundleId}:facade`)
1076
1083
  // Both outcomes persist: the facade is content-keyed with the bundle it wraps,
1077
1084
  // 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;
1085
+ if (existsSync(file)) return file
1086
+ if (existsSync(cjsNoFacadeMarker(bundleFile))) return undefined
1080
1087
 
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();
1088
+ const t0 = performance.now()
1089
+ const code = await readFile(bundleFile, 'utf8').catch(() => undefined)
1090
+ if (code === undefined) return undefined
1091
+ const tRead = performance.now()
1085
1092
  // The exec pass is REQUIRED, not a supplement: a statically-scanned set is routinely short, and an
1086
1093
  // incomplete facade is worse than inlining - it breaks an importer's named binding at build time.
1087
1094
  // A bundle that cannot be executed here simply gets no facade.
1088
- const executed = await executedExportNames(importHref);
1089
- const tExec = performance.now();
1095
+ const executed = await executedExportNames(importHref)
1096
+ const tExec = performance.now()
1090
1097
  const names =
1091
1098
  executed &&
1092
1099
  [...new Set([...commonJsExportNames(code), ...executed])]
1093
1100
  .filter(name => name !== 'default' && name !== '__esModule' && isIdentifier(name))
1094
- .sort();
1101
+ .sort()
1095
1102
  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;
1103
+ heavyProfRow({
1104
+ k: 'facade',
1105
+ f: path.basename(bundleFile),
1106
+ readMs: tRead - t0,
1107
+ execMs: tExec - tRead,
1108
+ verifyMs: 0,
1109
+ out: 'no-names',
1110
+ })
1111
+ await writeFileAtomic(cjsNoFacadeMarker(bundleFile), '')
1112
+ return undefined
1099
1113
  }
1100
1114
 
1101
- const binding = '__pnext_cjs';
1102
- const namespace = '__pnext_cjs_ns';
1103
- const holder = '__pnext_cjs_names';
1115
+ const binding = '__pnext_cjs'
1116
+ const namespace = '__pnext_cjs_ns'
1117
+ const holder = '__pnext_cjs_names'
1104
1118
  // A CommonJS entry marked `__esModule` but carrying no `default` (e.g.
1105
1119
  // bind-event-listener) leaves the default binding undefined, so reading names
1106
1120
  // off it threw at facade eval. Its names live on the bundle's own exports
@@ -1110,15 +1124,22 @@ async function writeCjsNamedExportFacade(bundleFile: string, importHref: string)
1110
1124
  `export default ${binding};\n` +
1111
1125
  `const ${holder} = ${binding} ?? ${namespace};\n` +
1112
1126
  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' });
1127
+ '\n'
1128
+ const ok = await facadeRebundles(file, source)
1129
+ heavyProfRow({
1130
+ k: 'facade',
1131
+ f: path.basename(bundleFile),
1132
+ readMs: tRead - t0,
1133
+ execMs: tExec - tRead,
1134
+ verifyMs: performance.now() - tExec,
1135
+ out: ok ? 'facade' : 'verify-fail',
1136
+ })
1116
1137
  if (!ok) {
1117
- await writeFileAtomic(cjsNoFacadeMarker(bundleFile), '');
1118
- return undefined;
1138
+ await writeFileAtomic(cjsNoFacadeMarker(bundleFile), '')
1139
+ return undefined
1119
1140
  }
1120
- await writeFileAtomic(file, source);
1121
- return file;
1141
+ await writeFileAtomic(file, source)
1142
+ return file
1122
1143
  }
1123
1144
 
1124
1145
  /**
@@ -1128,12 +1149,12 @@ async function writeCjsNamedExportFacade(bundleFile: string, importHref: string)
1128
1149
  */
1129
1150
  async function executedExportNames(importHref: string) {
1130
1151
  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);
1152
+ const module_ = (await import(importHref)) as Record<string, unknown>
1153
+ const source = (module_.default as Record<string, unknown> | undefined) ?? module_
1154
+ if (!source || (typeof source !== 'object' && typeof source !== 'function')) return undefined
1155
+ return Object.keys(source)
1135
1156
  } catch {
1136
- return undefined;
1157
+ return undefined
1137
1158
  }
1138
1159
  }
1139
1160
 
@@ -1152,9 +1173,9 @@ async function facadeRebundles(file: string, source: string) {
1152
1173
  target: 'es2022',
1153
1174
  packages: 'external',
1154
1175
  logLevel: 'silent',
1155
- });
1156
- return true;
1176
+ })
1177
+ return true
1157
1178
  } catch {
1158
- return false;
1179
+ return false
1159
1180
  }
1160
1181
  }