@wular/pnext 0.0.3 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (392) hide show
  1. package/README.md +11 -11
  2. package/bin/pnext +1 -1
  3. package/config/lint/base.js +7 -7
  4. package/config/ts/base.json +2 -4
  5. package/config/ts/react.json +2 -6
  6. package/package.json +23 -2
  7. package/reference/compat.md +1 -1
  8. package/reference/config.md +2 -2
  9. package/reference/css.md +13 -9
  10. package/reference/dev.md +1 -1
  11. package/reference/metadata.md +7 -7
  12. package/reference/navigation.md +35 -28
  13. package/reference/performance.md +70 -70
  14. package/reference/rendering.md +17 -17
  15. package/reference/routing.md +17 -17
  16. package/reference/typegen.md +12 -8
  17. package/src/api/cache.ts +36 -37
  18. package/src/api/client-cache.ts +2 -2
  19. package/src/api/client-navigation.ts +113 -109
  20. package/src/api/dynamic.tsx +58 -55
  21. package/src/api/link.tsx +49 -52
  22. package/src/api/navigation.ts +59 -58
  23. package/src/api/server.ts +152 -144
  24. package/src/api/suspense.ts +4 -4
  25. package/src/cli/adapters/vercel-warm.ts +126 -121
  26. package/src/cli/adapters/vercel.ts +437 -443
  27. package/src/cli/analyze.ts +333 -144
  28. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  29. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  30. package/src/cli/build.ts +976 -978
  31. package/src/cli/create.ts +71 -63
  32. package/src/cli/dev.ts +116 -116
  33. package/src/cli/index.ts +88 -85
  34. package/src/cli/migrate/package-json.ts +73 -71
  35. package/src/cli/migrate/report.ts +32 -33
  36. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  37. package/src/cli/migrate/scan.ts +46 -46
  38. package/src/cli/migrate/spinner.ts +9 -9
  39. package/src/cli/migrate/tsconfig.ts +35 -35
  40. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  41. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  42. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  43. package/src/cli/start.ts +65 -66
  44. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  45. package/src/client/build.ts +794 -768
  46. package/src/client/chunk-fold.ts +245 -240
  47. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  48. package/src/client/entry.ts +162 -147
  49. package/src/client/prebuilt.ts +231 -223
  50. package/src/client/profile.ts +29 -29
  51. package/src/client/react-compiler.ts +20 -15
  52. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  53. package/src/client/reference-stub.ts +51 -51
  54. package/src/client/reference.ts +19 -19
  55. package/src/{api → client}/router/events.ts +17 -17
  56. package/src/{api → client}/router/history.ts +16 -16
  57. package/src/{api → client}/router/hub.ts +52 -53
  58. package/src/{api/router.ts → client/router/index.ts} +61 -60
  59. package/src/{api → client}/router/policies.ts +24 -24
  60. package/src/{api → client}/router/runtime.ts +1988 -1961
  61. package/src/{api → client}/router/types.ts +93 -98
  62. package/src/compat/actions/client-plugin.ts +42 -43
  63. package/src/compat/actions/client-stub.ts +14 -14
  64. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  65. package/src/compat/actions/config.ts +63 -64
  66. package/src/compat/actions/detect.ts +68 -68
  67. package/src/compat/actions/discovery.ts +105 -105
  68. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  69. package/src/compat/actions/early-submit.ts +1 -1
  70. package/src/compat/actions/endpoint.ts +198 -198
  71. package/src/compat/actions/extensions.ts +811 -0
  72. package/src/compat/actions/flight.ts +23 -23
  73. package/src/compat/actions/form-state.ts +34 -34
  74. package/src/compat/actions/hoist.ts +382 -236
  75. package/src/compat/actions/ids.ts +7 -12
  76. package/src/compat/actions/index.ts +8 -8
  77. package/src/compat/actions/instances.ts +46 -46
  78. package/src/compat/actions/origin.ts +47 -48
  79. package/src/compat/actions/protocol.ts +22 -22
  80. package/src/compat/actions/registry.ts +19 -19
  81. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  82. package/src/compat/actions/rewrite.ts +125 -125
  83. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  84. package/src/compat/actions/serve.ts +132 -136
  85. package/src/compat/actions/server-tag.ts +4 -4
  86. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  87. package/src/compat/actions/unrecognized-error.ts +4 -4
  88. package/src/compat/{index.ts → aliases.ts} +112 -109
  89. package/src/compat/bundler/bun-externals.ts +18 -18
  90. package/src/compat/bundler/cjs-exports.ts +271 -223
  91. package/src/compat/bundler/config.ts +116 -111
  92. package/src/compat/bundler/externals.ts +12 -12
  93. package/src/compat/bundler/import-meta-url.ts +19 -19
  94. package/src/compat/bundler/modularize-imports.ts +36 -33
  95. package/src/compat/bundler/new-url-asset.ts +22 -24
  96. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  97. package/src/compat/bundler/polyfill.ts +28 -28
  98. package/src/compat/bundler/react-compiler.ts +35 -29
  99. package/src/compat/bundler/react-profiler.tsx +11 -11
  100. package/src/compat/bundler/relay-transform.ts +48 -47
  101. package/src/compat/bundler/require-context.ts +119 -113
  102. package/src/compat/bundler/resolve-extensions.ts +19 -19
  103. package/src/compat/bundler/source-cache.ts +25 -25
  104. package/src/compat/bundler/static-imports.ts +7 -7
  105. package/src/compat/bundler/symlink-imports.ts +46 -46
  106. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  107. package/src/compat/bundler/wasm.ts +58 -60
  108. package/src/compat/bundler/webpack-loaders.ts +254 -241
  109. package/src/compat/bundler/worker.ts +101 -104
  110. package/src/compat/cache/build-flags.ts +29 -29
  111. package/src/compat/cache/build-prerender-errors.ts +40 -44
  112. package/src/compat/cache/custom-handler.ts +60 -53
  113. package/src/compat/cache/fetch-patch.ts +240 -240
  114. package/src/compat/cache/handler.ts +27 -31
  115. package/src/compat/cache/modern-handler.ts +148 -126
  116. package/src/compat/cache/resume-data-cache.ts +47 -45
  117. package/src/compat/cache/revalidate.ts +233 -232
  118. package/src/compat/cache/runtime-error.ts +35 -35
  119. package/src/compat/cache/use-cache-transform.ts +442 -417
  120. package/src/compat/cache/use-cache.ts +650 -614
  121. package/src/compat/cache-control.ts +140 -142
  122. package/src/compat/client/base-path.ts +21 -20
  123. package/src/compat/client/css-order.ts +18 -18
  124. package/src/compat/client/errors/bare-boundary.ts +11 -11
  125. package/src/compat/client/errors/control-flow.ts +23 -23
  126. package/src/compat/client/errors/error-boundary.ts +61 -61
  127. package/src/compat/client/errors/global-error.ts +101 -91
  128. package/src/compat/client/errors/install.ts +71 -72
  129. package/src/compat/client/errors/lazy.ts +23 -23
  130. package/src/compat/client/errors/primitive-throw.ts +47 -45
  131. package/src/compat/client/errors/soft-refresh.ts +4 -4
  132. package/src/compat/client/link-status.ts +33 -33
  133. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  134. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  135. package/src/compat/client/navigation-scroll.ts +63 -63
  136. package/src/compat/client/optimistic-routing.ts +93 -88
  137. package/src/compat/client/prefetch-cache.ts +23 -24
  138. package/src/compat/client/route-announcer.ts +35 -35
  139. package/src/compat/client/segment-cache-policy.ts +20 -20
  140. package/src/compat/client/segment-cache.ts +309 -315
  141. package/src/compat/client/segment-prefetch.ts +127 -132
  142. package/src/compat/client/trailing-slash.ts +5 -4
  143. package/src/compat/css/chunking.ts +113 -116
  144. package/src/compat/css/inline-css.ts +21 -21
  145. package/src/compat/css/lightningcss.ts +37 -38
  146. package/src/compat/css/modules.ts +161 -175
  147. package/src/compat/css/nonce.ts +7 -7
  148. package/src/compat/css/sass-plugin.ts +18 -21
  149. package/src/compat/css/sass.ts +150 -152
  150. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  151. package/src/compat/css/styled-jsx.ts +21 -21
  152. package/src/compat/edge-runtime.ts +27 -27
  153. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  154. package/src/compat/export/client.ts +29 -31
  155. package/src/compat/export/{index.ts → emit.ts} +111 -110
  156. package/src/compat/export/standalone.ts +62 -54
  157. package/src/compat/image-optimizer/cache.ts +51 -49
  158. package/src/compat/image-optimizer/detect.ts +52 -52
  159. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  160. package/src/compat/image-optimizer/source.ts +77 -80
  161. package/src/compat/lifecycle/after-scope.ts +26 -26
  162. package/src/compat/lifecycle/after.ts +48 -45
  163. package/src/compat/lifecycle/error-funnel.ts +98 -102
  164. package/src/compat/lifecycle/error-serialize.ts +30 -26
  165. package/src/compat/lifecycle/error-ui.ts +67 -32
  166. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  167. package/src/compat/lifecycle/instrumentation.ts +85 -85
  168. package/src/compat/lifecycle/node-console.ts +11 -11
  169. package/src/compat/lifecycle/testmode.ts +160 -132
  170. package/src/compat/mdx/compile.ts +60 -57
  171. package/src/compat/mdx/plugin.ts +11 -11
  172. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  173. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  174. package/src/compat/metadata.ts +75 -81
  175. package/src/compat/next/cache.ts +68 -69
  176. package/src/compat/next/canonical-url.ts +9 -9
  177. package/src/compat/next/client-cache.ts +19 -19
  178. package/src/compat/next/client-navigation.ts +112 -116
  179. package/src/compat/next/client-only.ts +1 -1
  180. package/src/compat/next/client-script.tsx +99 -93
  181. package/src/compat/next/client-server.ts +10 -10
  182. package/src/compat/next/config-loader.ts +176 -173
  183. package/src/compat/next/config.ts +7 -7
  184. package/src/compat/next/constants.cjs +6 -6
  185. package/src/compat/next/constants.ts +6 -6
  186. package/src/compat/next/custom-server.ts +26 -24
  187. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  188. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  189. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  190. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  191. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  192. package/src/compat/next/dynamic.tsx +21 -18
  193. package/src/compat/next/error.tsx +52 -52
  194. package/src/compat/next/font/cache.ts +74 -74
  195. package/src/compat/next/font/google.ts +2 -2
  196. package/src/compat/next/font/index.ts +1 -1
  197. package/src/compat/next/font/local.ts +3 -3
  198. package/src/compat/next/font/runtime-client.ts +17 -15
  199. package/src/compat/next/font/runtime.ts +443 -408
  200. package/src/compat/next/font/shared.ts +120 -108
  201. package/src/compat/next/form.tsx +63 -63
  202. package/src/compat/next/head.tsx +2 -2
  203. package/src/compat/next/headers.ts +103 -95
  204. package/src/compat/next/image/client.tsx +220 -0
  205. package/src/compat/next/image/config.ts +56 -58
  206. package/src/compat/next/image/optimizer.ts +40 -36
  207. package/src/compat/next/image/patterns.ts +37 -40
  208. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  209. package/src/compat/next/image/shared.ts +65 -57
  210. package/src/compat/next/image/static-metadata.ts +98 -107
  211. package/src/compat/next/image/validate.ts +79 -88
  212. package/src/compat/next/image.tsx +19 -23
  213. package/src/compat/next/index.ts +1 -1
  214. package/src/compat/next/legacy-image.tsx +59 -60
  215. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  216. package/src/compat/next/link.tsx +158 -158
  217. package/src/compat/next/navigation.cjs +12 -3
  218. package/src/compat/next/navigation.ts +48 -50
  219. package/src/compat/next/offline.ts +27 -27
  220. package/src/compat/next/og.ts +121 -124
  221. package/src/compat/next/preferred-region.ts +13 -14
  222. package/src/compat/next/redirects.ts +58 -56
  223. package/src/compat/next/resource-hints.ts +73 -76
  224. package/src/compat/next/rewrites.ts +130 -133
  225. package/src/compat/next/root-params.ts +45 -45
  226. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  227. package/src/compat/next/router.cjs +4 -2
  228. package/src/compat/next/router.ts +58 -61
  229. package/src/compat/next/script.tsx +108 -108
  230. package/src/compat/next/server-only.ts +1 -1
  231. package/src/compat/next/server.ts +19 -19
  232. package/src/compat/next/svgr.ts +18 -17
  233. package/src/compat/next/telemetry.ts +24 -24
  234. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  235. package/src/compat/next/user-agent.ts +53 -49
  236. package/src/compat/next/web-vitals.ts +22 -24
  237. package/src/compat/otel/api.ts +41 -41
  238. package/src/compat/otel/client-trace-metadata.ts +25 -27
  239. package/src/compat/otel/fetch-span.ts +29 -29
  240. package/src/compat/otel/tracer.ts +331 -331
  241. package/src/compat/pages/api.ts +456 -0
  242. package/src/compat/pages/client-plugin.ts +36 -36
  243. package/src/compat/pages/router-state.ts +34 -34
  244. package/src/compat/pages/{index.ts → router.ts} +130 -135
  245. package/src/compat/ppr/io.ts +12 -12
  246. package/src/compat/ppr/missing-root-params.ts +34 -38
  247. package/src/compat/ppr/root-params-scan.ts +66 -66
  248. package/src/compat/ppr/root-params-transform.ts +24 -26
  249. package/src/compat/ppr/root-params.ts +30 -30
  250. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  251. package/src/compat/protocol.ts +71 -70
  252. package/src/compat/react/action-state.ts +47 -48
  253. package/src/compat/react/client-lite.ts +15 -15
  254. package/src/compat/react/client.ts +4 -4
  255. package/src/compat/react/compiler-runtime.ts +11 -11
  256. package/src/compat/react/dom-client.ts +44 -44
  257. package/src/compat/react/dom-react-server.ts +10 -16
  258. package/src/compat/react/dom-server.ts +10 -10
  259. package/src/compat/react/dom.ts +52 -52
  260. package/src/compat/react/hooks-extra.ts +34 -35
  261. package/src/compat/react/parity.ts +64 -61
  262. package/src/compat/react/preact.ts +81 -82
  263. package/src/compat/react/react-server.ts +28 -28
  264. package/src/compat/react/router-shim.ts +1 -1
  265. package/src/compat/react/server-component-use.ts +8 -8
  266. package/src/compat/react/server-inserted-html.ts +30 -31
  267. package/src/compat/react/server.ts +53 -55
  268. package/src/compat/react/use.ts +32 -32
  269. package/src/compat/react/view-transition.ts +20 -20
  270. package/src/compat/register/actions.ts +35 -824
  271. package/src/compat/register/boot.ts +41 -41
  272. package/src/compat/register/build-tier.ts +5 -5
  273. package/src/compat/register/build.ts +74 -70
  274. package/src/compat/register/bundler.ts +161 -156
  275. package/src/compat/register/cache.ts +20 -20
  276. package/src/compat/register/client-errors.ts +3 -3
  277. package/src/compat/register/config.ts +6 -6
  278. package/src/compat/register/css-extras.ts +30 -34
  279. package/src/compat/register/edge-runtime.ts +3 -3
  280. package/src/compat/register/errors.ts +10 -12
  281. package/src/compat/register/export.ts +16 -16
  282. package/src/compat/register/font.ts +11 -11
  283. package/src/compat/register/hooks.ts +2 -2
  284. package/src/compat/register/image.ts +40 -40
  285. package/src/compat/register/index.ts +59 -62
  286. package/src/compat/register/instrumentation-client.ts +10 -10
  287. package/src/compat/register/lifecycle.ts +25 -28
  288. package/src/compat/register/mdx.ts +10 -10
  289. package/src/compat/register/middleware.ts +226 -16
  290. package/src/compat/register/otel.ts +80 -88
  291. package/src/compat/register/pages-api.ts +10 -463
  292. package/src/compat/register/ppr.ts +16 -16
  293. package/src/compat/register/protocol.ts +17 -18
  294. package/src/compat/register/proxy.ts +49 -51
  295. package/src/compat/register/render.ts +79 -76
  296. package/src/compat/register/routing.ts +162 -159
  297. package/src/compat/register/segment.ts +24 -1887
  298. package/src/compat/register/static-image.ts +3 -3
  299. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  300. package/src/compat/register/typed-routes.ts +14 -14
  301. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  302. package/src/compat/register/validation.ts +18 -18
  303. package/src/compat/segment/loading-boundary.ts +43 -45
  304. package/src/compat/segment/page-slot.ts +69 -69
  305. package/src/compat/segment/serve.ts +1884 -0
  306. package/src/compat/segment/tree.ts +113 -112
  307. package/src/compat/segment/vary-key.ts +38 -38
  308. package/src/compat/segment/vary-params.ts +138 -142
  309. package/src/compat/static-params.ts +14 -12
  310. package/src/compat/tsconfig-defaults.ts +87 -91
  311. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  312. package/src/compat/typecheck/worker.ts +15 -12
  313. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  314. package/src/compat/typed-routes/manifest.ts +174 -170
  315. package/src/compat/typed-routes/typegen.ts +127 -110
  316. package/src/compat/validation/errors.ts +16 -19
  317. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  318. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  319. package/src/compat-bootstrap.ts +16 -16
  320. package/src/config.ts +69 -69
  321. package/src/css/build.ts +230 -227
  322. package/src/css/postcss.ts +79 -80
  323. package/src/css/worker.ts +14 -15
  324. package/src/dev/client-actions.ts +10 -10
  325. package/src/dev/client-chunk-store.ts +27 -27
  326. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  327. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  328. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  329. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  330. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  331. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  332. package/src/dev/server.ts +804 -820
  333. package/src/env.ts +46 -43
  334. package/src/extensions.ts +491 -478
  335. package/src/index.ts +8 -8
  336. package/src/internal.ts +20 -23
  337. package/src/{islands → render}/boundary-error.ts +3 -3
  338. package/src/render/hooks.ts +71 -71
  339. package/src/render/island-context.ts +14 -14
  340. package/src/render/metadata.ts +310 -310
  341. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  342. package/src/{ppr.ts → render/ppr.ts} +244 -245
  343. package/src/render/renderer.ts +2076 -2082
  344. package/src/render/resource-hints.ts +16 -17
  345. package/src/render/slots.tsx +224 -235
  346. package/src/{islands → render}/static-children.ts +9 -12
  347. package/src/{islands → render}/static-slots.ts +37 -37
  348. package/src/{cache/context.ts → request/cache.ts} +20 -20
  349. package/src/request/context.ts +107 -107
  350. package/src/{dynamic/source.ts → resolve/dynamic.ts} +152 -143
  351. package/src/resolve/engine.ts +90 -77
  352. package/src/resolve/imports.ts +475 -463
  353. package/src/resolve/scan-facts.ts +442 -186
  354. package/src/resolve/source-text.ts +37 -37
  355. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  356. package/src/routing/forwarded.ts +19 -19
  357. package/src/routing/handler.ts +84 -91
  358. package/src/routing/href.ts +69 -70
  359. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  360. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  361. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  362. package/src/routing/routes.ts +833 -848
  363. package/src/routing/slots.ts +164 -160
  364. package/src/runtime/loader.ts +952 -0
  365. package/src/{dev → runtime}/module-cache.ts +309 -287
  366. package/src/{dev → runtime}/module-generations.ts +9 -9
  367. package/src/{dev → runtime}/module-transform.ts +81 -72
  368. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  369. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  370. package/src/runtime/vendor.ts +425 -404
  371. package/src/styles.d.ts +9 -0
  372. package/src/types.ts +320 -335
  373. package/src/utils/ansi.ts +5 -5
  374. package/src/utils/{source.ts → code.ts} +15 -12
  375. package/src/utils/content-type.ts +3 -3
  376. package/src/utils/decode.ts +2 -2
  377. package/src/utils/dev-profile.ts +13 -13
  378. package/src/utils/error-log.ts +6 -6
  379. package/src/utils/esbuild.ts +18 -18
  380. package/src/utils/fs-cache.ts +13 -13
  381. package/src/utils/fs.ts +57 -49
  382. package/src/utils/html.ts +20 -24
  383. package/src/utils/native-require.ts +8 -8
  384. package/src/utils/serialize.ts +139 -146
  385. package/src/utils/verbose.ts +18 -18
  386. package/src/cli/analyze-print.ts +0 -181
  387. package/src/compat/middleware/manifest.ts +0 -210
  388. package/src/compat/next/image-client.tsx +0 -215
  389. package/src/compat/next/link-usage.ts +0 -29
  390. package/src/css/index.ts +0 -2
  391. package/src/render/index.ts +0 -1
  392. package/src/style-modules.d.ts +0 -9
@@ -1,29 +1,29 @@
1
- import { copyFile, link, mkdir, readFile, rename, rm, symlink, writeFile } from 'node:fs/promises';
2
- import { existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from 'node:fs';
3
- import { builtinModules } from 'node:module';
4
- import path from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
- import { pathToFileHref } from '../../config';
7
- import type { ResolvedConfig } from '../../config';
8
- import { globalCssSources } from '../../css';
9
- import { getImportAliasExtensions } from '../../extensions';
10
- import { importSpecifiers } from '../../resolve/scan-facts';
11
- import { compiledSpecifiersManifestSuffix } from '../../dev/imports';
12
- import { resolveImport, workspacePackageRoots } from '../../resolve/imports';
13
- import { findProxyFile, proxyRoutePatterns, type ProxyModule } from '../../proxy';
14
- import { cacheRoot } from '../named-bin';
15
- import { startWarmChild, type WarmChild } from './vercel-warm';
16
- import { listFiles, toPosixPath, writeText } from '../../utils/fs';
17
- import { createVerboseLogger, type VerboseLogger } from '../../utils/verbose';
18
- import type { BuildManifest, StaticFileMetadata } from '../../types';
1
+ import { copyFile, link, mkdir, readFile, rename, rm, symlink, writeFile } from 'node:fs/promises'
2
+ import { existsSync, readFileSync, readdirSync, realpathSync, rmSync, statSync } from 'node:fs'
3
+ import { builtinModules } from 'node:module'
4
+ import path from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { pathToFileHref } from '../../config'
7
+ import type { ResolvedConfig } from '../../config'
8
+ import { globalCssSources } from '../../css/build'
9
+ import { getImportAliasExtensions } from '../../extensions'
10
+ import { importSpecifiers } from '../../resolve/scan-facts'
11
+ import { compiledSpecifiersManifestSuffix } from '../../runtime/modules'
12
+ import { resolveImport, workspacePackageRoots } from '../../resolve/imports'
13
+ import { findProxyFile, proxyRoutePatterns, type ProxyModule } from '../../routing/proxy'
14
+ import { cacheRoot } from '../boot/named-bin'
15
+ import { startWarmChild, type WarmChild } from './vercel-warm'
16
+ import { listFiles, toPosixPath, writeText } from '../../utils/fs'
17
+ import { createVerboseLogger, type VerboseLogger } from '../../utils/verbose'
18
+ import type { BuildManifest, StaticFileMetadata } from '../../types'
19
19
 
20
20
  // Vercel unpacks a function's `.func` directory here at runtime. Baked
21
21
  // absolute paths in the build output are rewritten to this root.
22
- const RUNTIME_ROOT = '/var/task';
22
+ const RUNTIME_ROOT = '/var/task'
23
23
 
24
24
  // The single catch-all server function: the same `pnext start` pipeline,
25
25
  // running on Vercel's Bun runtime (`"bunVersion": "1.x"` in vercel.json).
26
- const SERVER_FUNCTION = '_pnext';
26
+ const SERVER_FUNCTION = '_pnext'
27
27
 
28
28
  // The platform the function actually runs on, declared once: `architecture`
29
29
  // goes into .vc-config.json and the same target selects the native packages
@@ -34,7 +34,7 @@ const FUNCTION_PLATFORM = {
34
34
  os: 'linux',
35
35
  cpu: 'x64',
36
36
  libc: 'glibc',
37
- } as const;
37
+ } as const
38
38
 
39
39
  // Directories that only ever hold build-tool output, pruned at *any* depth. Depth matters: pruning
40
40
  // only a package's top level ships every nested one. node_modules and .git are here for the same
@@ -48,7 +48,7 @@ const prunedDirNames = [
48
48
  '.cache',
49
49
  '.vercel',
50
50
  '.tmp',
51
- ];
51
+ ]
52
52
 
53
53
  // Files nothing reads at request time. Sourcemaps only decorate stack traces,
54
54
  // and Bun strips types rather than resolving declarations — neither can change
@@ -63,27 +63,27 @@ const droppedFileSuffixes = [
63
63
  // The compile-time specifier sidecars (see traceNodeModulesClosure below):
64
64
  // build-time metadata the trace consumes, never read at request time.
65
65
  compiledSpecifiersManifestSuffix,
66
- ];
66
+ ]
67
67
 
68
68
  /** What the function tree ships, resolved once from config. */
69
69
  interface PackRules {
70
- prunedDirs: Set<string>;
71
- droppedSuffixes: string[];
70
+ prunedDirs: Set<string>
71
+ droppedSuffixes: string[]
72
72
  /** Absolute sources exempt from the prune list — the app's own build output. */
73
- keepPaths: Set<string>;
73
+ keepPaths: Set<string>
74
74
  }
75
75
 
76
76
  // Test seam: the packing suite builds one app both ways and diffs the trees,
77
77
  // so it needs the unpruned closure on demand. Never off for a real deployment.
78
- let packPruningEnabled = true;
78
+ let packPruningEnabled = true
79
79
 
80
80
  /** @internal Test-only. Returns a restore function. */
81
81
  export function setPackPruningEnabled(enabled: boolean) {
82
- const previous = packPruningEnabled;
83
- packPruningEnabled = enabled;
82
+ const previous = packPruningEnabled
83
+ packPruningEnabled = enabled
84
84
  return () => {
85
- packPruningEnabled = previous;
86
- };
85
+ packPruningEnabled = previous
86
+ }
87
87
  }
88
88
 
89
89
  function packRules(config: ResolvedConfig): PackRules {
@@ -94,11 +94,11 @@ function packRules(config: ResolvedConfig): PackRules {
94
94
  prunedDirs: new Set(['node_modules', '.git']),
95
95
  droppedSuffixes: [],
96
96
  keepPaths: new Set([path.resolve(config.outPath)]),
97
- };
97
+ }
98
98
  }
99
- const keep = new Set(config.adapter?.keep ?? []);
100
- const extra = config.adapter?.exclude ?? [];
101
- const isSuffix = (entry: string) => entry.startsWith('.') && !prunedDirNames.includes(entry);
99
+ const keep = new Set(config.adapter?.keep ?? [])
100
+ const extra = config.adapter?.exclude ?? []
101
+ const isSuffix = (entry: string) => entry.startsWith('.') && !prunedDirNames.includes(entry)
102
102
  return {
103
103
  prunedDirs: new Set(
104
104
  [...prunedDirNames, ...extra.filter(entry => !isSuffix(entry))].filter(
@@ -111,24 +111,24 @@ function packRules(config: ResolvedConfig): PackRules {
111
111
  // The app's outDir shares its name with the prune list; it holds the
112
112
  // compiled module cache the function serves from and must survive.
113
113
  keepPaths: new Set([path.resolve(config.outPath)]),
114
- };
114
+ }
115
115
  }
116
116
 
117
117
  function shipsDir(pack: PackRules, name: string, source: string) {
118
- return !pack.prunedDirs.has(name) || pack.keepPaths.has(path.resolve(source));
118
+ return !pack.prunedDirs.has(name) || pack.keepPaths.has(path.resolve(source))
119
119
  }
120
120
 
121
121
  function shipsFile(pack: PackRules, name: string) {
122
- return !pack.droppedSuffixes.some(suffix => name.endsWith(suffix));
122
+ return !pack.droppedSuffixes.some(suffix => name.endsWith(suffix))
123
123
  }
124
124
 
125
125
  interface VercelConfig {
126
- version: 3;
126
+ version: 3
127
127
  routes?: (
128
128
  | { src: string; dest?: string; headers?: Record<string, string>; continue?: boolean }
129
129
  | { handle: 'filesystem' }
130
- )[];
131
- overrides?: Record<string, { path?: string; contentType?: string }>;
130
+ )[]
131
+ overrides?: Record<string, { path?: string; contentType?: string }>
132
132
  }
133
133
 
134
134
  export async function writeVercelOutput(
@@ -136,13 +136,13 @@ export async function writeVercelOutput(
136
136
  manifest: BuildManifest,
137
137
  options: { verbose?: boolean; warm?: WarmChild } = {},
138
138
  ) {
139
- const log = createVerboseLogger(options.verbose ?? false, 'vercel');
140
- const outputPath = path.join(config.root, '.vercel', 'output');
141
- const staticFiles = manifest.staticFiles ?? {};
139
+ const log = createVerboseLogger(options.verbose ?? false, 'vercel')
140
+ const outputPath = path.join(config.root, '.vercel', 'output')
141
+ const staticFiles = manifest.staticFiles ?? {}
142
142
  // Warming runs in its own process (see ./vercel-warm) and normally started
143
143
  // with the build; a caller that skipped that only loses the overlap.
144
- const warm = options.warm ?? startWarmChild(config);
145
- await log.step('prepare output directory', () => emptyDir(outputPath));
144
+ const warm = options.warm ?? startWarmChild(config)
145
+ await log.step('prepare output directory', () => emptyDir(outputPath))
146
146
 
147
147
  await log.step('copy static files', () =>
148
148
  copyStaticFiles(
@@ -150,28 +150,28 @@ export async function writeVercelOutput(
150
150
  path.join(outputPath, 'static'),
151
151
  relative => !staticFiles[relative] || canServeStaticOnVercel(staticFiles[relative]),
152
152
  ),
153
- );
154
- const overrides = await staticOverrides(path.join(config.outPath, 'public'), staticFiles);
153
+ )
154
+ const overrides = await staticOverrides(path.join(config.outPath, 'public'), staticFiles)
155
155
 
156
156
  // The compiled set is already final once the child reaches its handler
157
157
  // phase, so `onCompiled` resolves this ahead of full completion and
158
158
  // writeServerFunction starts tracing while the child's handler imports
159
159
  // still run. `finish` itself always fires `onCompiled` before it resolves,
160
160
  // so this promise never hangs on it.
161
- let resolveWarmedModules: (modules: string[]) => void;
161
+ let resolveWarmedModules: (modules: string[]) => void
162
162
  const warmedModulesEarly = new Promise<string[]>(resolve => {
163
- resolveWarmedModules = resolve;
164
- });
165
- const warmSettled = warm.finish(log, modules => resolveWarmedModules(modules));
163
+ resolveWarmedModules = resolve
164
+ })
165
+ const warmSettled = warm.finish(log, modules => resolveWarmedModules(modules))
166
166
  // Nothing awaits it until writeServerFunction's copy step; park the
167
167
  // rejection so it never surfaces as unhandled in between (it never actually
168
168
  // rejects — warmWithRestarts catches everything itself — but the gap
169
169
  // between here and that await is otherwise unguarded).
170
- warmSettled.catch(() => undefined);
171
- const warmedModules = await log.step('warm module cache (compiled)', () => warmedModulesEarly);
170
+ warmSettled.catch(() => undefined)
171
+ const warmedModules = await log.step('warm module cache (compiled)', () => warmedModulesEarly)
172
172
 
173
- const functionPath = path.join(outputPath, 'functions', `${SERVER_FUNCTION}.func`);
174
- await writeServerFunction(config, manifest, functionPath, warmedModules, warmSettled, log);
173
+ const functionPath = path.join(outputPath, 'functions', `${SERVER_FUNCTION}.func`)
174
+ await writeServerFunction(config, manifest, functionPath, warmedModules, warmSettled, log)
175
175
 
176
176
  const routes: NonNullable<VercelConfig['routes']> = [
177
177
  // Chunk and font filenames are content-hashed; serve them immutable.
@@ -185,17 +185,17 @@ export async function writeVercelOutput(
185
185
  ...(await proxyRoutes(config)),
186
186
  { handle: 'filesystem' },
187
187
  { src: '^/.*$', dest: `/${SERVER_FUNCTION}` },
188
- ];
188
+ ]
189
189
 
190
190
  const vercelConfig: VercelConfig = {
191
191
  version: 3,
192
192
  routes,
193
193
  ...(Object.keys(overrides).length > 0 ? { overrides } : {}),
194
- };
194
+ }
195
195
  await writeText(
196
196
  path.join(outputPath, 'config.json'),
197
197
  `${JSON.stringify(vercelConfig, null, 2)}\n`,
198
- );
198
+ )
199
199
  }
200
200
 
201
201
  // Unlinking the previous run's output (tens of thousands of hardlinks) must not block the build:
@@ -203,32 +203,32 @@ export async function writeVercelOutput(
203
203
  // unlinking. Nothing downstream depends on the bytes being gone, and anything an interrupted run
204
204
  // leaves behind is swept on the next build.
205
205
  async function emptyDir(dir: string) {
206
- const parent = path.dirname(dir);
207
- await mkdir(parent, { recursive: true });
208
- const staleMarker = `${path.basename(dir)}.stale-`;
206
+ const parent = path.dirname(dir)
207
+ await mkdir(parent, { recursive: true })
208
+ const staleMarker = `${path.basename(dir)}.stale-`
209
209
  if (existsSync(dir)) {
210
210
  await rename(
211
211
  dir,
212
212
  path.join(parent, `${staleMarker}${process.pid.toString(36)}-${Date.now().toString(36)}`),
213
- );
213
+ )
214
214
  }
215
215
  const stale = readdirSync(parent)
216
216
  .filter(entry => entry.startsWith(staleMarker))
217
- .map(entry => path.join(parent, entry));
218
- await mkdir(dir, { recursive: true });
217
+ .map(entry => path.join(parent, entry))
218
+ await mkdir(dir, { recursive: true })
219
219
  if (stale.length > 0) {
220
- Bun.spawn(['rm', '-rf', ...stale], { stdout: 'ignore', stderr: 'ignore' });
220
+ Bun.spawn(['rm', '-rf', ...stale], { stdout: 'ignore', stderr: 'ignore' })
221
221
  }
222
222
  }
223
223
 
224
224
  async function proxyRoutes(config: ResolvedConfig) {
225
- const proxyFile = findProxyFile(config);
226
- if (!proxyFile) return [];
227
- const proxyModule = (await import(pathToFileHref(proxyFile))) as ProxyModule;
225
+ const proxyFile = findProxyFile(config)
226
+ if (!proxyFile) return []
227
+ const proxyModule = (await import(pathToFileHref(proxyFile))) as ProxyModule
228
228
  return proxyRoutePatterns(proxyModule.config).map(src => ({
229
229
  src,
230
230
  dest: `/${SERVER_FUNCTION}`,
231
- }));
231
+ }))
232
232
  }
233
233
 
234
234
  async function writeServerFunction(
@@ -243,12 +243,12 @@ async function writeServerFunction(
243
243
  warmSettled: Promise<unknown>,
244
244
  log: VerboseLogger,
245
245
  ) {
246
- const workspaceRoot = config.workspaceRoot;
247
- await mkdir(functionPath, { recursive: true });
246
+ const workspaceRoot = config.workspaceRoot
247
+ await mkdir(functionPath, { recursive: true })
248
248
 
249
- const pack = packRules(config);
250
- const framework = await resolveFrameworkPackage(workspaceRoot);
251
- const { root: pnextRoot, inWorkspace: pnextInWorkspace, targetRel: pnextTargetRel } = framework;
249
+ const pack = packRules(config)
250
+ const framework = await resolveFrameworkPackage(workspaceRoot)
251
+ const { root: pnextRoot, inWorkspace: pnextInWorkspace, targetRel: pnextTargetRel } = framework
252
252
  const closure = await traceNodeModulesClosure(
253
253
  config,
254
254
  manifest,
@@ -257,54 +257,54 @@ async function writeServerFunction(
257
257
  warmedModules,
258
258
  pack,
259
259
  log,
260
- );
260
+ )
261
261
 
262
262
  const replicaPathFor = (file: string) => {
263
263
  if (isInsideDir(pnextRoot, file)) {
264
- return path.join(functionPath, pnextTargetRel, path.relative(pnextRoot, file));
264
+ return path.join(functionPath, pnextTargetRel, path.relative(pnextRoot, file))
265
265
  }
266
266
  if (isInsideDir(workspaceRoot, file)) {
267
- return path.join(functionPath, path.relative(workspaceRoot, file));
267
+ return path.join(functionPath, path.relative(workspaceRoot, file))
268
268
  }
269
- return undefined;
270
- };
269
+ return undefined
270
+ }
271
271
 
272
272
  // Replicate the workspace layout the build ran in: app source (the renderer reads convention files
273
273
  // and global css imports from it at request time), the build output, and the workspace packages the
274
274
  // server can actually reach. Unrelated workspace trees never ship.
275
- const appRel = path.relative(workspaceRoot, config.root);
276
- const packageRoots = workspacePackageRoots(workspaceRoot).map(root => path.resolve(root));
277
- const neededRoots = new Set<string>();
275
+ const appRel = path.relative(workspaceRoot, config.root)
276
+ const packageRoots = workspacePackageRoots(workspaceRoot).map(root => path.resolve(root))
277
+ const neededRoots = new Set<string>()
278
278
  for (const file of closure.tracedFiles) {
279
- const root = packageRoots.find(candidate => isInsideDir(candidate, file));
280
- if (root) neededRoots.add(root);
279
+ const root = packageRoots.find(candidate => isInsideDir(candidate, file))
280
+ if (root) neededRoots.add(root)
281
281
  }
282
282
  for (const relative of closure.workspacePackages) {
283
- neededRoots.add(path.resolve(workspaceRoot, relative));
283
+ neededRoots.add(path.resolve(workspaceRoot, relative))
284
284
  }
285
285
  // The renderer re-resolves the root layout's global css imports from source
286
286
  // at request time; the packages holding those css files must be present.
287
287
  for (const file of globalCssSources(config)) {
288
- const root = packageRoots.find(candidate => isInsideDir(candidate, file));
289
- if (root) neededRoots.add(root);
288
+ const root = packageRoots.find(candidate => isInsideDir(candidate, file))
289
+ if (root) neededRoots.add(root)
290
290
  }
291
291
  // Tracing sees what the *server* loaded, which is not everything the shipped source can still ask
292
292
  // for: the app's own sources travel with the function and a compat compile at request time resolves
293
293
  // their imports for real. A workspace package one of them names but no traced module reached would
294
294
  // not be here, so close over the declared workspace dependencies too.
295
- addWorkspaceDependencies(neededRoots, packageRoots, [path.resolve(config.root), ...neededRoots]);
296
- neededRoots.delete(path.resolve(config.root));
297
- neededRoots.delete(pnextRoot);
295
+ addWorkspaceDependencies(neededRoots, packageRoots, [path.resolve(config.root), ...neededRoots])
296
+ neededRoots.delete(path.resolve(config.root))
297
+ neededRoots.delete(pnextRoot)
298
298
 
299
299
  // The trace above never needed the handler phase, but the copy below ships
300
300
  // config.outPath verbatim — handler imports can still be writing vendor
301
301
  // bundles into it. Most of this wait is already gone by the time the trace
302
302
  // and the roots above finish; whatever remains is genuine handler-phase work.
303
- await log.step('await handler warm-up', () => warmSettled);
303
+ await log.step('await handler warm-up', () => warmSettled)
304
304
 
305
305
  // Every tree lands in its own subdirectory and the copies are io-bound, so
306
306
  // the whole replica goes out as one concurrent step instead of tree by tree.
307
- const roots = neededRoots.size + 2;
307
+ const roots = neededRoots.size + 2
308
308
  await log.step(`copy function tree (${closure.packageCount} packages, ${roots} roots)`, () =>
309
309
  Promise.all([
310
310
  closure.copy(),
@@ -330,7 +330,7 @@ async function writeServerFunction(
330
330
  .filter(file => existsSync(file))
331
331
  .map(file => copyFile(file, path.join(functionPath, path.basename(file)))),
332
332
  ]),
333
- );
333
+ )
334
334
 
335
335
  // Baked absolute paths (compiled cache imports, manifest file paths) point
336
336
  // at the build machine's workspace; rewrite them to the runtime root.
@@ -341,7 +341,7 @@ async function writeServerFunction(
341
341
  : [[pnextRoot, toPosixPath(path.join(RUNTIME_ROOT, pnextTargetRel))] as const]),
342
342
  [workspaceRoot, RUNTIME_ROOT] as const,
343
343
  ]),
344
- );
344
+ )
345
345
 
346
346
  // Bun's runtime onResolve plugins never see bare specifiers, so the compat aliases the dev pipeline
347
347
  // applies during compilation cannot be applied at runtime for raw-loaded sources (proxy, handlers).
@@ -349,9 +349,9 @@ async function writeServerFunction(
349
349
  // every shipped tsconfig.
350
350
  await log.step('inject compat tsconfig paths', () =>
351
351
  injectCompatPaths(config, functionPath, replicaPathFor),
352
- );
352
+ )
353
353
 
354
- const startEntry = toPosixPath(path.join(pnextTargetRel, 'src/cli/start.ts'));
354
+ const startEntry = toPosixPath(path.join(pnextTargetRel, 'src/cli/start.ts'))
355
355
  await writeText(
356
356
  path.join(functionPath, 'index.mjs'),
357
357
  `import path from 'node:path';
@@ -368,12 +368,12 @@ async function handler(request) {
368
368
  // Callable for the Node-style launcher, \`fetch\` for the Bun runtime.
369
369
  export default Object.assign(handler, { fetch: handler });
370
370
  `,
371
- );
371
+ )
372
372
 
373
373
  const maxDuration = manifest.routes.reduce<number | undefined>(
374
374
  (max, route) => (route.maxDuration ? Math.max(max ?? 0, route.maxDuration) : max),
375
375
  undefined,
376
- );
376
+ )
377
377
  await writeText(
378
378
  path.join(functionPath, '.vc-config.json'),
379
379
  `${JSON.stringify(
@@ -393,7 +393,7 @@ export default Object.assign(handler, { fetch: handler });
393
393
  null,
394
394
  2,
395
395
  )}\n`,
396
- );
396
+ )
397
397
  }
398
398
 
399
399
  /**
@@ -402,47 +402,47 @@ export default Object.assign(handler, { fetch: handler });
402
402
  * the usual spelling, but a pinned version resolved through the workspace counts the same.
403
403
  */
404
404
  function addWorkspaceDependencies(roots: Set<string>, packageRoots: string[], from: string[]) {
405
- const byName = new Map<string, string>();
405
+ const byName = new Map<string, string>()
406
406
  for (const root of packageRoots) {
407
- const name = packageManifest(root)?.name;
408
- if (name && !byName.has(name)) byName.set(name, root);
407
+ const name = packageManifest(root)?.name
408
+ if (name && !byName.has(name)) byName.set(name, root)
409
409
  }
410
- const queue = [...from];
411
- const seen = new Set(queue);
410
+ const queue = [...from]
411
+ const seen = new Set(queue)
412
412
  while (queue.length > 0) {
413
- const manifest = packageManifest(queue.pop()!);
414
- if (!manifest) continue;
413
+ const manifest = packageManifest(queue.pop()!)
414
+ if (!manifest) continue
415
415
  for (const field of ['dependencies', 'optionalDependencies'] as const) {
416
416
  for (const name of Object.keys(manifest[field] ?? {})) {
417
- const root = byName.get(name);
418
- if (!root || seen.has(root)) continue;
419
- seen.add(root);
420
- roots.add(root);
421
- queue.push(root);
417
+ const root = byName.get(name)
418
+ if (!root || seen.has(root)) continue
419
+ seen.add(root)
420
+ roots.add(root)
421
+ queue.push(root)
422
422
  }
423
423
  }
424
424
  }
425
425
  }
426
426
 
427
427
  interface PackageManifest {
428
- name?: string;
429
- dependencies?: Record<string, string>;
430
- optionalDependencies?: Record<string, string>;
428
+ name?: string
429
+ dependencies?: Record<string, string>
430
+ optionalDependencies?: Record<string, string>
431
431
  }
432
432
 
433
- const manifests = new Map<string, PackageManifest | undefined>();
433
+ const manifests = new Map<string, PackageManifest | undefined>()
434
434
 
435
435
  function packageManifest(root: string): PackageManifest | undefined {
436
- const cached = manifests.get(root);
437
- if (cached !== undefined || manifests.has(root)) return cached;
438
- let parsed: PackageManifest | undefined;
436
+ const cached = manifests.get(root)
437
+ if (cached !== undefined || manifests.has(root)) return cached
438
+ let parsed: PackageManifest | undefined
439
439
  try {
440
- parsed = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8')) as PackageManifest;
440
+ parsed = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8')) as PackageManifest
441
441
  } catch {
442
- parsed = undefined; // not a package, or unreadable
442
+ parsed = undefined // not a package, or unreadable
443
443
  }
444
- manifests.set(root, parsed);
445
- return parsed;
444
+ manifests.set(root, parsed)
445
+ return parsed
446
446
  }
447
447
 
448
448
  // Clones a tree with hardlinks instead of byte copies - the output is packaging-only, so sharing
@@ -456,54 +456,54 @@ async function copyTree(
456
456
  excludeNames: string[] = [],
457
457
  visited = new Set<string>([realDir(from) ?? path.resolve(from)]),
458
458
  ) {
459
- const exclude = new Set(excludeNames);
460
- const clone = directoryCloneEnabled ? await directoryCloner() : null;
459
+ const exclude = new Set(excludeNames)
460
+ const clone = directoryCloneEnabled ? await directoryCloner() : null
461
461
  // Nothing to skip at this level: the whole tree clones in one syscall.
462
462
  if (exclude.size === 0 && !existsSync(to)) {
463
- await mkdir(path.dirname(to), { recursive: true });
464
- if (clone?.(from, to)) return normalizeClone(to, pack, visited);
463
+ await mkdir(path.dirname(to), { recursive: true })
464
+ if (clone?.(from, to)) return normalizeClone(to, pack, visited)
465
465
  }
466
466
  // The build output is still live while the walk runs — typegen rewrites
467
467
  // `.pnext/types` under it — so a directory can vanish between the parent's
468
468
  // readdir and this one. It was never part of the shipped closure; skip it.
469
- const entries = readDirEntries(from);
470
- if (!entries) return;
471
- await mkdir(to, { recursive: true });
469
+ const entries = readDirEntries(from)
470
+ if (!entries) return
471
+ await mkdir(to, { recursive: true })
472
472
  await Promise.all(
473
473
  entries.map(async entry => {
474
- const source = path.join(from, entry.name);
475
- const target = path.join(to, entry.name);
474
+ const source = path.join(from, entry.name)
475
+ const target = path.join(to, entry.name)
476
476
  // Matched by name before type: a symlinked `node_modules` must be pruned
477
477
  // as the directory it points at, not followed.
478
- if (exclude.has(entry.name) || !shipsDir(pack, entry.name, source)) return;
478
+ if (exclude.has(entry.name) || !shipsDir(pack, entry.name, source)) return
479
479
  if (entry.isSymbolicLink()) {
480
- const link = resolveLinkTarget(source);
481
- if (!link) return;
480
+ const link = resolveLinkTarget(source)
481
+ if (!link) return
482
482
  if (link.isDirectory) {
483
- if (visited.has(link.path)) return;
484
- visited.add(link.path);
485
- return copyTree(link.path, target, pack, [], visited);
483
+ if (visited.has(link.path)) return
484
+ visited.add(link.path)
485
+ return copyTree(link.path, target, pack, [], visited)
486
486
  }
487
- return shipsFile(pack, entry.name) ? linkOrCopyFile(link.path, target) : undefined;
487
+ return shipsFile(pack, entry.name) ? linkOrCopyFile(link.path, target) : undefined
488
488
  }
489
489
  // One syscall for the whole subtree beats one per file — the excluded
490
490
  // names are stripped from the copy afterwards.
491
491
  if (entry.isDirectory() && clone?.(source, target)) {
492
- return normalizeClone(target, pack, visited);
492
+ return normalizeClone(target, pack, visited)
493
493
  }
494
- if (entry.isDirectory()) return copyTree(source, target, pack, [], visited);
495
- if (entry.isFile() && shipsFile(pack, entry.name)) return linkOrCopyFile(source, target);
494
+ if (entry.isDirectory()) return copyTree(source, target, pack, [], visited)
495
+ if (entry.isFile() && shipsFile(pack, entry.name)) return linkOrCopyFile(source, target)
496
496
  }),
497
- );
497
+ )
498
498
  }
499
499
 
500
500
  /** Directory entries, or undefined if the directory vanished under the walk. */
501
501
  function readDirEntries(dir: string) {
502
502
  try {
503
- return readdirSync(dir, { withFileTypes: true });
503
+ return readdirSync(dir, { withFileTypes: true })
504
504
  } catch (error) {
505
- if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;
506
- throw error;
505
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined
506
+ throw error
507
507
  }
508
508
  }
509
509
 
@@ -513,34 +513,34 @@ function readDirEntries(dir: string) {
513
513
  * never retried - no cloner just means the hardlink walk below does the work.
514
514
  */
515
515
  // Test seam: the equivalence suite builds the same app both ways and diffs the trees.
516
- let directoryCloneEnabled = true;
516
+ let directoryCloneEnabled = true
517
517
 
518
518
  /** @internal Test-only. Returns a restore function. */
519
519
  export function setDirectoryCloneEnabled(enabled: boolean) {
520
- const previous = directoryCloneEnabled;
521
- directoryCloneEnabled = enabled;
520
+ const previous = directoryCloneEnabled
521
+ directoryCloneEnabled = enabled
522
522
  return () => {
523
- directoryCloneEnabled = previous;
524
- };
523
+ directoryCloneEnabled = previous
524
+ }
525
525
  }
526
526
 
527
- let cloner: ((from: string, to: string) => boolean) | null | undefined;
527
+ let cloner: ((from: string, to: string) => boolean) | null | undefined
528
528
  async function directoryCloner() {
529
- if (cloner !== undefined) return cloner;
530
- cloner = null;
529
+ if (cloner !== undefined) return cloner
530
+ cloner = null
531
531
  if (process.platform === 'darwin') {
532
532
  try {
533
- const { dlopen } = await import('bun:ffi');
533
+ const { dlopen } = await import('bun:ffi')
534
534
  const { symbols } = dlopen('libSystem.B.dylib', {
535
535
  clonefile: { args: ['cstring', 'cstring', 'i32'], returns: 'i32' },
536
- });
536
+ })
537
537
  cloner = (from, to) =>
538
- symbols.clonefile(Buffer.from(`${from}\0`), Buffer.from(`${to}\0`), 0) === 0;
538
+ symbols.clonefile(Buffer.from(`${from}\0`), Buffer.from(`${to}\0`), 0) === 0
539
539
  } catch {
540
540
  // no clonefile here; the per-file walk stays correct, just slower
541
541
  }
542
542
  }
543
- return cloner;
543
+ return cloner
544
544
  }
545
545
 
546
546
  /**
@@ -551,49 +551,49 @@ async function directoryCloner() {
551
551
  * to the per-file copies this replaces.
552
552
  */
553
553
  async function normalizeClone(dir: string, pack: PackRules, visited: Set<string>) {
554
- const stack = [dir];
555
- const pending: Promise<unknown>[] = [];
554
+ const stack = [dir]
555
+ const pending: Promise<unknown>[] = []
556
556
  while (stack.length > 0) {
557
- const current = stack.pop()!;
557
+ const current = stack.pop()!
558
558
  for (const entry of readdirSync(current, { withFileTypes: true })) {
559
- const full = path.join(current, entry.name);
559
+ const full = path.join(current, entry.name)
560
560
  // Matched by name before type, as the walk does: the pages-compat shim
561
561
  // links `node_modules` at the app root, and following it would pull the
562
562
  // whole store into the function. The clone is already inside the
563
563
  // function, so the walk's keep-path exemption cannot apply here.
564
564
  if (pack.prunedDirs.has(entry.name)) {
565
- rmSync(full, { recursive: true, force: true });
565
+ rmSync(full, { recursive: true, force: true })
566
566
  } else if (entry.isSymbolicLink()) {
567
- const link = resolveLinkTarget(full);
568
- rmSync(full, { force: true });
569
- if (!link) continue;
567
+ const link = resolveLinkTarget(full)
568
+ rmSync(full, { force: true })
569
+ if (!link) continue
570
570
  if (link.isDirectory) {
571
- if (visited.has(link.path)) continue;
572
- visited.add(link.path);
573
- pending.push(copyTree(link.path, full, pack, [], visited));
571
+ if (visited.has(link.path)) continue
572
+ visited.add(link.path)
573
+ pending.push(copyTree(link.path, full, pack, [], visited))
574
574
  } else if (shipsFile(pack, entry.name)) {
575
- pending.push(linkOrCopyFile(link.path, full));
575
+ pending.push(linkOrCopyFile(link.path, full))
576
576
  }
577
577
  } else if (entry.isDirectory()) {
578
- stack.push(full);
578
+ stack.push(full)
579
579
  } else if (!shipsFile(pack, entry.name)) {
580
- rmSync(full, { force: true });
580
+ rmSync(full, { force: true })
581
581
  }
582
582
  }
583
583
  }
584
- await Promise.all(pending);
584
+ await Promise.all(pending)
585
585
  }
586
586
 
587
587
  interface FrameworkPackage {
588
- root: string;
589
- packageName: string;
588
+ root: string
589
+ packageName: string
590
590
  /** Declared runtime dependencies — the only packages it can load eagerly. */
591
- dependencies: string[];
591
+ dependencies: string[]
592
592
  /** Lazily loaded feature deps (og, mdx, sass, image optimization). */
593
- optionalDependencies: string[];
594
- inWorkspace: boolean;
593
+ optionalDependencies: string[]
594
+ inWorkspace: boolean
595
595
  /** Where the framework ships inside the function, relative to its root. */
596
- targetRel: string;
596
+ targetRel: string
597
597
  }
598
598
 
599
599
  // The framework itself ships with the function. Inside a workspace it keeps its
@@ -601,10 +601,10 @@ interface FrameworkPackage {
601
601
  // outside the workspace (e.g. installed from a registry) it lands in the
602
602
  // function's node_modules under its package name.
603
603
  async function resolveFrameworkPackage(workspaceRoot: string): Promise<FrameworkPackage> {
604
- const root = path.resolve(import.meta.dirname, '../../..');
605
- const packageJson = await readPackageJson(root);
606
- const packageName = packageJson?.name ?? 'pnext';
607
- const inWorkspace = isInsideDir(workspaceRoot, root) && !root.includes('node_modules');
604
+ const root = path.resolve(import.meta.dirname, '../../..')
605
+ const packageJson = await readPackageJson(root)
606
+ const packageName = packageJson?.name ?? 'pnext'
607
+ const inWorkspace = isInsideDir(workspaceRoot, root) && !root.includes('node_modules')
608
608
  return {
609
609
  root,
610
610
  packageName,
@@ -616,7 +616,7 @@ async function resolveFrameworkPackage(workspaceRoot: string): Promise<Framework
616
616
  targetRel: inWorkspace
617
617
  ? path.relative(workspaceRoot, root)
618
618
  : path.join('node_modules', packageName),
619
- };
619
+ }
620
620
  }
621
621
 
622
622
  // Build-time directories a package that publishes its whole tree still keeps.
@@ -630,7 +630,7 @@ const packageCopyExcludes = [
630
630
  'test',
631
631
  'tests',
632
632
  '__tests__',
633
- ];
633
+ ]
634
634
 
635
635
  /**
636
636
  * Copies a workspace package the way npm would publish it: a `files` list is the package's own
@@ -638,69 +638,69 @@ const packageCopyExcludes = [
638
638
  * back to the exclude list - matching npm's glob semantics is not worth it for a packaging copy.
639
639
  */
640
640
  async function copyPackageTree(from: string, to: string, pack: PackRules) {
641
- const packageJson = await readPackageJson(from);
642
- const files = packageJson?.files;
641
+ const packageJson = await readPackageJson(from)
642
+ const files = packageJson?.files
643
643
  if (!files?.length || files.some(entry => /[*?[\]{}!]/.test(entry))) {
644
- return copyTree(from, to, pack, packageCopyExcludes);
644
+ return copyTree(from, to, pack, packageCopyExcludes)
645
645
  }
646
- await mkdir(to, { recursive: true });
646
+ await mkdir(to, { recursive: true })
647
647
  // npm always publishes these two regardless of `files`; the runtime resolves
648
648
  // through both (tsconfig for Bun's `paths`/jsx settings). Listed entries need
649
649
  // no exclude list — `files` already said what ships — which also lets each
650
650
  // directory clone whole.
651
651
  await Promise.all(
652
652
  [...files, 'package.json', 'tsconfig.json'].map(async name => {
653
- const source = path.join(from, name);
654
- const stats = statSync(source, { throwIfNoEntry: false });
655
- if (!stats) return;
656
- if (stats.isDirectory()) return copyTree(source, path.join(to, name), pack);
657
- if (!shipsFile(pack, name)) return;
658
- return linkOrCopyFile(source, path.join(to, name));
653
+ const source = path.join(from, name)
654
+ const stats = statSync(source, { throwIfNoEntry: false })
655
+ if (!stats) return
656
+ if (stats.isDirectory()) return copyTree(source, path.join(to, name), pack)
657
+ if (!shipsFile(pack, name)) return
658
+ return linkOrCopyFile(source, path.join(to, name))
659
659
  }),
660
- );
660
+ )
661
661
  }
662
662
 
663
663
  async function readPackageJson(dir: string) {
664
- const file = path.join(dir, 'package.json');
665
- if (!existsSync(file)) return undefined;
664
+ const file = path.join(dir, 'package.json')
665
+ if (!existsSync(file)) return undefined
666
666
  return JSON.parse(await readFile(file, 'utf8')) as {
667
- name?: string;
668
- version?: string;
669
- files?: string[];
670
- os?: string[];
671
- cpu?: string[];
672
- libc?: string[];
673
- dependencies?: Record<string, string>;
674
- optionalDependencies?: Record<string, string>;
675
- };
667
+ name?: string
668
+ version?: string
669
+ files?: string[]
670
+ os?: string[]
671
+ cpu?: string[]
672
+ libc?: string[]
673
+ dependencies?: Record<string, string>
674
+ optionalDependencies?: Record<string, string>
675
+ }
676
676
  }
677
677
 
678
678
  // The closure hardlinks ~13k files: ~2.9s of a ~3.1s build. Test seam so suites
679
679
  // asserting only config shape can skip it. Never off for a real deployment.
680
- let dependencyClosureEnabled = true;
680
+ let dependencyClosureEnabled = true
681
681
 
682
682
  /** @internal Test-only. Returns a restore function. */
683
683
  export function setDependencyClosureEnabled(enabled: boolean) {
684
- const previous = dependencyClosureEnabled;
685
- dependencyClosureEnabled = enabled;
684
+ const previous = dependencyClosureEnabled
685
+ dependencyClosureEnabled = enabled
686
686
  return () => {
687
- dependencyClosureEnabled = previous;
688
- };
687
+ dependencyClosureEnabled = previous
688
+ }
689
689
  }
690
690
 
691
691
  function skipDependencyClosure() {
692
- return !dependencyClosureEnabled;
692
+ return !dependencyClosureEnabled
693
693
  }
694
694
 
695
695
  async function linkOrCopyFile(from: string, to: string) {
696
696
  try {
697
- await link(from, to);
697
+ await link(from, to)
698
698
  } catch {
699
699
  // Same race as the walk above: a source that vanished mid-copy was never
700
700
  // part of the closure, and nothing else here is worth failing the build for.
701
701
  await copyFile(from, to).catch(error => {
702
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
703
- });
702
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
703
+ })
704
704
  }
705
705
  }
706
706
 
@@ -709,26 +709,26 @@ async function linkOrCopyFile(from: string, to: string) {
709
709
  // dep, interrupted install), so an unreadable target is skipped rather than
710
710
  // failing the build.
711
711
  function resolveLinkTarget(link: string) {
712
- const resolved = realDir(link) ?? realFile(link);
713
- if (!resolved) return undefined;
712
+ const resolved = realDir(link) ?? realFile(link)
713
+ if (!resolved) return undefined
714
714
  try {
715
- return { path: resolved, isDirectory: statSync(resolved).isDirectory() };
715
+ return { path: resolved, isDirectory: statSync(resolved).isDirectory() }
716
716
  } catch {
717
- return undefined;
717
+ return undefined
718
718
  }
719
719
  }
720
720
 
721
721
  function realFile(file: string) {
722
722
  try {
723
- return realpathSync(file);
723
+ return realpathSync(file)
724
724
  } catch {
725
- return undefined;
725
+ return undefined
726
726
  }
727
727
  }
728
728
 
729
729
  function isInsideDir(root: string, file: string) {
730
- const relative = path.relative(root, file);
731
- return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
730
+ const relative = path.relative(root, file)
731
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))
732
732
  }
733
733
 
734
734
  async function injectCompatPaths(
@@ -739,101 +739,100 @@ async function injectCompatPaths(
739
739
  const aliasTargets = Object.entries(getImportAliasExtensions().aliases(config, 'server'))
740
740
  .filter(([, target]) => path.isAbsolute(target) && !target.includes('node_modules'))
741
741
  .map(([specifier, target]) => [specifier, replicaPathFor(target)] as const)
742
- .filter((entry): entry is readonly [string, string] => Boolean(entry[1]));
743
- if (aliasTargets.length === 0) return;
742
+ .filter((entry): entry is readonly [string, string] => Boolean(entry[1]))
743
+ if (aliasTargets.length === 0) return
744
744
 
745
745
  for (const file of walkTsconfigFiles(functionPath)) {
746
- let parsed: { compilerOptions?: { baseUrl?: string; paths?: Record<string, string[]> } };
746
+ let parsed: { compilerOptions?: { baseUrl?: string; paths?: Record<string, string[]> } }
747
747
  try {
748
- const source = stripJsonComments(await readFile(file, 'utf8')).replace(/,\s*([}\]])/g, '$1');
749
- parsed = JSON.parse(source) as typeof parsed;
748
+ const source = stripJsonComments(await readFile(file, 'utf8')).replace(/,\s*([}\]])/g, '$1')
749
+ parsed = JSON.parse(source) as typeof parsed
750
750
  } catch (error) {
751
- console.warn(`vercel adapter: could not update ${file}:`, error);
752
- continue;
751
+ console.warn(`vercel adapter: could not update ${file}:`, error)
752
+ continue
753
753
  }
754
- const dir = path.dirname(file);
755
- const compilerOptions = (parsed.compilerOptions ??= {});
756
- const paths = (compilerOptions.paths ??= {});
754
+ const dir = path.dirname(file)
755
+ const compilerOptions = (parsed.compilerOptions ??= {})
756
+ const paths = (compilerOptions.paths ??= {})
757
757
  for (const [specifier, target] of aliasTargets) {
758
- if (paths[specifier]) continue;
759
- const relative = toPosixPath(path.relative(dir, target));
760
- paths[specifier] = [relative.startsWith('.') ? relative : `./${relative}`];
758
+ if (paths[specifier]) continue
759
+ const relative = toPosixPath(path.relative(dir, target))
760
+ paths[specifier] = [relative.startsWith('.') ? relative : `./${relative}`]
761
761
  }
762
- compilerOptions.baseUrl ??= '.';
762
+ compilerOptions.baseUrl ??= '.'
763
763
  // The replica shares inodes with the original tsconfig; never write
764
764
  // through the hardlink.
765
- await rm(file);
766
- await writeFile(file, `${JSON.stringify(parsed, null, 2)}\n`);
765
+ await rm(file)
766
+ await writeFile(file, `${JSON.stringify(parsed, null, 2)}\n`)
767
767
  }
768
768
  }
769
769
 
770
770
  // Minimal JSONC support for tsconfig files: strips // and /* */ comments
771
771
  // outside strings (a "$schema": "https://..." value must survive).
772
772
  function stripJsonComments(source: string) {
773
- let result = '';
774
- let inString = false;
773
+ let result = ''
774
+ let inString = false
775
775
  for (let index = 0; index < source.length; index++) {
776
- const char = source[index]!;
776
+ const char = source[index]!
777
777
  if (inString) {
778
- result += char;
778
+ result += char
779
779
  if (char === '\\') {
780
- result += source[++index] ?? '';
780
+ result += source[++index] ?? ''
781
781
  } else if (char === '"') {
782
- inString = false;
782
+ inString = false
783
783
  }
784
- continue;
784
+ continue
785
785
  }
786
786
  if (char === '"') {
787
- inString = true;
788
- result += char;
789
- continue;
787
+ inString = true
788
+ result += char
789
+ continue
790
790
  }
791
791
  if (char === '/' && source[index + 1] === '/') {
792
- while (index < source.length && source[index] !== '\n') index++;
793
- result += '\n';
794
- continue;
792
+ while (index < source.length && source[index] !== '\n') index++
793
+ result += '\n'
794
+ continue
795
795
  }
796
796
  if (char === '/' && source[index + 1] === '*') {
797
- index += 2;
798
- while (index < source.length && !(source[index] === '*' && source[index + 1] === '/'))
799
- index++;
800
- index++;
801
- continue;
797
+ index += 2
798
+ while (index < source.length && !(source[index] === '*' && source[index + 1] === '/')) index++
799
+ index++
800
+ continue
802
801
  }
803
- result += char;
802
+ result += char
804
803
  }
805
- return result;
804
+ return result
806
805
  }
807
806
 
808
- const bakedPathFileFilter = /\.(?:m?js|cjs|jsx|tsx?|json|html|css)$/;
807
+ const bakedPathFileFilter = /\.(?:m?js|cjs|jsx|tsx?|json|html|css)$/
809
808
 
810
809
  // The build output is thousands of small files and only a few hundred carry a
811
810
  // baked path, so the pass is io-latency-bound: run it in bounded-concurrency
812
811
  // batches instead of one file at a time.
813
- const BAKED_PATH_CONCURRENCY = 64;
812
+ const BAKED_PATH_CONCURRENCY = 64
814
813
 
815
814
  async function rewriteBakedPaths(
816
815
  dir: string,
817
816
  replacements: readonly (readonly [string, string])[],
818
817
  ) {
819
- if (!existsSync(dir)) return;
820
- const files = (await listFiles(dir)).filter(file => bakedPathFileFilter.test(file));
818
+ if (!existsSync(dir)) return
819
+ const files = (await listFiles(dir)).filter(file => bakedPathFileFilter.test(file))
821
820
  for (let i = 0; i < files.length; i += BAKED_PATH_CONCURRENCY) {
822
821
  await Promise.all(
823
822
  files.slice(i, i + BAKED_PATH_CONCURRENCY).map(async file => {
824
- const source = await readFile(file, 'utf8');
825
- let rewritten = source;
823
+ const source = await readFile(file, 'utf8')
824
+ let rewritten = source
826
825
  for (const [from, to] of replacements) {
827
- if (rewritten.includes(from)) rewritten = rewritten.replaceAll(from, to);
826
+ if (rewritten.includes(from)) rewritten = rewritten.replaceAll(from, to)
828
827
  }
829
- if (rewritten === source) return;
828
+ if (rewritten === source) return
830
829
  // The replica is hardlinked to the real build output; unlink before
831
830
  // writing so the rewrite never reaches the original through a shared
832
831
  // inode.
833
- await rm(file);
834
- await writeFile(file, rewritten);
832
+ await rm(file)
833
+ await writeFile(file, rewritten)
835
834
  }),
836
- );
835
+ )
837
836
  }
838
837
  }
839
838
 
@@ -846,19 +845,19 @@ async function rewriteBakedPaths(
846
845
  async function artifactSpecifiers(file: string): Promise<string[] | null> {
847
846
  const sidecar = await readFile(`${file}${compiledSpecifiersManifestSuffix}`, 'utf8').catch(
848
847
  () => null,
849
- );
848
+ )
850
849
  if (sidecar !== null) {
851
850
  try {
852
- const parsed: unknown = JSON.parse(sidecar);
851
+ const parsed: unknown = JSON.parse(sidecar)
853
852
  if (Array.isArray(parsed) && parsed.every(entry => typeof entry === 'string')) {
854
- return parsed;
853
+ return parsed
855
854
  }
856
855
  } catch {
857
856
  // malformed sidecar (partial write, foreign tool) — fall through to parse
858
857
  }
859
858
  }
860
- const source = await readFile(file, 'utf8').catch(() => null);
861
- return source === null ? null : importSpecifiers(source, file);
859
+ const source = await readFile(file, 'utf8').catch(() => null)
860
+ return source === null ? null : importSpecifiers(source, file)
862
861
  }
863
862
 
864
863
  // Ship only the node_modules packages the server can actually reach at request time: bare imports
@@ -875,9 +874,9 @@ async function traceNodeModulesClosure(
875
874
  pack: PackRules,
876
875
  log: VerboseLogger,
877
876
  ) {
878
- const workspaceRoot = config.workspaceRoot;
879
- const specifiers = new Set<string>();
880
- const tracedFiles = new Set<string>();
877
+ const workspaceRoot = config.workspaceRoot
878
+ const specifiers = new Set<string>()
879
+ const tracedFiles = new Set<string>()
881
880
  // Compat-aliased specifiers whose target lives in the framework source
882
881
  // (react, next/*) never resolve from node_modules at runtime — shipping
883
882
  // them would drag in the full next/react trees. Aliases that point into
@@ -886,126 +885,126 @@ async function traceNodeModulesClosure(
886
885
  Object.entries(getImportAliasExtensions().aliases(config, 'server'))
887
886
  .filter(([, target]) => path.isAbsolute(target) && !target.includes('node_modules'))
888
887
  .map(([specifier]) => specifier),
889
- );
888
+ )
890
889
  await log.step('trace runtime imports', async () => {
891
890
  // Only code Bun imports raw at request time needs real node_modules: route handlers, the proxy,
892
891
  // the config chain, and the compiled modules the warm step just wrote. Pages resolve through that
893
892
  // compiled cache, whose externals are vendored, so their dependencies never load raw.
894
- const queue: string[] = [];
895
- const seen = tracedFiles;
893
+ const queue: string[] = []
894
+ const seen = tracedFiles
896
895
  const enqueue = (file: string | undefined) => {
897
- if (!file) return;
898
- const resolved = path.resolve(file);
899
- if (seen.has(resolved) || !existsSync(resolved)) return;
900
- seen.add(resolved);
901
- queue.push(resolved);
902
- };
896
+ if (!file) return
897
+ const resolved = path.resolve(file)
898
+ if (seen.has(resolved) || !existsSync(resolved)) return
899
+ seen.add(resolved)
900
+ queue.push(resolved)
901
+ }
903
902
 
904
903
  for (const route of manifest.routes) {
905
- if (route.kind === 'handler') enqueue(route.file);
904
+ if (route.kind === 'handler') enqueue(route.file)
906
905
  }
907
- enqueue(findProxyFile(config) ?? undefined);
908
- enqueue(path.join(config.root, 'pnext.config.ts'));
909
- enqueue(path.join(config.root, 'next.config.js'));
910
- for (const file of warmedModules) enqueue(file);
906
+ enqueue(findProxyFile(config) ?? undefined)
907
+ enqueue(path.join(config.root, 'pnext.config.ts'))
908
+ enqueue(path.join(config.root, 'next.config.js'))
909
+ for (const file of warmedModules) enqueue(file)
911
910
 
912
911
  const visit = async (file: string) => {
913
- if (!scriptFilePattern.test(file)) return;
914
- const found = await artifactSpecifiers(file);
915
- if (found === null) return;
912
+ if (!scriptFilePattern.test(file)) return
913
+ const found = await artifactSpecifiers(file)
914
+ if (found === null) return
916
915
  for (const specifier of found) {
917
- if (frameworkAliasedSpecifiers.has(specifier)) continue;
916
+ if (frameworkAliasedSpecifiers.has(specifier)) continue
918
917
  // Compiled modules import each other — and whatever the compiler left
919
918
  // external — by absolute href. Follow the ones inside the build output
920
919
  // and ship the packages the rest point into.
921
- const absolute = absolutePathFromSpecifier(specifier);
920
+ const absolute = absolutePathFromSpecifier(specifier)
922
921
  if (absolute) {
923
- const name = packageNameFromPath(absolute);
924
- if (name) specifiers.add(name);
925
- else if (isInsideDir(config.outPath, absolute)) enqueue(absolute);
926
- continue;
922
+ const name = packageNameFromPath(absolute)
923
+ if (name) specifiers.add(name)
924
+ else if (isInsideDir(config.outPath, absolute)) enqueue(absolute)
925
+ continue
927
926
  }
928
- if (/^(?:node:|bun$|bun:|data:)/.test(specifier)) continue;
927
+ if (/^(?:node:|bun$|bun:|data:)/.test(specifier)) continue
929
928
  // resolveImport covers relative paths, tsconfig aliases, package imports and workspace
930
929
  // packages - workspace source gets traced further. Bare package specifiers are also recorded
931
930
  // so the closure ships (or workspace-links) their node_modules entries; workspace packages
932
931
  // need the link even when their source is traced.
933
- const resolved = resolveImport(config.root, file, specifier);
932
+ const resolved = resolveImport(config.root, file, specifier)
934
933
  if (
935
934
  resolved &&
936
935
  isInsideDir(workspaceRoot, resolved) &&
937
936
  !resolved.includes('node_modules')
938
937
  ) {
939
- enqueue(resolved);
938
+ enqueue(resolved)
940
939
  }
941
- const name = packageNameFromSpecifier(specifier);
942
- if (name) specifiers.add(name);
940
+ const name = packageNameFromSpecifier(specifier)
941
+ if (name) specifiers.add(name)
943
942
  }
944
- };
943
+ }
945
944
 
946
945
  // A level's reads are independent, so the walk advances a level at a time, bounded so a wide
947
946
  // graph cannot exhaust the fd table.
948
- const READ_BATCH = 64;
947
+ const READ_BATCH = 64
949
948
  for (let frontier = queue.splice(0); frontier.length > 0; frontier = queue.splice(0)) {
950
949
  for (let index = 0; index < frontier.length; index += READ_BATCH) {
951
- await Promise.all(frontier.slice(index, index + READ_BATCH).map(visit));
950
+ await Promise.all(frontier.slice(index, index + READ_BATCH).map(visit))
952
951
  }
953
952
  }
954
- log.log(`traced ${seen.size} runtime modules`);
955
- });
953
+ log.log(`traced ${seen.size} runtime modules`)
954
+ })
956
955
 
957
- const packageDirs = new Map<string, string>();
958
- const workspaceLinks = new Map<string, string>();
956
+ const packageDirs = new Map<string, string>()
957
+ const workspaceLinks = new Map<string, string>()
959
958
  // Native bindings resolved here are built for the build host; the function
960
959
  // needs the ones for its own platform, fetched below.
961
- const hostNatives = new Map<string, { dir: string; owner?: PackageVersion }>();
960
+ const hostNatives = new Map<string, { dir: string; owner?: PackageVersion }>()
962
961
  // The framework ships whole, so what it can load at runtime is exactly what it declares as a
963
962
  // runtime dependency - resolved from its own root, which is not the app's when it comes from a
964
963
  // registry. Its optional deps back compat features that only load when the app uses them, so they
965
964
  // ship only where the app declares them too.
966
- const appDependencies = await declaredDependencies(config.root);
965
+ const appDependencies = await declaredDependencies(config.root)
967
966
  const queue: { name: string; from: string; owner?: PackageVersion }[] = [
968
967
  ...framework.dependencies.map(name => ({ name, from: framework.root })),
969
968
  ...framework.optionalDependencies
970
969
  .filter(name => appDependencies.has(name))
971
970
  .map(name => ({ name, from: framework.root })),
972
971
  ...[...specifiers].map(name => ({ name, from: workspaceRoot })),
973
- ];
974
- const seen = new Set<string>();
972
+ ]
973
+ const seen = new Set<string>()
975
974
 
976
975
  while (queue.length > 0) {
977
- const { name, from, owner } = queue.shift()!;
978
- if (seen.has(name)) continue;
979
- seen.add(name);
976
+ const { name, from, owner } = queue.shift()!
977
+ if (seen.has(name)) continue
978
+ seen.add(name)
980
979
  // The framework is copied to its own location by the caller; copying it
981
980
  // again under its package name collides with that (and with the workspace
982
981
  // link it needs when it lives in the workspace).
983
982
  if (name === framework.packageName) {
984
- if (framework.inWorkspace) workspaceLinks.set(name, framework.targetRel);
985
- continue;
983
+ if (framework.inWorkspace) workspaceLinks.set(name, framework.targetRel)
984
+ continue
986
985
  }
987
- const dir = resolvePackageDir(name, [from, workspaceRoot, config.root]);
988
- if (!dir) continue;
986
+ const dir = resolvePackageDir(name, [from, workspaceRoot, config.root])
987
+ if (!dir) continue
989
988
 
990
989
  // Workspace packages ship at their workspace-relative path; link them so
991
990
  // bare imports resolve to the same real path the compiled cache uses.
992
- const workspaceRelative = path.relative(workspaceRoot, dir);
991
+ const workspaceRelative = path.relative(workspaceRoot, dir)
993
992
  if (!workspaceRelative.startsWith('..') && !workspaceRelative.startsWith('node_modules')) {
994
- workspaceLinks.set(name, workspaceRelative);
995
- continue;
993
+ workspaceLinks.set(name, workspaceRelative)
994
+ continue
996
995
  }
997
996
 
998
- const packageJson = await readPackageJson(dir);
997
+ const packageJson = await readPackageJson(dir)
999
998
  // `os`/`cpu` are npm's own declaration that a package is platform-bound;
1000
999
  // one that already fits the function's platform ships as-is.
1001
- const platformBound = Boolean(packageJson?.os ?? packageJson?.cpu);
1000
+ const platformBound = Boolean(packageJson?.os ?? packageJson?.cpu)
1002
1001
  if (packageJson && platformBound && !matchesFunctionPlatform(packageJson)) {
1003
- hostNatives.set(name, { dir, owner });
1004
- continue;
1002
+ hostNatives.set(name, { dir, owner })
1003
+ continue
1005
1004
  }
1006
- packageDirs.set(name, dir);
1007
- if (!packageJson) continue;
1008
- const self = { name, version: packageJson.version ?? '' };
1005
+ packageDirs.set(name, dir)
1006
+ if (!packageJson) continue
1007
+ const self = { name, version: packageJson.version ?? '' }
1009
1008
  // Peer dependencies are deliberately not expanded — they fan out to whole
1010
1009
  // ecosystems (react, next, ...) that are only shipped when something
1011
1010
  // actually imports them.
@@ -1013,7 +1012,7 @@ async function traceNodeModulesClosure(
1013
1012
  ...Object.keys(packageJson.dependencies ?? {}),
1014
1013
  ...Object.keys(packageJson.optionalDependencies ?? {}),
1015
1014
  ]) {
1016
- queue.push({ name: dependency, from: dir, owner: self });
1015
+ queue.push({ name: dependency, from: dir, owner: self })
1017
1016
  }
1018
1017
  }
1019
1018
 
@@ -1021,44 +1020,44 @@ async function traceNodeModulesClosure(
1021
1020
  const targets = await log.step(
1022
1021
  `resolve ${FUNCTION_PLATFORM.os}-${FUNCTION_PLATFORM.cpu} natives (${hostNatives.size} packages)`,
1023
1022
  () => platformNativePackages(hostNatives, log),
1024
- );
1025
- for (const [name, dir] of targets) packageDirs.set(name, dir);
1023
+ )
1024
+ for (const [name, dir] of targets) packageDirs.set(name, dir)
1026
1025
  }
1027
1026
 
1028
1027
  const copy = async () => {
1029
- const entries = skipDependencyClosure() ? [] : [...packageDirs];
1030
- const batchSize = 16;
1028
+ const entries = skipDependencyClosure() ? [] : [...packageDirs]
1029
+ const batchSize = 16
1031
1030
  for (let index = 0; index < entries.length; index += batchSize) {
1032
1031
  await Promise.all(
1033
1032
  entries.slice(index, index + batchSize).map(async ([name, dir]) => {
1034
- const target = path.join(functionPath, 'node_modules', name);
1035
- await mkdir(path.dirname(target), { recursive: true });
1036
- await copyTree(dir, target, pack);
1033
+ const target = path.join(functionPath, 'node_modules', name)
1034
+ await mkdir(path.dirname(target), { recursive: true })
1035
+ await copyTree(dir, target, pack)
1037
1036
  }),
1038
- );
1037
+ )
1039
1038
  }
1040
1039
  for (const [name, workspaceRelative] of workspaceLinks) {
1041
- const linkPath = path.join(functionPath, 'node_modules', name);
1042
- await mkdir(path.dirname(linkPath), { recursive: true });
1040
+ const linkPath = path.join(functionPath, 'node_modules', name)
1041
+ await mkdir(path.dirname(linkPath), { recursive: true })
1043
1042
  const target = path.relative(
1044
1043
  path.dirname(linkPath),
1045
1044
  path.join(functionPath, workspaceRelative),
1046
- );
1047
- if (!existsSync(linkPath)) await symlink(target, linkPath, 'dir');
1045
+ )
1046
+ if (!existsSync(linkPath)) await symlink(target, linkPath, 'dir')
1048
1047
  }
1049
- };
1048
+ }
1050
1049
 
1051
1050
  return {
1052
1051
  tracedFiles,
1053
1052
  workspacePackages: [...workspaceLinks.values()],
1054
1053
  packageCount: packageDirs.size,
1055
1054
  copy,
1056
- };
1055
+ }
1057
1056
  }
1058
1057
 
1059
1058
  interface PackageVersion {
1060
- name: string;
1061
- version: string;
1059
+ name: string
1060
+ version: string
1062
1061
  }
1063
1062
 
1064
1063
  /**
@@ -1074,56 +1073,52 @@ async function platformNativePackages(
1074
1073
  ) {
1075
1074
  // Install the package that *owns* the binding: only it lists every
1076
1075
  // platform's build in its optional deps.
1077
- const owners = new Map<string, string>();
1076
+ const owners = new Map<string, string>()
1078
1077
  for (const [name, { dir, owner }] of hostNatives) {
1079
- const spec = owner ?? { name, version: (await readPackageJson(dir))?.version ?? '' };
1080
- if (spec.version) owners.set(spec.name, spec.version);
1078
+ const spec = owner ?? { name, version: (await readPackageJson(dir))?.version ?? '' }
1079
+ if (spec.version) owners.set(spec.name, spec.version)
1081
1080
  }
1082
- const specs = [...owners].map(([name, version]) => `${name}@${version}`).sort();
1083
- const fallback = () => new Map([...hostNatives].map(([name, { dir }]) => [name, dir]));
1084
- if (specs.length === 0) return fallback();
1081
+ const specs = [...owners].map(([name, version]) => `${name}@${version}`).sort()
1082
+ const fallback = () => new Map([...hostNatives].map(([name, { dir }]) => [name, dir]))
1083
+ if (specs.length === 0) return fallback()
1085
1084
 
1086
1085
  try {
1087
- const installed = await crossPlatformInstall(specs);
1088
- const matches = new Map<string, string>();
1086
+ const installed = await crossPlatformInstall(specs)
1087
+ const matches = new Map<string, string>()
1089
1088
  for (const [name, dir] of installed) {
1090
- const packageJson = await readPackageJson(dir);
1091
- if (packageJson && matchesFunctionPlatform(packageJson)) matches.set(name, dir);
1089
+ const packageJson = await readPackageJson(dir)
1090
+ if (packageJson && matchesFunctionPlatform(packageJson)) matches.set(name, dir)
1092
1091
  }
1093
1092
  if (matches.size === 0) {
1094
- throw new Error(`no ${FUNCTION_PLATFORM.os} builds in ${specs.join(' ')}`);
1093
+ throw new Error(`no ${FUNCTION_PLATFORM.os} builds in ${specs.join(' ')}`)
1095
1094
  }
1096
- return matches;
1095
+ return matches
1097
1096
  } catch (error) {
1098
1097
  console.warn(
1099
1098
  `vercel adapter: could not fetch ${FUNCTION_PLATFORM.os}-${FUNCTION_PLATFORM.cpu} native packages; ` +
1100
1099
  `the function will carry this machine's builds and fail at runtime:`,
1101
1100
  error,
1102
- );
1103
- log.log('falling back to host native packages');
1104
- return fallback();
1101
+ )
1102
+ log.log('falling back to host native packages')
1103
+ return fallback()
1105
1104
  }
1106
1105
  }
1107
1106
 
1108
- function matchesFunctionPlatform(packageJson: {
1109
- os?: string[];
1110
- cpu?: string[];
1111
- libc?: string[];
1112
- }) {
1107
+ function matchesFunctionPlatform(packageJson: { os?: string[]; cpu?: string[]; libc?: string[] }) {
1113
1108
  // npm's field semantics: an all-negated list excludes, anything else is an
1114
1109
  // allow-list (`os: ["!win32"]` still fits linux).
1115
1110
  const matches = (declared: string[] | undefined, value: string) => {
1116
- if (!declared?.length) return true;
1117
- if (declared.every(entry => entry.startsWith('!'))) return !declared.includes(`!${value}`);
1118
- return declared.includes(value);
1119
- };
1111
+ if (!declared?.length) return true
1112
+ if (declared.every(entry => entry.startsWith('!'))) return !declared.includes(`!${value}`)
1113
+ return declared.includes(value)
1114
+ }
1120
1115
  return (
1121
1116
  matches(packageJson.os, FUNCTION_PLATFORM.os) &&
1122
1117
  matches(packageJson.cpu, FUNCTION_PLATFORM.cpu) &&
1123
1118
  // Both a glibc and a musl build exist for the same os/cpu; Vercel's
1124
1119
  // function runtime is glibc.
1125
1120
  matches(packageJson.libc, FUNCTION_PLATFORM.libc)
1126
- );
1121
+ )
1127
1122
  }
1128
1123
 
1129
1124
  /**
@@ -1135,16 +1130,16 @@ function matchesFunctionPlatform(packageJson: {
1135
1130
  async function crossPlatformInstall(specs: string[]) {
1136
1131
  const key = Bun.hash
1137
1132
  .xxHash3(`${FUNCTION_PLATFORM.os}-${FUNCTION_PLATFORM.cpu}\0${specs.join('\0')}`)
1138
- .toString(16);
1139
- const dir = path.join(cacheRoot(), 'vercel-natives', key);
1140
- const modules = path.join(dir, 'node_modules');
1133
+ .toString(16)
1134
+ const dir = path.join(cacheRoot(), 'vercel-natives', key)
1135
+ const modules = path.join(dir, 'node_modules')
1141
1136
  if (!existsSync(modules)) {
1142
1137
  // Land under a temp name and rename: a concurrent build must never read a
1143
1138
  // half-installed tree.
1144
- const staging = `${dir}.${process.pid.toString(36)}`;
1145
- await rm(staging, { recursive: true, force: true });
1146
- await mkdir(staging, { recursive: true });
1147
- await writeText(path.join(staging, 'package.json'), '{"name":"pnext-natives"}\n');
1139
+ const staging = `${dir}.${process.pid.toString(36)}`
1140
+ await rm(staging, { recursive: true, force: true })
1141
+ await mkdir(staging, { recursive: true })
1142
+ await writeText(path.join(staging, 'package.json'), '{"name":"pnext-natives"}\n')
1148
1143
  const install = Bun.spawn(
1149
1144
  [
1150
1145
  'bun',
@@ -1157,46 +1152,46 @@ async function crossPlatformInstall(specs: string[]) {
1157
1152
  ...specs,
1158
1153
  ],
1159
1154
  { cwd: staging, stdout: 'ignore', stderr: 'pipe' },
1160
- );
1155
+ )
1161
1156
  if ((await install.exited) !== 0) {
1162
- const stderr = await new Response(install.stderr).text();
1163
- await rm(staging, { recursive: true, force: true });
1164
- throw new Error(stderr.trim() || 'bun add failed');
1157
+ const stderr = await new Response(install.stderr).text()
1158
+ await rm(staging, { recursive: true, force: true })
1159
+ throw new Error(stderr.trim() || 'bun add failed')
1165
1160
  }
1166
- await rm(dir, { recursive: true, force: true });
1167
- await rename(staging, dir);
1161
+ await rm(dir, { recursive: true, force: true })
1162
+ await rename(staging, dir)
1168
1163
  }
1169
1164
 
1170
- const installed = new Map<string, string>();
1165
+ const installed = new Map<string, string>()
1171
1166
  for (const entry of readdirSync(modules)) {
1172
- if (entry.startsWith('.')) continue;
1167
+ if (entry.startsWith('.')) continue
1173
1168
  if (!entry.startsWith('@')) {
1174
- installed.set(entry, path.join(modules, entry));
1175
- continue;
1169
+ installed.set(entry, path.join(modules, entry))
1170
+ continue
1176
1171
  }
1177
1172
  for (const scoped of readdirSync(path.join(modules, entry))) {
1178
- installed.set(`${entry}/${scoped}`, path.join(modules, entry, scoped));
1173
+ installed.set(`${entry}/${scoped}`, path.join(modules, entry, scoped))
1179
1174
  }
1180
1175
  }
1181
- return installed;
1176
+ return installed
1182
1177
  }
1183
1178
 
1184
- const scriptFilePattern = /\.(?:m?js|cjs|jsx|tsx?)$/;
1179
+ const scriptFilePattern = /\.(?:m?js|cjs|jsx|tsx?)$/
1185
1180
 
1186
1181
  // Directories the shipped tsconfigs can never live in; pruned during the walk —
1187
1182
  // a naive recursive listing would crawl the function's own node_modules.
1188
- const skippedScanDirs = new Set(['node_modules', '.git', '.next', '.pnext', '.turbo', '.vercel']);
1183
+ const skippedScanDirs = new Set(['node_modules', '.git', '.next', '.pnext', '.turbo', '.vercel'])
1189
1184
 
1190
1185
  function* walkTsconfigFiles(root: string): Generator<string> {
1191
- const stack = [root];
1186
+ const stack = [root]
1192
1187
  while (stack.length > 0) {
1193
- const dir = stack.pop()!;
1188
+ const dir = stack.pop()!
1194
1189
  for (const entry of readdirSync(dir, { withFileTypes: true })) {
1195
- const full = path.join(dir, entry.name);
1190
+ const full = path.join(dir, entry.name)
1196
1191
  if (entry.isDirectory()) {
1197
- if (!skippedScanDirs.has(entry.name)) stack.push(full);
1192
+ if (!skippedScanDirs.has(entry.name)) stack.push(full)
1198
1193
  } else if (entry.isFile() && entry.name === 'tsconfig.json') {
1199
- yield full;
1194
+ yield full
1200
1195
  }
1201
1196
  }
1202
1197
  }
@@ -1205,62 +1200,61 @@ function* walkTsconfigFiles(root: string): Generator<string> {
1205
1200
  /** Every dependency name an app's own package.json declares. */
1206
1201
  async function declaredDependencies(root: string) {
1207
1202
  const packageJson = (await readPackageJson(root)) as
1208
- | Record<string, Record<string, string> | undefined>
1209
- | undefined;
1203
+ Record<string, Record<string, string> | undefined> | undefined
1210
1204
  return new Set(
1211
1205
  ['dependencies', 'optionalDependencies', 'devDependencies'].flatMap(field =>
1212
1206
  Object.keys(packageJson?.[field] ?? {}),
1213
1207
  ),
1214
- );
1208
+ )
1215
1209
  }
1216
1210
 
1217
1211
  /** Absolute path behind an `import`, whether written as a path or a file URL. */
1218
1212
  function absolutePathFromSpecifier(specifier: string) {
1219
- if (specifier.startsWith('file://')) return fileURLToPath(specifier);
1220
- return path.isAbsolute(specifier) ? specifier : undefined;
1213
+ if (specifier.startsWith('file://')) return fileURLToPath(specifier)
1214
+ return path.isAbsolute(specifier) ? specifier : undefined
1221
1215
  }
1222
1216
 
1223
1217
  /** The package an absolute path belongs to, if it points inside node_modules. */
1224
1218
  function packageNameFromPath(file: string) {
1225
- const parts = toPosixPath(file).split('/node_modules/');
1226
- const tail = parts.at(-1);
1227
- if (parts.length < 2 || !tail) return undefined;
1228
- const segments = tail.split('/');
1229
- return segments[0]?.startsWith('@') ? segments.slice(0, 2).join('/') : segments[0];
1219
+ const parts = toPosixPath(file).split('/node_modules/')
1220
+ const tail = parts.at(-1)
1221
+ if (parts.length < 2 || !tail) return undefined
1222
+ const segments = tail.split('/')
1223
+ return segments[0]?.startsWith('@') ? segments.slice(0, 2).join('/') : segments[0]
1230
1224
  }
1231
1225
 
1232
1226
  function packageNameFromSpecifier(specifier: string) {
1233
- if (/^[./#]|^file:|^node:|^data:/.test(specifier)) return undefined;
1234
- if (specifier === 'bun' || specifier.startsWith('bun:')) return undefined;
1235
- if (specifier.includes(':')) return undefined;
1227
+ if (/^[./#]|^file:|^node:|^data:/.test(specifier)) return undefined
1228
+ if (specifier === 'bun' || specifier.startsWith('bun:')) return undefined
1229
+ if (specifier.includes(':')) return undefined
1236
1230
  // Scoped names need a real scope — `@/env`-style tsconfig aliases are not
1237
1231
  // packages.
1238
- if (specifier.startsWith('@') && !/^@[^/]+\/[^/]+/.test(specifier)) return undefined;
1239
- const parts = specifier.split('/');
1240
- const name = specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0];
1241
- if (!name || builtinModules.includes(name)) return undefined;
1242
- return name;
1232
+ if (specifier.startsWith('@') && !/^@[^/]+\/[^/]+/.test(specifier)) return undefined
1233
+ const parts = specifier.split('/')
1234
+ const name = specifier.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0]
1235
+ if (!name || builtinModules.includes(name)) return undefined
1236
+ return name
1243
1237
  }
1244
1238
 
1245
1239
  function resolvePackageDir(name: string, fromDirs: string[]) {
1246
1240
  for (const from of fromDirs) {
1247
- let dir = path.resolve(from);
1241
+ let dir = path.resolve(from)
1248
1242
  while (true) {
1249
- const candidate = path.join(dir, 'node_modules', name);
1250
- if (existsSync(path.join(candidate, 'package.json'))) return realDir(candidate);
1251
- const parent = path.dirname(dir);
1252
- if (parent === dir) break;
1253
- dir = parent;
1243
+ const candidate = path.join(dir, 'node_modules', name)
1244
+ if (existsSync(path.join(candidate, 'package.json'))) return realDir(candidate)
1245
+ const parent = path.dirname(dir)
1246
+ if (parent === dir) break
1247
+ dir = parent
1254
1248
  }
1255
1249
  }
1256
- return undefined;
1250
+ return undefined
1257
1251
  }
1258
1252
 
1259
1253
  function realDir(dir: string) {
1260
1254
  try {
1261
- return statSync(dir).isDirectory() ? realpathSync(dir) : undefined;
1255
+ return statSync(dir).isDirectory() ? realpathSync(dir) : undefined
1262
1256
  } catch {
1263
- return undefined;
1257
+ return undefined
1264
1258
  }
1265
1259
  }
1266
1260
 
@@ -1271,40 +1265,40 @@ async function copyStaticFiles(
1271
1265
  ) {
1272
1266
  const files = (await listFiles(from)).filter(file =>
1273
1267
  shouldCopy(toPosixPath(path.relative(from, file))),
1274
- );
1268
+ )
1275
1269
  // One mkdir per directory, not per file, then link them all at once.
1276
1270
  const directories = new Set(
1277
1271
  files.map(file => path.dirname(path.join(to, path.relative(from, file)))),
1278
- );
1279
- await Promise.all([...directories].map(dir => mkdir(dir, { recursive: true })));
1272
+ )
1273
+ await Promise.all([...directories].map(dir => mkdir(dir, { recursive: true })))
1280
1274
  await Promise.all(
1281
1275
  files.map(file => linkOrCopyFile(file, path.join(to, path.relative(from, file)))),
1282
- );
1276
+ )
1283
1277
  }
1284
1278
 
1285
1279
  async function staticOverrides(
1286
1280
  publicPath: string,
1287
1281
  staticFiles: Record<string, StaticFileMetadata>,
1288
1282
  ) {
1289
- const overrides: NonNullable<VercelConfig['overrides']> = {};
1283
+ const overrides: NonNullable<VercelConfig['overrides']> = {}
1290
1284
  for (const file of await listFiles(publicPath)) {
1291
- const relative = toPosixPath(path.relative(publicPath, file));
1292
- if (!relative.endsWith('/index.html')) continue;
1293
- overrides[relative] = { path: relative.replace(/\/index\.html$/, '') };
1285
+ const relative = toPosixPath(path.relative(publicPath, file))
1286
+ if (!relative.endsWith('/index.html')) continue
1287
+ overrides[relative] = { path: relative.replace(/\/index\.html$/, '') }
1294
1288
  }
1295
1289
  for (const [relative, metadata] of Object.entries(staticFiles)) {
1296
- if (!canServeStaticOnVercel(metadata)) continue;
1290
+ if (!canServeStaticOnVercel(metadata)) continue
1297
1291
  const contentType = metadata.headers.find(
1298
1292
  ([name]) => name.toLowerCase() === 'content-type',
1299
- )?.[1];
1300
- if (contentType) overrides[relative] = { ...overrides[relative], contentType };
1293
+ )?.[1]
1294
+ if (contentType) overrides[relative] = { ...overrides[relative], contentType }
1301
1295
  }
1302
- return overrides;
1296
+ return overrides
1303
1297
  }
1304
1298
 
1305
1299
  function canServeStaticOnVercel(metadata: StaticFileMetadata) {
1306
1300
  return (
1307
1301
  metadata.status === 200 &&
1308
1302
  metadata.headers.every(([name]) => name.toLowerCase() === 'content-type')
1309
- );
1303
+ )
1310
1304
  }