@wular/pnext 0.0.1

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 (371) hide show
  1. package/README.md +153 -0
  2. package/bin/pnext +67 -0
  3. package/config/lint/base.js +48 -0
  4. package/config/ts/base.json +26 -0
  5. package/config/ts/react.json +13 -0
  6. package/package.json +70 -0
  7. package/reference/compat.md +63 -0
  8. package/reference/config.md +120 -0
  9. package/reference/css.md +58 -0
  10. package/reference/dev.md +69 -0
  11. package/reference/env.md +40 -0
  12. package/reference/metadata.md +86 -0
  13. package/reference/navigation.md +149 -0
  14. package/reference/overview.md +35 -0
  15. package/reference/performance.md +97 -0
  16. package/reference/rendering.md +127 -0
  17. package/reference/routing.md +167 -0
  18. package/reference/typegen.md +64 -0
  19. package/src/api/cache.ts +80 -0
  20. package/src/api/client-cache.ts +9 -0
  21. package/src/api/client-navigation.ts +279 -0
  22. package/src/api/dynamic.tsx +102 -0
  23. package/src/api/link.tsx +119 -0
  24. package/src/api/navigation.ts +198 -0
  25. package/src/api/router/events.ts +53 -0
  26. package/src/api/router/history.ts +70 -0
  27. package/src/api/router/hub.ts +194 -0
  28. package/src/api/router/policies.ts +107 -0
  29. package/src/api/router/runtime.ts +5238 -0
  30. package/src/api/router/types.ts +299 -0
  31. package/src/api/router.ts +167 -0
  32. package/src/api/server.ts +323 -0
  33. package/src/api/suspense.ts +16 -0
  34. package/src/cache/context.ts +61 -0
  35. package/src/cli/adapters/vercel-warm.ts +375 -0
  36. package/src/cli/adapters/vercel.ts +1310 -0
  37. package/src/cli/analyze-print.ts +181 -0
  38. package/src/cli/analyze.ts +328 -0
  39. package/src/cli/boot-trace.ts +29 -0
  40. package/src/cli/build.ts +3114 -0
  41. package/src/cli/dev.ts +276 -0
  42. package/src/cli/index.ts +196 -0
  43. package/src/cli/named-bin.ts +119 -0
  44. package/src/cli/serve-ui.ts +160 -0
  45. package/src/cli/start.ts +1425 -0
  46. package/src/client/build.ts +2136 -0
  47. package/src/client/chunk-fold.ts +526 -0
  48. package/src/client/entry.ts +1525 -0
  49. package/src/client/paths.ts +22 -0
  50. package/src/client/prebuilt.ts +621 -0
  51. package/src/client/profile.ts +75 -0
  52. package/src/client/react-compiler.ts +94 -0
  53. package/src/client/reference-stub.ts +145 -0
  54. package/src/client/reference.ts +47 -0
  55. package/src/compat/actions/action-client.ts +531 -0
  56. package/src/compat/actions/action-dispatch.ts +676 -0
  57. package/src/compat/actions/action-router.ts +40 -0
  58. package/src/compat/actions/action-shared.ts +95 -0
  59. package/src/compat/actions/client-plugin.ts +135 -0
  60. package/src/compat/actions/client-stub.ts +65 -0
  61. package/src/compat/actions/config.ts +164 -0
  62. package/src/compat/actions/detect.ts +208 -0
  63. package/src/compat/actions/discovery.ts +244 -0
  64. package/src/compat/actions/early-submit.ts +40 -0
  65. package/src/compat/actions/endpoint.ts +602 -0
  66. package/src/compat/actions/flight.ts +52 -0
  67. package/src/compat/actions/form-state.ts +73 -0
  68. package/src/compat/actions/hoist.ts +485 -0
  69. package/src/compat/actions/ids.ts +39 -0
  70. package/src/compat/actions/index.ts +41 -0
  71. package/src/compat/actions/instances.ts +144 -0
  72. package/src/compat/actions/origin.ts +109 -0
  73. package/src/compat/actions/protocol.ts +125 -0
  74. package/src/compat/actions/registry.ts +74 -0
  75. package/src/compat/actions/rewrite.ts +282 -0
  76. package/src/compat/actions/serve.ts +414 -0
  77. package/src/compat/actions/server-tag.ts +21 -0
  78. package/src/compat/actions/unrecognized-error.ts +30 -0
  79. package/src/compat/adapter/build-complete.ts +257 -0
  80. package/src/compat/bundler/bun-externals.ts +53 -0
  81. package/src/compat/bundler/cjs-exports.ts +542 -0
  82. package/src/compat/bundler/config.ts +363 -0
  83. package/src/compat/bundler/externals.ts +34 -0
  84. package/src/compat/bundler/import-meta-url.ts +60 -0
  85. package/src/compat/bundler/modularize-imports.ts +119 -0
  86. package/src/compat/bundler/new-url-asset.ts +87 -0
  87. package/src/compat/bundler/optimize-package-imports.ts +273 -0
  88. package/src/compat/bundler/polyfill.ts +88 -0
  89. package/src/compat/bundler/react-compiler.ts +61 -0
  90. package/src/compat/bundler/react-profiler.tsx +25 -0
  91. package/src/compat/bundler/relay-transform.ts +116 -0
  92. package/src/compat/bundler/require-context.ts +281 -0
  93. package/src/compat/bundler/resolve-extensions.ts +75 -0
  94. package/src/compat/bundler/source-cache.ts +61 -0
  95. package/src/compat/bundler/static-imports.ts +25 -0
  96. package/src/compat/bundler/symlink-imports.ts +119 -0
  97. package/src/compat/bundler/tsconfig-paths.ts +50 -0
  98. package/src/compat/bundler/wasm.ts +153 -0
  99. package/src/compat/bundler/webpack-loaders.ts +685 -0
  100. package/src/compat/bundler/worker.ts +278 -0
  101. package/src/compat/cache/build-flags.ts +81 -0
  102. package/src/compat/cache/build-prerender-errors.ts +163 -0
  103. package/src/compat/cache/custom-handler.ts +159 -0
  104. package/src/compat/cache/fetch-patch.ts +745 -0
  105. package/src/compat/cache/handler.ts +100 -0
  106. package/src/compat/cache/modern-handler.ts +275 -0
  107. package/src/compat/cache/resume-data-cache.ts +143 -0
  108. package/src/compat/cache/revalidate.ts +759 -0
  109. package/src/compat/cache/runtime-error.ts +124 -0
  110. package/src/compat/cache/use-cache-transform.ts +961 -0
  111. package/src/compat/cache/use-cache.ts +1695 -0
  112. package/src/compat/cache-control.ts +269 -0
  113. package/src/compat/client/base-path.ts +64 -0
  114. package/src/compat/client/css-order.ts +36 -0
  115. package/src/compat/client/errors/control-flow.ts +92 -0
  116. package/src/compat/client/errors/error-boundary.ts +222 -0
  117. package/src/compat/client/errors/global-error.ts +238 -0
  118. package/src/compat/client/errors/install.ts +217 -0
  119. package/src/compat/client/errors/lazy.ts +53 -0
  120. package/src/compat/client/errors/primitive-throw.ts +126 -0
  121. package/src/compat/client/errors/soft-refresh.ts +14 -0
  122. package/src/compat/client/link-status.ts +86 -0
  123. package/src/compat/client/nav-compat-runtime.ts +57 -0
  124. package/src/compat/client/nav-compat.ts +42 -0
  125. package/src/compat/client/navigation-scroll.ts +154 -0
  126. package/src/compat/client/optimistic-routing.ts +206 -0
  127. package/src/compat/client/prefetch-cache.ts +111 -0
  128. package/src/compat/client/route-announcer.ts +72 -0
  129. package/src/compat/client/segment-cache-policy.ts +159 -0
  130. package/src/compat/client/segment-cache.ts +1077 -0
  131. package/src/compat/client/segment-prefetch.ts +375 -0
  132. package/src/compat/client/trailing-slash.ts +24 -0
  133. package/src/compat/css/chunking.ts +254 -0
  134. package/src/compat/css/inline-css.ts +73 -0
  135. package/src/compat/css/lightningcss.ts +90 -0
  136. package/src/compat/css/modules.ts +373 -0
  137. package/src/compat/css/nonce.ts +30 -0
  138. package/src/compat/css/sass-plugin.ts +65 -0
  139. package/src/compat/css/sass.ts +392 -0
  140. package/src/compat/css/styled-jsx-runtime.ts +80 -0
  141. package/src/compat/css/styled-jsx.ts +49 -0
  142. package/src/compat/edge-runtime.ts +71 -0
  143. package/src/compat/export/client.ts +112 -0
  144. package/src/compat/export/index.ts +272 -0
  145. package/src/compat/export/standalone.ts +207 -0
  146. package/src/compat/image-optimizer/cache.ts +119 -0
  147. package/src/compat/image-optimizer/detect.ts +143 -0
  148. package/src/compat/image-optimizer/index.ts +601 -0
  149. package/src/compat/image-optimizer/source.ts +243 -0
  150. package/src/compat/index.ts +458 -0
  151. package/src/compat/lifecycle/after-scope.ts +86 -0
  152. package/src/compat/lifecycle/after.ts +173 -0
  153. package/src/compat/lifecycle/error-funnel.ts +306 -0
  154. package/src/compat/lifecycle/error-serialize.ts +87 -0
  155. package/src/compat/lifecycle/error-ui.ts +167 -0
  156. package/src/compat/lifecycle/instrumentation-client.ts +138 -0
  157. package/src/compat/lifecycle/instrumentation.ts +277 -0
  158. package/src/compat/lifecycle/node-console.ts +19 -0
  159. package/src/compat/lifecycle/testmode.ts +263 -0
  160. package/src/compat/mdx/compile.ts +219 -0
  161. package/src/compat/mdx/next-mdx-stub.ts +46 -0
  162. package/src/compat/mdx/plugin.ts +37 -0
  163. package/src/compat/metadata-route-artifacts.ts +458 -0
  164. package/src/compat/metadata.ts +295 -0
  165. package/src/compat/middleware/manifest.ts +210 -0
  166. package/src/compat/misc/action-return.ts +173 -0
  167. package/src/compat/next/cache.ts +211 -0
  168. package/src/compat/next/canonical-url.ts +35 -0
  169. package/src/compat/next/client-cache.ts +57 -0
  170. package/src/compat/next/client-navigation.ts +313 -0
  171. package/src/compat/next/client-only.ts +3 -0
  172. package/src/compat/next/client-script.tsx +215 -0
  173. package/src/compat/next/client-server.ts +39 -0
  174. package/src/compat/next/config-loader.ts +569 -0
  175. package/src/compat/next/config.ts +29 -0
  176. package/src/compat/next/constants.cjs +6 -0
  177. package/src/compat/next/constants.ts +6 -0
  178. package/src/compat/next/custom-server.ts +236 -0
  179. package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
  180. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
  181. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
  182. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
  183. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
  184. package/src/compat/next/dynamic.tsx +46 -0
  185. package/src/compat/next/error.tsx +148 -0
  186. package/src/compat/next/font/cache.ts +171 -0
  187. package/src/compat/next/font/google.ts +2 -0
  188. package/src/compat/next/font/index.ts +8 -0
  189. package/src/compat/next/font/local.ts +5 -0
  190. package/src/compat/next/font/runtime-client.ts +71 -0
  191. package/src/compat/next/font/runtime.ts +974 -0
  192. package/src/compat/next/font/shared.ts +281 -0
  193. package/src/compat/next/form.tsx +156 -0
  194. package/src/compat/next/head.tsx +10 -0
  195. package/src/compat/next/headers.ts +247 -0
  196. package/src/compat/next/image/config.ts +196 -0
  197. package/src/compat/next/image/optimizer.ts +96 -0
  198. package/src/compat/next/image/patterns.ts +103 -0
  199. package/src/compat/next/image/shared.ts +141 -0
  200. package/src/compat/next/image/static-metadata.ts +283 -0
  201. package/src/compat/next/image/validate.ts +269 -0
  202. package/src/compat/next/image-client.tsx +215 -0
  203. package/src/compat/next/image-props.ts +575 -0
  204. package/src/compat/next/image-usage.ts +102 -0
  205. package/src/compat/next/image.tsx +56 -0
  206. package/src/compat/next/index.ts +1 -0
  207. package/src/compat/next/legacy-image.tsx +97 -0
  208. package/src/compat/next/link-usage.ts +29 -0
  209. package/src/compat/next/link-validation-transform.ts +200 -0
  210. package/src/compat/next/link.tsx +466 -0
  211. package/src/compat/next/navigation.cjs +21 -0
  212. package/src/compat/next/navigation.ts +188 -0
  213. package/src/compat/next/offline.ts +51 -0
  214. package/src/compat/next/og.ts +324 -0
  215. package/src/compat/next/optimistic-route-state.ts +188 -0
  216. package/src/compat/next/preferred-region.ts +39 -0
  217. package/src/compat/next/redirects.ts +131 -0
  218. package/src/compat/next/resource-hints.ts +136 -0
  219. package/src/compat/next/rewrites.ts +350 -0
  220. package/src/compat/next/root-params.ts +142 -0
  221. package/src/compat/next/router.cjs +49 -0
  222. package/src/compat/next/router.ts +143 -0
  223. package/src/compat/next/script.tsx +355 -0
  224. package/src/compat/next/server-only.ts +3 -0
  225. package/src/compat/next/server.ts +28 -0
  226. package/src/compat/next/svgr.ts +58 -0
  227. package/src/compat/next/telemetry.ts +77 -0
  228. package/src/compat/next/user-agent.ts +100 -0
  229. package/src/compat/next/web-vitals.ts +56 -0
  230. package/src/compat/otel/api.ts +95 -0
  231. package/src/compat/otel/client-trace-metadata.ts +71 -0
  232. package/src/compat/otel/fetch-span.ts +77 -0
  233. package/src/compat/otel/tracer.ts +944 -0
  234. package/src/compat/pages/client-plugin.ts +108 -0
  235. package/src/compat/pages/index.ts +527 -0
  236. package/src/compat/pages/router-state.ts +94 -0
  237. package/src/compat/ppr/io.ts +38 -0
  238. package/src/compat/ppr/missing-root-params.ts +105 -0
  239. package/src/compat/ppr/root-params-scan.ts +164 -0
  240. package/src/compat/ppr/root-params-transform.ts +75 -0
  241. package/src/compat/ppr/root-params.ts +129 -0
  242. package/src/compat/ppr/segment-config-incompat.ts +34 -0
  243. package/src/compat/protocol.ts +202 -0
  244. package/src/compat/react/client.ts +59 -0
  245. package/src/compat/react/compiler-runtime.ts +60 -0
  246. package/src/compat/react/dom-client.ts +115 -0
  247. package/src/compat/react/dom-react-server.ts +20 -0
  248. package/src/compat/react/dom-server.ts +40 -0
  249. package/src/compat/react/dom.ts +154 -0
  250. package/src/compat/react/preact.ts +522 -0
  251. package/src/compat/react/react-server.ts +84 -0
  252. package/src/compat/react/router-shim.ts +26 -0
  253. package/src/compat/react/server-component-use.ts +48 -0
  254. package/src/compat/react/server-inserted-html.ts +87 -0
  255. package/src/compat/react/server.ts +156 -0
  256. package/src/compat/react/view-transition.ts +60 -0
  257. package/src/compat/register/actions.ts +875 -0
  258. package/src/compat/register/boot.ts +141 -0
  259. package/src/compat/register/build-tier.ts +11 -0
  260. package/src/compat/register/build.ts +182 -0
  261. package/src/compat/register/bundler.ts +587 -0
  262. package/src/compat/register/cache.ts +85 -0
  263. package/src/compat/register/client-errors.ts +18 -0
  264. package/src/compat/register/config.ts +17 -0
  265. package/src/compat/register/css-extras.ts +120 -0
  266. package/src/compat/register/edge-runtime.ts +6 -0
  267. package/src/compat/register/errors.ts +46 -0
  268. package/src/compat/register/export.ts +23 -0
  269. package/src/compat/register/font.ts +36 -0
  270. package/src/compat/register/hooks.ts +34 -0
  271. package/src/compat/register/image.ts +133 -0
  272. package/src/compat/register/index.ts +111 -0
  273. package/src/compat/register/instrumentation-client.ts +35 -0
  274. package/src/compat/register/lifecycle.ts +86 -0
  275. package/src/compat/register/mdx.ts +48 -0
  276. package/src/compat/register/middleware.ts +36 -0
  277. package/src/compat/register/misc.ts +44 -0
  278. package/src/compat/register/otel.ts +288 -0
  279. package/src/compat/register/pages-api.ts +473 -0
  280. package/src/compat/register/ppr.ts +56 -0
  281. package/src/compat/register/protocol.ts +57 -0
  282. package/src/compat/register/proxy.ts +127 -0
  283. package/src/compat/register/render.ts +268 -0
  284. package/src/compat/register/routing.ts +410 -0
  285. package/src/compat/register/segment.ts +1903 -0
  286. package/src/compat/register/static-image.ts +21 -0
  287. package/src/compat/register/typed-routes.ts +35 -0
  288. package/src/compat/register/usecache.ts +131 -0
  289. package/src/compat/register/validation.ts +56 -0
  290. package/src/compat/segment/loading-boundary.ts +113 -0
  291. package/src/compat/segment/page-slot.ts +200 -0
  292. package/src/compat/segment/tree.ts +481 -0
  293. package/src/compat/segment/vary-key.ts +102 -0
  294. package/src/compat/segment/vary-params.ts +551 -0
  295. package/src/compat/static-params.ts +33 -0
  296. package/src/compat/tsconfig-defaults.ts +301 -0
  297. package/src/compat/typecheck/index.ts +1481 -0
  298. package/src/compat/typecheck/worker.ts +26 -0
  299. package/src/compat/typed-routes/index.ts +92 -0
  300. package/src/compat/typed-routes/manifest.ts +356 -0
  301. package/src/compat/typed-routes/typegen.ts +566 -0
  302. package/src/compat/validation/errors.ts +159 -0
  303. package/src/compat/validation/index.ts +1770 -0
  304. package/src/compat/validation/prerender-diagnostics.ts +1508 -0
  305. package/src/compat-bootstrap.ts +67 -0
  306. package/src/config.ts +218 -0
  307. package/src/css/build.ts +697 -0
  308. package/src/css/index.ts +2 -0
  309. package/src/css/postcss.ts +236 -0
  310. package/src/css/worker.ts +34 -0
  311. package/src/dev/client-actions.ts +35 -0
  312. package/src/dev/client-chunk-store.ts +92 -0
  313. package/src/dev/client-key-cache.ts +178 -0
  314. package/src/dev/global-css-cache.ts +212 -0
  315. package/src/dev/imports.ts +2430 -0
  316. package/src/dev/module-cache.ts +721 -0
  317. package/src/dev/module-generations.ts +38 -0
  318. package/src/dev/module-transform.ts +188 -0
  319. package/src/dev/node-module-bundle-cache.ts +63 -0
  320. package/src/dev/restart-cache.ts +10 -0
  321. package/src/dev/route-bundle-key-cache.ts +154 -0
  322. package/src/dev/route-facts-cache.ts +223 -0
  323. package/src/dev/server.ts +1710 -0
  324. package/src/dynamic/source.ts +307 -0
  325. package/src/dynamic/tree-shake.ts +262 -0
  326. package/src/env.ts +92 -0
  327. package/src/extensions.ts +1898 -0
  328. package/src/index.ts +34 -0
  329. package/src/internal.ts +43 -0
  330. package/src/islands/boundary-error.ts +8 -0
  331. package/src/islands/static-children.ts +37 -0
  332. package/src/islands/static-slots.ts +106 -0
  333. package/src/ppr-postpone.ts +24 -0
  334. package/src/ppr.ts +784 -0
  335. package/src/proxy.ts +752 -0
  336. package/src/render/hooks.ts +384 -0
  337. package/src/render/index.ts +1 -0
  338. package/src/render/island-context.ts +47 -0
  339. package/src/render/metadata.ts +857 -0
  340. package/src/render/renderer.ts +7391 -0
  341. package/src/render/resource-hints.ts +44 -0
  342. package/src/render/slots.tsx +679 -0
  343. package/src/request/context.ts +396 -0
  344. package/src/resolve/engine.ts +219 -0
  345. package/src/resolve/imports.ts +1104 -0
  346. package/src/resolve/scan-facts.ts +474 -0
  347. package/src/resolve/source-text.ts +86 -0
  348. package/src/routing/forwarded.ts +41 -0
  349. package/src/routing/handler.ts +271 -0
  350. package/src/routing/href.ts +203 -0
  351. package/src/routing/metadata.ts +1018 -0
  352. package/src/routing/request-runtime.ts +43 -0
  353. package/src/routing/routes.ts +2560 -0
  354. package/src/routing/slots.ts +432 -0
  355. package/src/runtime/server.ts +3453 -0
  356. package/src/runtime/vendor.ts +1160 -0
  357. package/src/style-modules.d.ts +9 -0
  358. package/src/typegen.ts +151 -0
  359. package/src/types.ts +725 -0
  360. package/src/utils/ansi.ts +9 -0
  361. package/src/utils/content-type.ts +31 -0
  362. package/src/utils/decode.ts +7 -0
  363. package/src/utils/dev-profile.ts +31 -0
  364. package/src/utils/error-log.ts +29 -0
  365. package/src/utils/fs-cache.ts +31 -0
  366. package/src/utils/fs.ts +119 -0
  367. package/src/utils/html.ts +46 -0
  368. package/src/utils/serialize.ts +378 -0
  369. package/src/utils/source.ts +35 -0
  370. package/src/utils/verbose.ts +39 -0
  371. package/tsconfig.json +10 -0
@@ -0,0 +1,1104 @@
1
+ import { existsSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import {
4
+ probeFile,
5
+ probePackageTarget,
6
+ resolveFrom,
7
+ setEngineExtensions,
8
+ setEngineTsConfigPath,
9
+ splitResourceQuery,
10
+ tsConfigFileFor,
11
+ } from './engine';
12
+ import { onExtensionHostReset } from '../extensions';
13
+
14
+ // Conditions for "is this a local source file" lookups. Ordered to mirror the
15
+ // preference a bundler applies, though `exports` key order ultimately decides.
16
+ const localConditions = ['source', 'import', 'browser', 'node', 'default', 'require'];
17
+
18
+ /**
19
+ * Override the resolver's extension probe order (next.config `resolveExtensions`).
20
+ * Pass undefined (or []) to restore the built-in source-extension order.
21
+ */
22
+ export function setResolveExtensions(extensions: readonly string[] | undefined): void {
23
+ setEngineExtensions(extensions);
24
+ clearVendorPackageResolutions();
25
+ }
26
+
27
+ // B7 (serverExternalPackages / transpilePackages): the node_modules bundle-vs-
28
+ // external decision is driven by next.config, which lives in compat. Core keeps
29
+ // no static edge into compat, so compat registers the policy here (a setter) and
30
+ // core's server compile sites (dev/imports.ts, runtime/server.ts) consult it.
31
+ export interface ExternalPackagePolicy {
32
+ /** Force-external (never bundle) — serverExternalPackages + built-ins. */
33
+ external: (packageName: string) => boolean;
34
+ /** Force-bundle + transpile (incl. node_modules TS/CSS/font) — transpilePackages. */
35
+ transpile: (packageName: string) => boolean;
36
+ /**
37
+ * The same rule as a LIST, where one exists: bundler filters need names up front, and probing
38
+ * `transpile` per installed package is expensive. Absent, callers must assume any package may
39
+ * transpile.
40
+ */
41
+ transpiled?: () => readonly string[];
42
+ /** ESM external mode for package exports interop. */
43
+ esmExternals: () => boolean | 'loose';
44
+ }
45
+
46
+ export type ExternalLoadTarget = 'server' | 'edge' | 'client' | 'client-ssr';
47
+
48
+ export interface ExternalLoadContext {
49
+ root: string;
50
+ fromFile: string;
51
+ specifier: string;
52
+ target: ExternalLoadTarget;
53
+ }
54
+
55
+ export type ExternalLoadResolver = (context: ExternalLoadContext) => string | undefined;
56
+
57
+ const noExternalPolicy: ExternalPackagePolicy = {
58
+ external: () => false,
59
+ transpile: () => false,
60
+ esmExternals: () => true,
61
+ };
62
+ let externalPackagePolicy: ExternalPackagePolicy = noExternalPolicy;
63
+ let externalLoadResolver: ExternalLoadResolver | undefined;
64
+
65
+ /** Install the compat-driven external/transpile package policy (see B7). */
66
+ export function setExternalPackagePolicy(policy: ExternalPackagePolicy | undefined): void {
67
+ externalPackagePolicy = policy ?? noExternalPolicy;
68
+ }
69
+
70
+ export function getExternalPackagePolicy(): ExternalPackagePolicy {
71
+ return externalPackagePolicy;
72
+ }
73
+
74
+ /** Install an optional concrete-file resolver for external runtime loads. */
75
+ export function setExternalLoadResolver(resolver: ExternalLoadResolver | undefined): void {
76
+ externalLoadResolver = resolver;
77
+ externalLoadTargets.clear();
78
+ }
79
+
80
+ // Every bare import evaluated by the server runtime reaches the resolve hook, and an external one costs a
81
+ // full oxc package resolve there. Keyed on the RESOLUTION ROOT - the only thing the resolver reads out of
82
+ // `fromFile` - for the reason `resolveVendorPackageSpecifier` documents: keying on the importer misses on
83
+ // nearly every call.
84
+ const externalLoadTargets = new Map<string, string | undefined>();
85
+
86
+ export function resolveExternalLoadTarget(context: ExternalLoadContext): string | undefined {
87
+ if (!externalLoadResolver) return undefined;
88
+ // `#imports` resolve from the importer's OWN package, so they are not
89
+ // shareable across a resolution root; they stay uncached.
90
+ if (context.specifier.startsWith('#')) return externalLoadResolver(context);
91
+ const key = `${context.target}\0${resolutionRootForFile(context.root, context.fromFile)}\0${context.specifier}`;
92
+ if (externalLoadTargets.has(key)) return externalLoadTargets.get(key);
93
+ const target = externalLoadResolver(context);
94
+ externalLoadTargets.set(key, target);
95
+ return target;
96
+ }
97
+
98
+ /** The npm package name owning a bare specifier (`foo/bar` -> `foo`, scoped ok). */
99
+ export function packageNameOfSpecifier(specifier: string): string | undefined {
100
+ return packageNameFromSpecifier(specifier);
101
+ }
102
+
103
+ export function packageNameForFile(file: string): string | undefined {
104
+ const packageRoot = nearestPackageRoot(path.dirname(file));
105
+ return packageRoot ? readPackageJson(packageRoot).name : undefined;
106
+ }
107
+
108
+ export function externalPackageImportTarget(
109
+ root: string,
110
+ fromFile: string,
111
+ specifier: string,
112
+ ): string | undefined {
113
+ const packageName = packageNameFromSpecifier(specifier);
114
+ if (!packageName) return undefined;
115
+ const policy = getExternalPackagePolicy();
116
+ if (!policy.external(packageName) || policy.esmExternals() !== true) return undefined;
117
+ const imported = resolvePackageSpecifier(root, fromFile, specifier, ['import', 'node', 'default']);
118
+ if (!imported || isEsmModuleFile(imported)) return undefined;
119
+ const required = resolvePackageSpecifier(root, fromFile, specifier, ['require', 'node', 'default']);
120
+ return required && required !== imported ? required : undefined;
121
+ }
122
+
123
+ interface TsConfig {
124
+ compilerOptions?: {
125
+ baseUrl?: string;
126
+ paths?: Record<string, string[]>;
127
+ };
128
+ }
129
+
130
+ export function setTsConfigPath(file: string | undefined): void {
131
+ setEngineTsConfigPath(file);
132
+ tsConfigCache.clear();
133
+ clearVendorPackageResolutions();
134
+ }
135
+
136
+ interface PackageJson {
137
+ name?: string;
138
+ type?: string;
139
+ browser?: string;
140
+ main?: string;
141
+ module?: string;
142
+ source?: string;
143
+ exports?: PackageExport;
144
+ imports?: Record<string, PackageExport>;
145
+ dependencies?: Record<string, string>;
146
+ devDependencies?: Record<string, string>;
147
+ optionalDependencies?: Record<string, string>;
148
+ peerDependencies?: Record<string, string>;
149
+ workspaces?: string[] | { packages?: string[] };
150
+ }
151
+
152
+ // Compat (or any bundler extension) registers its bare-specifier to file aliases here (e.g. `next/form` ->
153
+ // `src/compat/next/form.tsx`). Core's own route scanner has no static knowledge of those aliases otherwise,
154
+ // so a server component importing a 'use client' compat module would never be walked into and its client
155
+ // reference would silently be dropped from the page's bundle.
156
+ //
157
+ // Deliberately NOT folded into resolveImport itself: that function has many callers, most of which treat
158
+ // "resolves to a real file" as "is a local, in-root source file", and an alias pointing outside the app root
159
+ // would misfire those assumptions. Only the route scanner's own walk needs this fallback.
160
+ let extraModuleAliases: Record<string, string> | undefined;
161
+ // Compat registers these alongside the extension host, so they clear with it.
162
+ onExtensionHostReset(() => {
163
+ extraModuleAliases = undefined;
164
+ clearVendorPackageResolutions();
165
+ });
166
+
167
+ /** Register extra bare-specifier -> absolute-file aliases for the route scanner. */
168
+ export function setModuleAliases(aliases: Record<string, string> | undefined): void {
169
+ extraModuleAliases = aliases;
170
+ }
171
+
172
+ /** Bare-specifier alias lookup registered via setModuleAliases, if any. */
173
+ export function resolveModuleAlias(specifier: string): string | undefined {
174
+ const aliased = extraModuleAliases?.[specifier];
175
+ return aliased ? probeFile(aliased, localConditions) : undefined;
176
+ }
177
+
178
+ /**
179
+ * Resolve a specifier to a LOCAL source file, or undefined when it belongs to
180
+ * an external package. Callers read undefined as "leave this to node/esbuild".
181
+ */
182
+ export function resolveImport(root: string, fromFile: string, specifier: string) {
183
+ // A `?query`/`#fragment` resource belongs to its loader chain (turbopack
184
+ // rules, `.wasm?module`): those plugins strip the suffix and resolve the base
185
+ // themselves, and the query is part of the module's identity — so it is never
186
+ // a local resolution here, and the specifier reaches them untouched.
187
+ if (splitResourceQuery(specifier).query) return undefined;
188
+
189
+ // realpath: a resolution reached through a symlinked shim dir (pages-compat)
190
+ // must collapse to the real app file, or downstream workspace-membership
191
+ // checks misclassify it as external and leave the specifier verbatim.
192
+ if (specifier.startsWith('.')) {
193
+ return resolveLocalFile(path.dirname(realImportPath(fromFile)), specifier);
194
+ }
195
+
196
+ if (path.isAbsolute(specifier)) return probeFile(specifier, localConditions);
197
+
198
+ const resolutionRoot = resolutionRootForFile(root, fromFile);
199
+
200
+ const tsResolved = resolveTsPath(resolutionRoot, specifier);
201
+ if (tsResolved) return realImportPath(tsResolved);
202
+
203
+ if (specifier.startsWith('#')) {
204
+ const resolved = resolveLocalFile(resolutionRoot, specifier);
205
+ return resolved ? realImportPath(resolved) : undefined;
206
+ }
207
+
208
+ const workspaceResolved = resolveWorkspacePackageImport(root, specifier);
209
+ if (workspaceResolved) return workspaceResolved;
210
+
211
+ return resolveAppTreePackage(resolutionRoot, root, fromFile, specifier);
212
+ }
213
+
214
+ function resolveLocalFile(dir: string, specifier: string) {
215
+ const outcome = resolveFrom(dir, specifier, localConditions);
216
+ return outcome && 'path' in outcome ? outcome.path : undefined;
217
+ }
218
+
219
+ // Every specifier of a file re-resolves that file's real path - one syscall per import, now one per
220
+ // path. Only successes are cached: a path that does not exist yet may resolve differently once the
221
+ // watcher creates it.
222
+ const realImportPathCache = new Map<string, string>();
223
+
224
+ function realImportPath(file: string) {
225
+ const cached = realImportPathCache.get(file);
226
+ if (cached !== undefined) return cached;
227
+ try {
228
+ const real = realpathSync(file);
229
+ realImportPathCache.set(file, real);
230
+ return real;
231
+ } catch {
232
+ return file;
233
+ }
234
+ }
235
+
236
+ // A package vendored inside the app tree (`app/x/node_modules/my-dep`) is local source the bundler and
237
+ // scanner must walk into; the project's own node_modules, or anything above it, stays external and resolves
238
+ // at runtime. One resolution per (root, directory, specifier) instead of one per import of it: every module
239
+ // of a route's bundle asks about the same handful of app-tree packages, and each ask is a full oxc resolve.
240
+ // Successes only, exactly as `realImportPath` and `resolveRuntimeSpecifier` do.
241
+ const appTreeResolveCache = new Map<string, string>();
242
+ // Bisect seam: `PNEXT_RESOLVE_APPTREE_MEMO=0` restores the resolve-per-import path.
243
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
244
+ const memoAppTree = process.env.PNEXT_RESOLVE_APPTREE_MEMO !== '0';
245
+
246
+ function resolveAppTreePackage(
247
+ resolutionRoot: string,
248
+ root: string,
249
+ fromFile: string,
250
+ specifier: string,
251
+ ) {
252
+ const from = path.dirname(path.resolve(fromFile));
253
+ const key = `${resolutionRoot}\0${root}\0${from}\0${specifier}`;
254
+ if (memoAppTree) {
255
+ const cached = appTreeResolveCache.get(key);
256
+ if (cached !== undefined) return cached;
257
+ }
258
+ const outcome = resolveFrom(from, specifier, localConditions, resolutionRoot);
259
+ if (!outcome || !('path' in outcome)) return undefined;
260
+ const resolved = isAppTreeFile(path.resolve(root), outcome.path) ? outcome.path : undefined;
261
+ if (memoAppTree && resolved !== undefined) appTreeResolveCache.set(key, resolved);
262
+ return resolved;
263
+ }
264
+
265
+ function isAppTreeFile(root: string, file: string) {
266
+ const relative = path.relative(root, file);
267
+ if (relative.startsWith('..') || path.isAbsolute(relative)) return false;
268
+ const nodeModules = relative.split(path.sep).indexOf('node_modules');
269
+ return nodeModules !== 0;
270
+ }
271
+
272
+ /**
273
+ * Resolve a bare specifier ONLY through the configured tsconfig `paths` (and `baseUrl`), returning the
274
+ * absolute source file or undefined. Unlike resolveImport, this never falls through to node_modules,
275
+ * workspace or package lookups - callers such as the client esbuild resolver want tsconfig `paths` to win
276
+ * but everything else to stay on esbuild's native resolution.
277
+ */
278
+ export function resolveConfiguredTsPath(
279
+ root: string,
280
+ fromFile: string,
281
+ specifier: string,
282
+ ): string | undefined {
283
+ if (specifier.startsWith('.') || specifier.startsWith('#') || path.isAbsolute(specifier)) {
284
+ return undefined;
285
+ }
286
+ return resolveTsPath(resolutionRootForFile(root, fromFile), specifier);
287
+ }
288
+
289
+ /**
290
+ * Every `paths` pattern declared by a tsconfig `resolveConfiguredTsPath` can
291
+ * reach from this app: its own root, the workspace root, and each workspace
292
+ * member (an importer outside the app root resolves against its nearest package
293
+ * root). Lets a caller pre-filter specifiers instead of asking per import.
294
+ */
295
+ export function configuredTsPathPatterns(root: string, workspaceRoot?: string): string[] {
296
+ const roots = new Set([path.resolve(root)]);
297
+ if (workspaceRoot) {
298
+ const resolved = path.resolve(workspaceRoot);
299
+ roots.add(resolved);
300
+ for (const packageRoot of workspacePackages(resolved).values()) roots.add(packageRoot);
301
+ }
302
+ const patterns = new Set<string>();
303
+ for (const dir of roots) {
304
+ for (const pattern of Object.keys(readTsConfig(dir).compilerOptions?.paths ?? {})) {
305
+ patterns.add(pattern);
306
+ }
307
+ }
308
+ return [...patterns];
309
+ }
310
+
311
+ function resolveTsPath(root: string, specifier: string) {
312
+ const compilerOptions = readTsConfig(root).compilerOptions ?? {};
313
+ const baseUrl = path.resolve(root, compilerOptions.baseUrl ?? '.');
314
+
315
+ for (const [pattern, targets] of Object.entries(compilerOptions.paths ?? {})) {
316
+ const match = matchPattern(pattern, specifier);
317
+ if (!match) continue;
318
+ for (const target of targets) {
319
+ const resolved = probeFile(path.resolve(baseUrl, applyPattern(target, match)), localConditions);
320
+ if (resolved) return resolved;
321
+ }
322
+ }
323
+
324
+ return undefined;
325
+ }
326
+
327
+ type PackageExport = unknown;
328
+
329
+ /**
330
+ * Resolve a sibling workspace package to its RAW first-party source - the `source`/`src` entries and the
331
+ * on-disk subpath fallback no standard resolver takes, because the published `exports` point at build output
332
+ * that does not exist in a checkout.
333
+ */
334
+ function resolveWorkspacePackageImport(root: string, specifier: string) {
335
+ const workspaceRoot = findWorkspaceRoot(root);
336
+ if (!workspaceRoot) return undefined;
337
+
338
+ const packageName = packageNameFromSpecifier(specifier);
339
+ if (!packageName) return undefined;
340
+
341
+ const packageRoot = workspacePackages(workspaceRoot).get(packageName);
342
+ if (!packageRoot) return undefined;
343
+
344
+ const packageJson = readPackageJson(packageRoot);
345
+ const subpath = subpathOfSpecifier(packageName, specifier);
346
+ const exported = packageExportTarget(packageJson.exports, subpath);
347
+ if (exported) {
348
+ const resolved = probePackageTarget(path.resolve(packageRoot, exported), localConditions);
349
+ if (resolved && isInside(packageRoot, resolved)) return resolved;
350
+ }
351
+
352
+ const onDisk = onDiskSubpath(packageRoot, subpath, localConditions);
353
+ if (onDisk) return onDisk;
354
+
355
+ for (const entry of [packageJson.source, packageJson.module, packageJson.main, './src/index', './index']) {
356
+ if (!entry) continue;
357
+ const resolved = probePackageTarget(path.resolve(packageRoot, entry), localConditions);
358
+ if (resolved) return resolved;
359
+ }
360
+
361
+ return undefined;
362
+ }
363
+
364
+ function packageExportTarget(exports: PackageJson['exports'], subpath: string) {
365
+ if (!exports) return undefined;
366
+ if (!isSubpathExports(exports)) return subpath === '.' ? packageTargetPath(exports) : undefined;
367
+
368
+ for (const [pattern, target] of Object.entries(exports)) {
369
+ const match = matchPattern(pattern, subpath);
370
+ if (!match) continue;
371
+ const targetPath = packageTargetPath(target);
372
+ if (targetPath) return applyPattern(targetPath, match);
373
+ }
374
+
375
+ return undefined;
376
+ }
377
+
378
+ function packageExportTargetForConditions(
379
+ exports: PackageJson['exports'],
380
+ subpath: string,
381
+ conditions: readonly string[],
382
+ ): string | undefined {
383
+ if (!exports) return undefined;
384
+ if (!isSubpathExports(exports)) {
385
+ return subpath === '.' ? packageTargetPathForConditions(exports, conditions) : undefined;
386
+ }
387
+
388
+ for (const [pattern, target] of Object.entries(exports)) {
389
+ const match = matchPattern(pattern, subpath);
390
+ if (!match) continue;
391
+ const targetPath = packageTargetPathForConditions(target, conditions);
392
+ if (targetPath) return applyPattern(targetPath, match);
393
+ }
394
+
395
+ return undefined;
396
+ }
397
+
398
+ function packageTargetPathForConditions(
399
+ target: unknown,
400
+ conditions: readonly string[],
401
+ ): string | undefined {
402
+ if (!target) return undefined;
403
+ if (typeof target === 'string') return target;
404
+ if (Array.isArray(target)) {
405
+ for (const item of target) {
406
+ const resolved = packageTargetPathForConditions(item, conditions);
407
+ if (resolved) return resolved;
408
+ }
409
+ return undefined;
410
+ }
411
+
412
+ if (!isPackageExportObject(target)) return undefined;
413
+
414
+ for (const [key, value] of Object.entries(target)) {
415
+ if (key !== 'default' && !conditions.includes(key)) continue;
416
+ const resolved = packageTargetPathForConditions(value, conditions);
417
+ if (resolved) return resolved;
418
+ }
419
+
420
+ return undefined;
421
+ }
422
+
423
+ function isSubpathExports(exports: PackageExport): exports is Record<string, unknown> {
424
+ return isPackageExportObject(exports) && Object.keys(exports).some(key => key.startsWith('.'));
425
+ }
426
+
427
+ function packageTargetPath(target: unknown): string | undefined {
428
+ if (!target) return undefined;
429
+ if (typeof target === 'string') return target;
430
+ if (Array.isArray(target)) {
431
+ for (const item of target) {
432
+ const resolved = packageTargetPath(item);
433
+ if (resolved) return resolved;
434
+ }
435
+ return undefined;
436
+ }
437
+
438
+ if (!isPackageExportObject(target)) return undefined;
439
+
440
+ for (const key of ['source', 'import', 'browser', 'node', 'default', 'types', 'require']) {
441
+ const resolved = packageTargetPath(target[key]);
442
+ if (resolved) return resolved;
443
+ }
444
+
445
+ for (const value of Object.values(target)) {
446
+ const resolved = packageTargetPath(value);
447
+ if (resolved) return resolved;
448
+ }
449
+
450
+ return undefined;
451
+ }
452
+
453
+ function isPackageExportObject(target: unknown): target is Record<string, unknown> {
454
+ return typeof target === 'object' && target !== null && !Array.isArray(target);
455
+ }
456
+
457
+ function matchPattern(pattern: string, specifier: string) {
458
+ if (pattern === specifier) return { wildcard: '' };
459
+ const star = pattern.indexOf('*');
460
+ if (star === -1) return undefined;
461
+ const prefix = pattern.slice(0, star);
462
+ const suffix = pattern.slice(star + 1);
463
+ if (!specifier.startsWith(prefix) || !specifier.endsWith(suffix)) return undefined;
464
+ return { wildcard: specifier.slice(prefix.length, specifier.length - suffix.length) };
465
+ }
466
+
467
+ function applyPattern(pattern: string, match: { wildcard: string }) {
468
+ return pattern.replace('*', match.wildcard);
469
+ }
470
+
471
+ function packageNameFromSpecifier(specifier: string) {
472
+ const parts = specifier.split('/');
473
+ if (specifier.startsWith('@')) return parts.length >= 2 ? `${parts[0]}/${parts[1]}` : undefined;
474
+ return parts[0] || undefined;
475
+ }
476
+
477
+ function subpathOfSpecifier(packageName: string, specifier: string) {
478
+ return specifier === packageName ? '.' : `.${specifier.slice(packageName.length)}`;
479
+ }
480
+
481
+ /**
482
+ * The directory a bare specifier resolves from — exported so callers can dedupe
483
+ * repeat resolutions on exactly the inputs the resolver reads.
484
+ */
485
+ export function resolutionRootForFile(root: string, fromFile: string) {
486
+ const resolvedRoot = path.resolve(root);
487
+ const resolvedFile = path.resolve(fromFile);
488
+ if (isInside(resolvedRoot, resolvedFile)) return resolvedRoot;
489
+ return nearestPackageRoot(path.dirname(resolvedFile)) ?? resolvedRoot;
490
+ }
491
+
492
+ /**
493
+ * Resolve a bare specifier as the IMPORTING package sees it: from the importer's REAL path, never collapsed
494
+ * to the app root, so a transitive dependency pinned to a version distinct from the hoisted root copy (pnpm
495
+ * virtual store, npm nesting) resolves to that nested copy. The realpath step is load-bearing under pnpm -
496
+ * the symlinked `node_modules/<pkg>` has no sibling deps, but its `.pnpm/<pkg>@ver` real path does. Used only
497
+ * by the server-external vendoring path.
498
+ */
499
+ export function resolveNestedPackageFromImporter(
500
+ importerFile: string,
501
+ specifier: string,
502
+ conditions: readonly string[],
503
+ ): string | undefined {
504
+ const packageName = packageNameFromSpecifier(specifier);
505
+ if (!packageName) return undefined;
506
+ const from = path.dirname(realImportPath(path.resolve(importerFile)));
507
+ return realLinkedPackageFile(resolvePackageFrom(from, packageName, specifier, conditions));
508
+ }
509
+
510
+ /**
511
+ * Memoized per directory: the walk is an `existsSync` per level, and every
512
+ * resolve, every `isEsmModuleFile` and every vendor build asks it about the
513
+ * same few thousand node_modules directories.
514
+ */
515
+ const packageRoots = new Map<string, string | undefined>();
516
+
517
+ function nearestPackageRoot(fromDir: string) {
518
+ const start = path.resolve(fromDir);
519
+ if (packageRoots.has(start)) return packageRoots.get(start);
520
+ let dir = start;
521
+ const walked: string[] = [];
522
+ let found: string | undefined;
523
+ while (true) {
524
+ walked.push(dir);
525
+ // Plain existsSync: the walk is memoized per dir, a dirent listing of large
526
+ // node_modules dirs costs more than the one probe it would save.
527
+ if (existsSync(path.join(dir, 'package.json'))) {
528
+ found = dir;
529
+ break;
530
+ }
531
+ const parent = path.dirname(dir);
532
+ if (parent === dir) break;
533
+ dir = parent;
534
+ }
535
+ for (const seen of walked) packageRoots.set(seen, found);
536
+ return found;
537
+ }
538
+
539
+ /**
540
+ * Entry resolutions the vendor build already computed, handed to the CJS-interop
541
+ * plugin so its entry-point onResolve never re-resolves a bare specifier.
542
+ */
543
+ const vendorEntryProvisions = new Map<string, string>();
544
+
545
+ export function provideEntryResolution(specifier: string, resolveDir: string, resolved: string) {
546
+ vendorEntryProvisions.set(`${specifier}\0${resolveDir}`, resolved);
547
+ }
548
+
549
+ export function clearProvidedEntryResolutions() {
550
+ vendorEntryProvisions.clear();
551
+ }
552
+
553
+ export function providedEntryResolution(specifier: string, resolveDir: string) {
554
+ return vendorEntryProvisions.get(`${specifier}\0${resolveDir}`);
555
+ }
556
+
557
+ export function resolvePackageSpecifier(
558
+ root: string,
559
+ fromFile: string,
560
+ specifier: string,
561
+ conditions: readonly string[],
562
+ ): string | undefined {
563
+ const packageName = packageNameFromSpecifier(specifier);
564
+ if (!packageName) return undefined;
565
+ const from = resolutionRootForFile(root, fromFile);
566
+ return realLinkedPackageFile(resolvePackageFrom(from, packageName, specifier, conditions));
567
+ }
568
+
569
+ /**
570
+ * `resolvePackageSpecifier` memoized on exactly the inputs the resolver reads. For the VENDOR path
571
+ * only, where a package's installed layout is already treated as immutable for the process (the
572
+ * artifact cache and the plugin source memo both assume it) - the ordinary dev resolver keeps
573
+ * resolving live.
574
+ */
575
+ const vendorPackageResolutions = new Map<string, string | undefined>();
576
+
577
+ export function resolveVendorPackageSpecifier(
578
+ root: string,
579
+ fromFile: string,
580
+ specifier: string,
581
+ conditions: readonly string[],
582
+ ): string | undefined {
583
+ const packageName = packageNameFromSpecifier(specifier);
584
+ if (!packageName) return undefined;
585
+ // Keyed on the RESOLUTION ROOT, not the importer's directory: that is the only thing the resolver
586
+ // reads out of `fromFile`, and one root stands in for the hundreds of importer dirs inside it.
587
+ // Keying on `fromFile` left the memo missing on nearly every call.
588
+ const from = resolutionRootForFile(root, fromFile);
589
+ const key = `${from}\0${specifier}\0${conditions.join(',')}`;
590
+ if (vendorPackageResolutions.has(key)) return vendorPackageResolutions.get(key);
591
+ const resolved = realLinkedPackageFile(resolvePackageFrom(from, packageName, specifier, conditions));
592
+ vendorPackageResolutions.set(key, resolved);
593
+ return resolved;
594
+ }
595
+
596
+ export function clearVendorPackageResolutions() {
597
+ vendorPackageResolutions.clear();
598
+ }
599
+
600
+ /**
601
+ * Drop the memoized app-tree package resolutions. Paired with
602
+ * `clearResolverFsCache` on a structural dev save: a file created or deleted
603
+ * since the last resolve can move an answer this remembered.
604
+ */
605
+ export function clearAppTreeResolutions() {
606
+ appTreeResolveCache.clear();
607
+ externalLoadTargets.clear();
608
+ }
609
+
610
+ export function resolvePackageImportSpecifier(
611
+ root: string,
612
+ fromFile: string,
613
+ specifier: string,
614
+ conditions: readonly string[],
615
+ ): string | undefined {
616
+ if (!specifier.startsWith('#')) return undefined;
617
+ const from = nearestPackageRoot(path.dirname(fromFile)) ?? resolutionRootForFile(root, fromFile);
618
+ const outcome = resolveFrom(from, specifier, conditions);
619
+ return outcome && 'path' in outcome ? outcome.path : undefined;
620
+ }
621
+
622
+ function resolvePackageFrom(
623
+ from: string,
624
+ packageName: string,
625
+ specifier: string,
626
+ conditions: readonly string[],
627
+ ) {
628
+ const outcome = resolveFrom(from, specifier, conditions);
629
+ if (outcome && 'path' in outcome) return outcome.path;
630
+ const packageRoot = findNodePackageRoot(from, packageName);
631
+ if (!packageRoot) return undefined;
632
+ return onDiskSubpath(packageRoot, subpathOfSpecifier(packageName, specifier), conditions);
633
+ }
634
+
635
+ // `exports` blocks deep access, so an on-disk file at the same path is not a
636
+ // fallback Node would ever take — but pnext's resolver does take it.
637
+ function onDiskSubpath(packageRoot: string, subpath: string, conditions: readonly string[]) {
638
+ if (subpath === '.') return undefined;
639
+ const resolved = probePackageTarget(path.resolve(packageRoot, subpath.slice(2)), conditions);
640
+ return resolved && isInside(packageRoot, resolved) ? resolved : undefined;
641
+ }
642
+
643
+ const runtimeResolveCache = new Map<string, string>();
644
+
645
+ /**
646
+ * Bun's own resolution of `specifier` from `dir`, memoized. Source rewrites ask this about a module they are
647
+ * about to hand to Bun (is it a `.cjs`?), so the answer must be Bun's, not the bundler resolver's - but the
648
+ * syscalls behind it repeat across every module of a chain and belong in one cache. Only successes are
649
+ * cached, as with `realImportPath`: a specifier the watcher is about to create must resolve on the next ask.
650
+ */
651
+ export function resolveRuntimeSpecifier(dir: string, specifier: string): string | undefined {
652
+ const key = `${dir}\0${specifier}`;
653
+ const cached = runtimeResolveCache.get(key);
654
+ if (cached !== undefined) return cached;
655
+ try {
656
+ const resolved = Bun.resolveSync(specifier, dir);
657
+ runtimeResolveCache.set(key, resolved);
658
+ return resolved;
659
+ } catch {
660
+ return undefined;
661
+ }
662
+ }
663
+
664
+ const linkedPackageFileCache = new Map<string, string>();
665
+
666
+ // webpack's `resolve.symlinks: true`: a linked workspace package compiles from
667
+ // its real path, as first-party source. Only collapse when the realpath escapes
668
+ // node_modules — pnpm's virtual store links within it, and those paths carry
669
+ // the version identity nested-dependency pinning needs.
670
+ function realLinkedPackageFile(file: string | undefined) {
671
+ if (!file) return file;
672
+ const cached = linkedPackageFileCache.get(file);
673
+ if (cached !== undefined) return cached;
674
+ let resolved = file;
675
+ try {
676
+ const real = realpathSync(file);
677
+ if (!real.includes(`${path.sep}node_modules${path.sep}`)) resolved = real;
678
+ } catch {
679
+ /* keep the unresolved path */
680
+ }
681
+ linkedPackageFileCache.set(file, resolved);
682
+ return resolved;
683
+ }
684
+
685
+ /**
686
+ * True when the package ships an `exports` map whose matching subpath entry resolves to nothing under
687
+ * `conditions` - the shape Node reports as ERR_PACKAGE_PATH_NOT_EXPORTED. A browser-only module writes this
688
+ * as `{ "./browser": { "browser": "./b.js", "node": null } }`: the subpath exists, but it is deliberately
689
+ * unavailable to a server graph.
690
+ *
691
+ * Callers use it to tell "the package says no" apart from "the package (or the file) is missing", which need
692
+ * very different handling: the former is a runtime error only if the import is actually evaluated, the
693
+ * latter is a real unresolved dependency.
694
+ */
695
+ export function isPackageSubpathUnexported(
696
+ root: string,
697
+ fromFile: string,
698
+ specifier: string,
699
+ conditions: readonly string[],
700
+ ): boolean {
701
+ const packageName = packageNameFromSpecifier(specifier);
702
+ if (!packageName) return false;
703
+ const resolutionRoot = resolutionRootForFile(root, fromFile);
704
+ const packageRoot =
705
+ findNodePackageRoot(resolutionRoot, packageName) ??
706
+ findLinkedPackageRoot(resolutionRoot, packageName);
707
+ if (!packageRoot) return false;
708
+ const { exports } = readPackageJson(packageRoot);
709
+ if (!exports || !isSubpathExports(exports)) return false;
710
+ const subpath = subpathOfSpecifier(packageName, specifier);
711
+ if (!Object.keys(exports).some(pattern => matchPattern(pattern, subpath))) return false;
712
+ if (packageExportTargetForConditions(exports, subpath, conditions)) return false;
713
+ return !onDiskSubpath(packageRoot, subpath, conditions);
714
+ }
715
+
716
+ /** Resolve a `link:`/`file:`/relative dependency to its checked-out source. */
717
+ export function resolveLinkedPackageSpecifier(
718
+ root: string,
719
+ fromFile: string,
720
+ specifier: string,
721
+ conditions: readonly string[],
722
+ ): string | undefined {
723
+ const packageName = packageNameFromSpecifier(specifier);
724
+ if (!packageName) return undefined;
725
+ const packageRoot = findLinkedPackageRoot(resolutionRootForFile(root, fromFile), packageName);
726
+ if (!packageRoot) return undefined;
727
+ return realLinkedPackageFile(
728
+ resolvePackageAtRoot(packageRoot, subpathOfSpecifier(packageName, specifier), conditions),
729
+ );
730
+ }
731
+
732
+ function resolvePackageAtRoot(
733
+ packageRoot: string,
734
+ subpath: string,
735
+ conditions: readonly string[],
736
+ ) {
737
+ const packageJson = readPackageJson(packageRoot);
738
+ const exported = packageExportTargetForConditions(packageJson.exports, subpath, conditions);
739
+ if (exported) {
740
+ const resolved = probePackageTarget(path.resolve(packageRoot, exported), conditions);
741
+ if (resolved && isInside(packageRoot, resolved)) return resolved;
742
+ }
743
+
744
+ const onDisk = onDiskSubpath(packageRoot, subpath, conditions);
745
+ if (onDisk) return onDisk;
746
+
747
+ const edgeLight = conditions.includes('edge-light')
748
+ ? [(packageJson as Record<string, unknown>)['edge-light']]
749
+ : [];
750
+ const browser = conditions.includes('browser') ? [packageJson.browser] : [];
751
+ for (const entry of [...edgeLight, ...browser, packageJson.module, packageJson.main, './index']) {
752
+ if (typeof entry !== 'string') continue;
753
+ const resolved = probePackageTarget(path.resolve(packageRoot, entry), conditions);
754
+ if (resolved) return resolved;
755
+ }
756
+
757
+ return undefined;
758
+ }
759
+
760
+ function findNodePackageRoot(root: string, packageName: string) {
761
+ let dir = path.resolve(root);
762
+ while (true) {
763
+ const candidate = path.join(dir, 'node_modules', packageName);
764
+ if (existsSync(path.join(candidate, 'package.json'))) return candidate;
765
+ const parent = path.dirname(dir);
766
+ if (parent === dir) return undefined;
767
+ dir = parent;
768
+ }
769
+ }
770
+
771
+ // One upward directory walk per (root, package) pair instead of one per import
772
+ // of it: the manifests it reads are already memoized (packageJsonCache), so the
773
+ // walk itself was the cost, repeated for every specifier a route's graph names.
774
+ const linkedPackageRootCache = new Map<string, string | undefined>();
775
+ // Bisect seam: `PNEXT_RESOLVE_LINK_MEMO=0` restores the walk-per-import path.
776
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
777
+ const memoLinkedRoots = process.env.PNEXT_RESOLVE_LINK_MEMO !== '0';
778
+
779
+ function findLinkedPackageRoot(root: string, packageName: string) {
780
+ if (!memoLinkedRoots) return walkForLinkedPackageRoot(root, packageName);
781
+ const key = `${path.resolve(root)}\0${packageName}`;
782
+ const cached = linkedPackageRootCache.get(key);
783
+ if (cached !== undefined || linkedPackageRootCache.has(key)) return cached;
784
+ const found = walkForLinkedPackageRoot(root, packageName);
785
+ linkedPackageRootCache.set(key, found);
786
+ return found;
787
+ }
788
+
789
+ function walkForLinkedPackageRoot(root: string, packageName: string) {
790
+ let dir = path.resolve(root);
791
+ while (true) {
792
+ const linked = linkedPackageRoot(dir, packageName);
793
+ if (linked) return linked;
794
+ const parent = path.dirname(dir);
795
+ if (parent === dir) return undefined;
796
+ dir = parent;
797
+ }
798
+ }
799
+
800
+ function linkedPackageRoot(packageRoot: string, packageName: string) {
801
+ const packageJson = readPackageJson(packageRoot);
802
+ const specifier =
803
+ packageJson.dependencies?.[packageName] ??
804
+ packageJson.devDependencies?.[packageName] ??
805
+ packageJson.optionalDependencies?.[packageName] ??
806
+ packageJson.peerDependencies?.[packageName];
807
+ const relative = localDependencyPath(specifier);
808
+ if (!relative) return undefined;
809
+ const candidate = path.resolve(packageRoot, relative);
810
+ const linkedPackageJson = readPackageJson(candidate);
811
+ return linkedPackageJson.name === packageName ? candidate : undefined;
812
+ }
813
+
814
+ function localDependencyPath(specifier: string | undefined) {
815
+ if (!specifier) return undefined;
816
+ for (const prefix of ['link:', 'file:']) {
817
+ if (specifier.startsWith(prefix)) return specifier.slice(prefix.length);
818
+ }
819
+ return specifier.startsWith('./') || specifier.startsWith('../') ? specifier : undefined;
820
+ }
821
+
822
+ export function isEsmModuleFile(file: string) {
823
+ const ext = path.extname(file);
824
+ if (ext === '.mjs' || ext === '.mts') return true;
825
+ if (ext === '.cjs' || ext === '.cts') return false;
826
+ const packageRoot = nearestPackageRoot(path.dirname(file));
827
+ return Boolean(packageRoot && readPackageJson(packageRoot).type === 'module');
828
+ }
829
+
830
+ const esmEntryCache = new Map<string, boolean>();
831
+
832
+ /**
833
+ * Whether a resolved package entry is really ESM. `isEsmModuleFile` reads only metadata (extension +
834
+ * nearest `package.json#type`), which calls every plain `.js` entry of a package without
835
+ * `"type": "module"` CommonJS - many of which need no inlining and no facade. Extensions stay
836
+ * authoritative (`.cjs`/`.cts` are CommonJS by definition); only the ambiguous `.js` case falls
837
+ * through to the content check.
838
+ */
839
+ export function isEsmModuleEntry(file: string) {
840
+ if (isEsmModuleFile(file)) return true;
841
+ if (path.extname(file) !== '.js') return false;
842
+ const cached = esmEntryCache.get(file);
843
+ if (cached !== undefined) return cached;
844
+ let esm = false;
845
+ try {
846
+ esm = !isCommonJsModuleSource(readFileSync(file, 'utf8'), file);
847
+ } catch {
848
+ // Unreadable entry: keep the conservative metadata answer.
849
+ }
850
+ esmEntryCache.set(file, esm);
851
+ return esm;
852
+ }
853
+
854
+ export function isCommonJsModuleSource(source: string, file: string) {
855
+ const extension = path.extname(file);
856
+ if ((extension !== '.js' && extension !== '.cjs') || isEsmModuleFile(file)) return false;
857
+ return !/^\s*(?:import(?:\s|[{'"*])|export(?:\s|[{*]))/m.test(source);
858
+ }
859
+
860
+ export function commonJsModuleHasDefaultExport(source: string, file: string) {
861
+ return (
862
+ isCommonJsModuleSource(source, file) &&
863
+ (/\bexports\.default\s*=/.test(source) || /\bmodule\.exports\s*=/.test(source))
864
+ );
865
+ }
866
+
867
+ export function findWorkspaceRoot(root: string) {
868
+ const key = path.resolve(root);
869
+ if (workspaceRootCache.has(key)) return workspaceRootCache.get(key);
870
+
871
+ let dir = key;
872
+ while (true) {
873
+ if (readPackageJson(dir).workspaces || existsSync(path.join(dir, 'pnpm-workspace.yaml'))) {
874
+ workspaceRootCache.set(key, dir);
875
+ return dir;
876
+ }
877
+ const parent = path.dirname(dir);
878
+ if (parent === dir) {
879
+ workspaceRootCache.set(key, undefined);
880
+ return undefined;
881
+ }
882
+ dir = parent;
883
+ }
884
+ }
885
+
886
+ function isInside(root: string, file: string) {
887
+ const relative = path.relative(root, file);
888
+ return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
889
+ }
890
+
891
+ function workspacePackageDirs(root: string) {
892
+ const workspaces = readPackageJson(root).workspaces;
893
+ const patterns = [
894
+ ...(Array.isArray(workspaces) ? workspaces : (workspaces?.packages ?? [])),
895
+ ...readPnpmWorkspacePatterns(root),
896
+ ];
897
+ const dirs = new Set<string>();
898
+
899
+ for (const pattern of patterns) {
900
+ if (pattern.startsWith('!')) continue;
901
+ const star = pattern.indexOf('*');
902
+ if (star === -1) {
903
+ dirs.add(path.resolve(root, pattern));
904
+ continue;
905
+ }
906
+
907
+ const base = path.resolve(root, pattern.slice(0, star));
908
+ if (!existsSync(base)) continue;
909
+ for (const entry of readdirSync(base, { withFileTypes: true })) {
910
+ if (!entry.isDirectory()) continue;
911
+ dirs.add(path.join(base, entry.name, pattern.slice(star + 1)));
912
+ }
913
+ }
914
+
915
+ return [...dirs];
916
+ }
917
+
918
+ function readPnpmWorkspacePatterns(root: string) {
919
+ const file = path.join(root, 'pnpm-workspace.yaml');
920
+ if (!existsSync(file)) return [];
921
+
922
+ const patterns: string[] = [];
923
+ let inPackages = false;
924
+ for (const line of readFileSync(file, 'utf8').split(/\r?\n/)) {
925
+ const trimmed = line.trim();
926
+ if (!trimmed || trimmed.startsWith('#')) continue;
927
+ if (/^packages\s*:\s*$/.test(trimmed)) {
928
+ inPackages = true;
929
+ continue;
930
+ }
931
+ if (!inPackages) continue;
932
+ const match = /^-\s*['"]?([^'"]+)['"]?\s*(?:#.*)?$/.exec(trimmed);
933
+ if (match?.[1]) {
934
+ patterns.push(match[1]);
935
+ continue;
936
+ }
937
+ if (/^\S/.test(line)) break;
938
+ }
939
+ return patterns;
940
+ }
941
+
942
+ const tsConfigCache = new Map<string, TsConfig>();
943
+ const packageJsonCache = new Map<string, PackageJson>();
944
+ const workspaceRootCache = new Map<string, string | undefined>();
945
+ const workspacePackageCache = new Map<string, Map<string, string>>();
946
+
947
+ function readTsConfig(root: string) {
948
+ const existing = tsConfigCache.get(root);
949
+ if (existing) return existing;
950
+ const file = tsConfigFileFor(root);
951
+ const config = (file ? readJsonc<TsConfig>(file) : undefined) ?? {};
952
+ tsConfigCache.set(root, config);
953
+ return config;
954
+ }
955
+
956
+ function readPackageJson(root: string) {
957
+ const existing = packageJsonCache.get(root);
958
+ if (existing) return existing;
959
+ const config = readJson<PackageJson>(path.join(root, 'package.json')) ?? {};
960
+ packageJsonCache.set(root, config);
961
+ return config;
962
+ }
963
+
964
+ function workspacePackages(root: string) {
965
+ const existing = workspacePackageCache.get(root);
966
+ if (existing) return existing;
967
+
968
+ const packages = new Map<string, string>();
969
+ for (const dir of workspacePackageDirs(root)) {
970
+ const packageJson = readPackageJson(dir);
971
+ if (packageJson.name) packages.set(packageJson.name, dir);
972
+ }
973
+ workspacePackageCache.set(root, packages);
974
+ return packages;
975
+ }
976
+
977
+ const workspaceMembershipCache = new Map<string, boolean>();
978
+
979
+ /**
980
+ * True when `name` is first-party workspace source. The workspace manifest (bun/npm `workspaces`,
981
+ * pnpm-workspace.yaml) is the only authority - a node_modules symlink is not, since pnpm links every
982
+ * dependency. An app outside the workspace (a checkout linked into it) sees the package only through
983
+ * node_modules, so the package's own real dir is asked about its manifest too.
984
+ */
985
+ export function isWorkspacePackage(root: string, name: string): boolean {
986
+ const key = `${path.resolve(root)}\0${name}`;
987
+ const cached = workspaceMembershipCache.get(key);
988
+ if (cached !== undefined) return cached;
989
+
990
+ const workspaceRoot = findWorkspaceRoot(root);
991
+ let member = Boolean(workspaceRoot && workspacePackages(workspaceRoot).has(name));
992
+ if (!member) {
993
+ const packageRoot = findNodePackageRoot(path.resolve(root), name);
994
+ const real = packageRoot ? realImportPath(packageRoot) : undefined;
995
+ const linkedRoot = real ? findWorkspaceRoot(real) : undefined;
996
+ const declared = linkedRoot ? workspacePackages(linkedRoot).get(name) : undefined;
997
+ member = Boolean(declared && realImportPath(declared) === real);
998
+ }
999
+
1000
+ workspaceMembershipCache.set(key, member);
1001
+ return member;
1002
+ }
1003
+
1004
+ export function workspacePackageRoots(root: string) {
1005
+ return [...workspacePackages(path.resolve(root)).values()].sort();
1006
+ }
1007
+
1008
+ function readJson<T>(file: string) {
1009
+ if (!existsSync(file)) return undefined;
1010
+ return JSON.parse(readFileSync(file, 'utf8')) as T;
1011
+ }
1012
+
1013
+ function readJsonc<T>(file: string) {
1014
+ if (!existsSync(file)) return undefined;
1015
+ return JSON.parse(stripTrailingCommas(stripJsonComments(readFileSync(file, 'utf8')))) as T;
1016
+ }
1017
+
1018
+ function stripJsonComments(source: string) {
1019
+ let output = '';
1020
+ let inString = false;
1021
+ let quote = '';
1022
+ let escaped = false;
1023
+
1024
+ for (let index = 0; index < source.length; index += 1) {
1025
+ const char = source[index]!;
1026
+ const next = source[index + 1];
1027
+
1028
+ if (inString) {
1029
+ output += char;
1030
+ if (escaped) {
1031
+ escaped = false;
1032
+ } else if (char === '\\') {
1033
+ escaped = true;
1034
+ } else if (char === quote) {
1035
+ inString = false;
1036
+ }
1037
+ continue;
1038
+ }
1039
+
1040
+ if (char === '"' || char === "'") {
1041
+ inString = true;
1042
+ quote = char;
1043
+ output += char;
1044
+ continue;
1045
+ }
1046
+
1047
+ if (char === '/' && next === '/') {
1048
+ while (index < source.length && source[index] !== '\n') index += 1;
1049
+ output += '\n';
1050
+ continue;
1051
+ }
1052
+
1053
+ if (char === '/' && next === '*') {
1054
+ index += 2;
1055
+ while (index < source.length && !(source[index] === '*' && source[index + 1] === '/')) index += 1;
1056
+ index += 1;
1057
+ continue;
1058
+ }
1059
+
1060
+ output += char;
1061
+ }
1062
+
1063
+ return output;
1064
+ }
1065
+
1066
+ function stripTrailingCommas(source: string) {
1067
+ let output = '';
1068
+ let inString = false;
1069
+ let quote = '';
1070
+ let escaped = false;
1071
+
1072
+ for (let index = 0; index < source.length; index += 1) {
1073
+ const char = source[index]!;
1074
+
1075
+ if (inString) {
1076
+ output += char;
1077
+ if (escaped) {
1078
+ escaped = false;
1079
+ } else if (char === '\\') {
1080
+ escaped = true;
1081
+ } else if (char === quote) {
1082
+ inString = false;
1083
+ }
1084
+ continue;
1085
+ }
1086
+
1087
+ if (char === '"' || char === "'") {
1088
+ inString = true;
1089
+ quote = char;
1090
+ output += char;
1091
+ continue;
1092
+ }
1093
+
1094
+ if (char === ',') {
1095
+ let nextIndex = index + 1;
1096
+ while (/\s/.test(source[nextIndex] ?? '')) nextIndex += 1;
1097
+ if (source[nextIndex] === '}' || source[nextIndex] === ']') continue;
1098
+ }
1099
+
1100
+ output += char;
1101
+ }
1102
+
1103
+ return output;
1104
+ }