@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.9 → 3.9.3-ultramodern.2

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 (275) hide show
  1. package/README.md +23 -0
  2. package/dist/cjs/backend-federation-build/codegen.js +29 -18
  3. package/dist/cjs/backend-federation-build/config.js +84 -45
  4. package/dist/cjs/backend-federation-build/emit.js +63 -22
  5. package/dist/cjs/build-config/build-environment.js +25 -162
  6. package/dist/cjs/build-config/internal-effect-discovery.js +143 -0
  7. package/dist/cjs/build-config/public.js +2 -5
  8. package/dist/cjs/build-config/remote-address.js +58 -0
  9. package/dist/cjs/cloudflare/artifacts.js +8 -19
  10. package/dist/cjs/cloudflare/delivery-unit.js +125 -43
  11. package/dist/cjs/cloudflare/index.js +14 -4
  12. package/dist/cjs/cloudflare/native-renderer.js +100 -0
  13. package/dist/cjs/cloudflare/renderer-identity.js +106 -0
  14. package/dist/cjs/cloudflare/worker-manifest.js +54 -13
  15. package/dist/cjs/cloudflare/worker-options.js +48 -0
  16. package/dist/cjs/cloudflare/wrangler-config.js +210 -13
  17. package/dist/cjs/cloudflare-builder.js +307 -89
  18. package/dist/cjs/cloudflare-output-contract.js +36 -4
  19. package/dist/cjs/cloudflare-output-verifier/identity.js +112 -23
  20. package/dist/cjs/cloudflare-output-verifier/index.js +10 -0
  21. package/dist/cjs/cloudflare-output-verifier/manifest.js +46 -0
  22. package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +7 -1
  23. package/dist/cjs/cloudflare-output-verifier/worker-bundles.js +10 -5
  24. package/dist/cjs/config.js +10 -5
  25. package/dist/cjs/deploy-output/npmAliases.js +92 -6
  26. package/dist/cjs/deploy-output/plugin.js +27 -6
  27. package/dist/cjs/deploy-output/public-assets.js +146 -0
  28. package/dist/cjs/deploy-output/target.js +57 -1
  29. package/dist/cjs/deploy-output/trace-files.js +99 -0
  30. package/dist/cjs/module-federation-config/exposes.js +70 -0
  31. package/dist/cjs/module-federation-config/index.js +73 -0
  32. package/dist/cjs/module-federation-config/inspect.js +103 -0
  33. package/dist/cjs/module-federation-config/object-literal.js +87 -0
  34. package/dist/cjs/module-federation-config/syntax.js +151 -0
  35. package/dist/cjs/module-federation-config/types.js +18 -0
  36. package/dist/cjs/native-type-checker.js +130 -13
  37. package/dist/cjs/policy-defaults.js +38 -29
  38. package/dist/cjs/release-envelope/canonical.js +2 -9
  39. package/dist/cjs/release-envelope/contract.js +68 -0
  40. package/dist/cjs/release-envelope/framework-output.js +296 -105
  41. package/dist/cjs/release-envelope/index.js +150 -36
  42. package/dist/cjs/release-envelope/plugin.js +24 -15
  43. package/dist/cjs/release-envelope/renderer-output-stamp.js +170 -0
  44. package/dist/cjs/release-envelope/types.js +10 -8
  45. package/dist/cjs/release-identity.js +8 -5
  46. package/dist/cjs/renderer-build-identity.js +134 -0
  47. package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  48. package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  49. package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  50. package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  51. package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  52. package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
  53. package/dist/cjs/workspace-topology.js +84 -0
  54. package/dist/esm/backend-federation-build/codegen.mjs +29 -18
  55. package/dist/esm/backend-federation-build/config.mjs +74 -38
  56. package/dist/esm/backend-federation-build/emit.mjs +64 -23
  57. package/dist/esm/build-config/build-environment.mjs +26 -160
  58. package/dist/esm/build-config/internal-effect-discovery.mjs +102 -0
  59. package/dist/esm/build-config/public.mjs +1 -1
  60. package/dist/esm/build-config/remote-address.mjs +20 -0
  61. package/dist/esm/cloudflare/artifacts.mjs +8 -19
  62. package/dist/esm/cloudflare/delivery-unit.mjs +124 -45
  63. package/dist/esm/cloudflare/index.mjs +15 -5
  64. package/dist/esm/cloudflare/native-renderer.mjs +48 -0
  65. package/dist/esm/cloudflare/renderer-identity.mjs +57 -0
  66. package/dist/esm/cloudflare/worker-manifest.mjs +55 -14
  67. package/dist/esm/cloudflare/worker-options.mjs +10 -0
  68. package/dist/esm/cloudflare/wrangler-config.mjs +203 -12
  69. package/dist/esm/cloudflare-builder.mjs +301 -89
  70. package/dist/esm/cloudflare-output-contract.mjs +29 -3
  71. package/dist/esm/cloudflare-output-verifier/identity.mjs +113 -24
  72. package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
  73. package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
  74. package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  75. package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  76. package/dist/esm/config.mjs +3 -1
  77. package/dist/esm/deploy-output/npmAliases.mjs +92 -6
  78. package/dist/esm/deploy-output/plugin.mjs +24 -6
  79. package/dist/esm/deploy-output/public-assets.mjs +82 -0
  80. package/dist/esm/deploy-output/target.mjs +50 -3
  81. package/dist/esm/deploy-output/trace-files.mjs +47 -0
  82. package/dist/esm/module-federation-config/exposes.mjs +18 -0
  83. package/dist/esm/module-federation-config/index.mjs +4 -0
  84. package/dist/esm/module-federation-config/inspect.mjs +65 -0
  85. package/dist/esm/module-federation-config/object-literal.mjs +43 -0
  86. package/dist/esm/module-federation-config/syntax.mjs +101 -0
  87. package/dist/esm/module-federation-config/types.mjs +0 -0
  88. package/dist/esm/native-type-checker.mjs +124 -13
  89. package/dist/esm/policy-defaults.mjs +36 -30
  90. package/dist/esm/release-envelope/canonical.mjs +2 -9
  91. package/dist/esm/release-envelope/contract.mjs +21 -0
  92. package/dist/esm/release-envelope/framework-output.mjs +297 -106
  93. package/dist/esm/release-envelope/index.mjs +149 -38
  94. package/dist/esm/release-envelope/plugin.mjs +24 -15
  95. package/dist/esm/release-envelope/renderer-output-stamp.mjs +112 -0
  96. package/dist/esm/release-envelope/types.mjs +2 -3
  97. package/dist/esm/release-identity.mjs +8 -5
  98. package/dist/esm/renderer-build-identity.mjs +82 -0
  99. package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  100. package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  101. package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  102. package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  103. package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  104. package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
  105. package/dist/esm/workspace-topology.mjs +21 -0
  106. package/dist/esm-node/backend-federation-build/codegen.mjs +29 -18
  107. package/dist/esm-node/backend-federation-build/config.mjs +74 -38
  108. package/dist/esm-node/backend-federation-build/emit.mjs +64 -23
  109. package/dist/esm-node/build-config/build-environment.mjs +26 -160
  110. package/dist/esm-node/build-config/internal-effect-discovery.mjs +103 -0
  111. package/dist/esm-node/build-config/public.mjs +1 -1
  112. package/dist/esm-node/build-config/remote-address.mjs +21 -0
  113. package/dist/esm-node/cloudflare/artifacts.mjs +8 -19
  114. package/dist/esm-node/cloudflare/delivery-unit.mjs +124 -45
  115. package/dist/esm-node/cloudflare/index.mjs +15 -5
  116. package/dist/esm-node/cloudflare/native-renderer.mjs +49 -0
  117. package/dist/esm-node/cloudflare/renderer-identity.mjs +58 -0
  118. package/dist/esm-node/cloudflare/worker-manifest.mjs +55 -14
  119. package/dist/esm-node/cloudflare/worker-options.mjs +11 -0
  120. package/dist/esm-node/cloudflare/wrangler-config.mjs +203 -12
  121. package/dist/esm-node/cloudflare-builder.mjs +301 -89
  122. package/dist/esm-node/cloudflare-output-contract.mjs +29 -3
  123. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +113 -24
  124. package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
  125. package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
  126. package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  127. package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  128. package/dist/esm-node/config.mjs +3 -1
  129. package/dist/esm-node/deploy-output/npmAliases.mjs +92 -6
  130. package/dist/esm-node/deploy-output/plugin.mjs +24 -6
  131. package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
  132. package/dist/esm-node/deploy-output/target.mjs +50 -3
  133. package/dist/esm-node/deploy-output/trace-files.mjs +48 -0
  134. package/dist/esm-node/module-federation-config/exposes.mjs +19 -0
  135. package/dist/esm-node/module-federation-config/index.mjs +5 -0
  136. package/dist/esm-node/module-federation-config/inspect.mjs +66 -0
  137. package/dist/esm-node/module-federation-config/object-literal.mjs +44 -0
  138. package/dist/esm-node/module-federation-config/syntax.mjs +102 -0
  139. package/dist/esm-node/module-federation-config/types.mjs +1 -0
  140. package/dist/esm-node/native-type-checker.mjs +124 -13
  141. package/dist/esm-node/policy-defaults.mjs +36 -30
  142. package/dist/esm-node/release-envelope/canonical.mjs +2 -9
  143. package/dist/esm-node/release-envelope/contract.mjs +22 -0
  144. package/dist/esm-node/release-envelope/framework-output.mjs +297 -106
  145. package/dist/esm-node/release-envelope/index.mjs +149 -38
  146. package/dist/esm-node/release-envelope/plugin.mjs +24 -15
  147. package/dist/esm-node/release-envelope/renderer-output-stamp.mjs +113 -0
  148. package/dist/esm-node/release-envelope/types.mjs +2 -3
  149. package/dist/esm-node/release-identity.mjs +8 -5
  150. package/dist/esm-node/renderer-build-identity.mjs +83 -0
  151. package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  152. package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  153. package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  154. package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  155. package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  156. package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
  157. package/dist/esm-node/workspace-topology.mjs +22 -0
  158. package/dist/types/backend-federation-build/codegen.d.ts +30 -0
  159. package/dist/types/backend-federation-build/config.d.ts +10 -57
  160. package/dist/types/backend-federation-build/emit.d.ts +12 -2
  161. package/dist/types/backend-federation-build/index.d.ts +1 -1
  162. package/dist/types/build-config/build-environment.d.ts +1 -9
  163. package/dist/types/build-config/internal-effect-discovery.d.ts +4 -0
  164. package/dist/types/build-config/public.d.ts +1 -1
  165. package/dist/types/build-config/remote-address.d.ts +3 -0
  166. package/dist/types/cloudflare/artifacts.d.ts +7 -6
  167. package/dist/types/cloudflare/constants.d.ts +1 -1
  168. package/dist/types/cloudflare/delivery-unit.d.ts +38 -7
  169. package/dist/types/cloudflare/native-renderer.d.ts +30 -0
  170. package/dist/types/cloudflare/renderer-identity.d.ts +25 -0
  171. package/dist/types/cloudflare/worker-manifest.d.ts +17 -2
  172. package/dist/types/cloudflare/worker-options.d.ts +20 -0
  173. package/dist/types/cloudflare/wrangler-config.d.ts +55 -2
  174. package/dist/types/cloudflare-builder.d.ts +51 -8
  175. package/dist/types/cloudflare-output-contract.d.ts +5 -3
  176. package/dist/types/cloudflare-output-verifier/identity.d.ts +7 -3
  177. package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
  178. package/dist/types/config.d.ts +98 -13
  179. package/dist/types/deploy-output/plugin.d.ts +29 -9
  180. package/dist/types/deploy-output/public-assets.d.ts +51 -0
  181. package/dist/types/deploy-output/target.d.ts +28 -5
  182. package/dist/types/deploy-output/trace-files.d.ts +17 -0
  183. package/dist/types/module-federation-config/exposes.d.ts +11 -0
  184. package/dist/types/module-federation-config/index.d.ts +5 -0
  185. package/dist/types/module-federation-config/inspect.d.ts +2 -0
  186. package/dist/types/module-federation-config/object-literal.d.ts +4 -0
  187. package/dist/types/module-federation-config/syntax.d.ts +7 -0
  188. package/dist/types/module-federation-config/types.d.ts +20 -0
  189. package/dist/types/native-type-checker.d.ts +26 -3
  190. package/dist/types/policy-defaults.d.ts +12 -2
  191. package/dist/types/release-envelope/canonical.d.ts +5 -5
  192. package/dist/types/release-envelope/contract.d.mts +8 -0
  193. package/dist/types/release-envelope/framework-output.d.ts +23 -13
  194. package/dist/types/release-envelope/index.d.ts +17 -5
  195. package/dist/types/release-envelope/plugin.d.ts +10 -6
  196. package/dist/types/release-envelope/renderer-output-stamp.d.ts +34 -0
  197. package/dist/types/release-envelope/types.d.ts +42 -4
  198. package/dist/types/release-identity.d.ts +3 -2
  199. package/dist/types/renderer-build-identity.d.ts +30 -0
  200. package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
  201. package/dist/types/workspace-topology.d.ts +91 -0
  202. package/package.json +87 -27
  203. package/src/backend-federation-build/codegen.ts +28 -19
  204. package/src/backend-federation-build/config.ts +189 -123
  205. package/src/backend-federation-build/emit.ts +119 -38
  206. package/src/backend-federation-build/index.ts +4 -1
  207. package/src/build-config/build-environment.ts +59 -285
  208. package/src/build-config/internal-effect-discovery.ts +239 -0
  209. package/src/build-config/public.ts +0 -1
  210. package/src/build-config/remote-address.ts +33 -0
  211. package/src/cloudflare/artifacts.ts +22 -43
  212. package/src/cloudflare/delivery-unit.ts +283 -60
  213. package/src/cloudflare/index.ts +35 -19
  214. package/src/cloudflare/native-renderer.ts +117 -0
  215. package/src/cloudflare/renderer-identity.ts +171 -0
  216. package/src/cloudflare/worker-manifest.ts +108 -10
  217. package/src/cloudflare/worker-options.ts +60 -0
  218. package/src/cloudflare/wrangler-config.ts +390 -14
  219. package/src/cloudflare-builder.ts +543 -170
  220. package/src/cloudflare-output-contract.ts +45 -2
  221. package/src/cloudflare-output-verifier/identity.ts +138 -7
  222. package/src/cloudflare-output-verifier/index.ts +20 -0
  223. package/src/cloudflare-output-verifier/manifest.ts +77 -0
  224. package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
  225. package/src/cloudflare-output-verifier/worker-bundles.ts +27 -11
  226. package/src/config.ts +101 -19
  227. package/src/deploy-output/npmAliases.ts +141 -4
  228. package/src/deploy-output/plugin.ts +55 -18
  229. package/src/deploy-output/public-assets.ts +224 -0
  230. package/src/deploy-output/target.ts +97 -14
  231. package/src/deploy-output/trace-files.ts +80 -0
  232. package/src/module-federation-config/exposes.ts +46 -0
  233. package/src/module-federation-config/index.ts +22 -0
  234. package/src/module-federation-config/inspect.ts +157 -0
  235. package/src/module-federation-config/object-literal.ts +76 -0
  236. package/src/module-federation-config/syntax.ts +212 -0
  237. package/src/module-federation-config/types.ts +22 -0
  238. package/src/native-type-checker.ts +233 -20
  239. package/src/policy-defaults.ts +47 -17
  240. package/src/release-envelope/canonical.ts +6 -18
  241. package/src/release-envelope/contract.mjs +28 -0
  242. package/src/release-envelope/framework-output.ts +681 -156
  243. package/src/release-envelope/index.ts +315 -60
  244. package/src/release-envelope/plugin.ts +54 -38
  245. package/src/release-envelope/renderer-output-stamp.ts +210 -0
  246. package/src/release-envelope/types.ts +75 -6
  247. package/src/release-identity.ts +19 -2
  248. package/src/renderer-build-identity.ts +168 -0
  249. package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  250. package/src/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  251. package/src/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  252. package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  253. package/src/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  254. package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
  255. package/src/workspace-topology.ts +125 -0
  256. package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +0 -11
  257. package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +0 -281
  258. package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  259. package/dist/cjs/templates/cloudflare-worker-path.mjs +0 -98
  260. package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +0 -11
  261. package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +0 -281
  262. package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  263. package/dist/esm/templates/cloudflare-worker-path.mjs +0 -98
  264. package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +0 -11
  265. package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +0 -281
  266. package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  267. package/dist/esm-node/templates/cloudflare-worker-path.mjs +0 -98
  268. package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +0 -5
  269. package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +0 -48
  270. package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +0 -13
  271. package/dist/types/templates/cloudflare-worker-path.d.mts +0 -21
  272. package/src/templates/cloudflare-worker-fs-promises.mjs +0 -11
  273. package/src/templates/cloudflare-worker-loadable-server.mjs +0 -281
  274. package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  275. package/src/templates/cloudflare-worker-path.mjs +0 -98
@@ -1,5 +1,5 @@
1
1
  import fs from 'node:fs';
2
- import { createRequire } from 'node:module';
2
+ import { createRequire, isBuiltin } from 'node:module';
3
3
  import path from 'node:path';
4
4
  import { SERVICE_WORKER_ENVIRONMENT_NAME } from '@modern-js/builder';
5
5
  import type { BffUserConfig } from '@modern-js/server-core';
@@ -10,18 +10,46 @@ import type {
10
10
  ModifyBundlerChainFn,
11
11
  RsbuildEntry,
12
12
  RsbuildEntryDescription,
13
+ Rspack,
13
14
  } from '@rsbuild/core';
14
- import { provider } from 'std-env';
15
- import { CLOUDFLARE_WORKER_NODE_BUILTINS } from './cloudflare-output-contract';
15
+ import {
16
+ CLOUDFLARE_ROUTE_DATA_HANDLER_EXPORT,
17
+ CLOUDFLARE_WORKER_NODE_BUILTINS,
18
+ CLOUDFLARE_WORKER_PLATFORM_MODULES,
19
+ getCloudflareWorkerRouteDataEntryName,
20
+ } from './cloudflare-output-contract';
21
+ import type { ResolvedDeployTarget } from './deploy-output/target';
16
22
  import { getTemplatePath } from './read-template';
17
23
 
18
24
  const moduleRequire = createRequire(import.meta.url);
19
25
  const BFF_EFFECT_WORKER_ENTRY_NAME = '__modern_bff_effect';
20
26
  const BFF_EFFECT_WORKER_RUNTIME_QUERY = 'modern-bff-runtime';
21
- const MF_SSR_DATA_FETCH_RUNTIME_PLUGIN =
22
- '@module-federation/modern-js-v3/ssr-inject-data-fetch-function-plugin';
23
- const MF_SSR_DEV_RUNTIME_PLUGIN =
24
- '@module-federation/modern-js-v3/ssr-dev-plugin';
27
+ // Generated by the router code generator for SSR entries with nested route
28
+ // data loaders; the Node server loads this module as its loader bundle. It
29
+ // re-exports the data loader runtime next to the entry's route loaders.
30
+ const SERVER_LOADER_COMBINED_FILE = 'server-loader-combined.js';
31
+ // The entry's route loaders (`routes`), which the loader bundle re-exports.
32
+ const ROUTE_SERVER_LOADERS_FILE = 'route-server-loaders.js';
33
+ const WORKER_ROUTE_DATA_FILE = 'cloudflare-worker-route-data.js';
34
+ const WORKER_ROUTE_DATA_TEMPLATE = 'cloudflare-worker-route-data.mjs';
35
+ // Client builds rewrite `*.data.*` modules into `?__loader=` fetch clients
36
+ // through the data loader plugin's webpack loader. The worker is a server
37
+ // runtime and must execute route loaders in-process. The rule is matched by
38
+ // that loader module, not by the rule name the SSR adapter registers, in both
39
+ // the workspace package and the published `@bleedingdev/modern-js-*` name.
40
+ const DATA_LOADER_CLIENT_TRANSFORM =
41
+ /plugin-data-loader[\\/](?:.+[\\/])?loader\.[cm]?js$/u;
42
+ // Rspack's default dependency-type conditions (`resolve.byDependency`).
43
+ const WORKER_DEPENDENCY_CONDITIONS = [
44
+ ['esm', 'import'],
45
+ ['wasm', 'import'],
46
+ ['loaderImport', 'import'],
47
+ ['worker', 'import'],
48
+ ['commonjs', 'require'],
49
+ ['amd', 'require'],
50
+ ['loader', 'require'],
51
+ ['unknown', 'require'],
52
+ ] as const;
25
53
  const JS_OR_TS_EXTENSIONS = new Set([
26
54
  '.js',
27
55
  '.jsx',
@@ -35,23 +63,24 @@ const JS_OR_TS_EXTENSIONS = new Set([
35
63
 
36
64
  export interface CloudflareBuilderNormalizedConfig {
37
65
  bff?: BffUserConfig;
38
- deploy?: { target?: string };
66
+ server?: { rsc?: unknown };
39
67
  }
40
68
 
41
69
  export interface CloudflareBuilderAppContext {
70
+ apiOnly?: boolean;
71
+ deployTarget?: ResolvedDeployTarget;
42
72
  apiDirectory: string;
43
73
  appDirectory: string;
74
+ entrypoints?: ReadonlyArray<{ entryName: string }>;
75
+ internalDirectory?: string;
44
76
  }
45
77
 
46
78
  export interface CloudflareBuilderEnvironmentsOptions {
47
79
  appContext: CloudflareBuilderAppContext;
48
80
  environments: Record<string, EnvironmentConfig>;
49
81
  normalizedConfig: CloudflareBuilderNormalizedConfig;
50
- resolveDeployProvider?: ResolveCloudflareDeployProvider;
51
82
  }
52
83
 
53
- export type ResolveCloudflareDeployProvider = () => string | undefined;
54
-
55
84
  export interface CloudflareWorkerRspackConfig {
56
85
  externals: Record<string, string>;
57
86
  externalsType: 'module-import';
@@ -190,16 +219,339 @@ const setAliasIfPresent = (
190
219
  }
191
220
  };
192
221
 
222
+ // Node accepts a bare specifier only for built-ins that are not prefix-only
223
+ // (`sqlite` and `test` are ordinary npm package names without `node:`).
193
224
  const getCloudflareWorkerNodeExternals = () =>
194
- Object.fromEntries(
195
- CLOUDFLARE_WORKER_NODE_BUILTINS.flatMap(builtin => {
225
+ Object.fromEntries([
226
+ ...CLOUDFLARE_WORKER_NODE_BUILTINS.flatMap(builtin => {
196
227
  const nodeBuiltin = `node:${builtin}`;
197
- return [
198
- [builtin, `module-import ${nodeBuiltin}`],
199
- [nodeBuiltin, `module-import ${nodeBuiltin}`],
200
- ];
228
+ const external = `module-import ${nodeBuiltin}`;
229
+ return isBuiltin(builtin)
230
+ ? [
231
+ [builtin, external],
232
+ [nodeBuiltin, external],
233
+ ]
234
+ : [[nodeBuiltin, external]];
201
235
  }),
202
- );
236
+ ...CLOUDFLARE_WORKER_PLATFORM_MODULES.map(platformModule => [
237
+ platformModule,
238
+ `module-import ${platformModule}`,
239
+ ]),
240
+ ]);
241
+
242
+ const getPackageNameFromRequest = (request: string) => {
243
+ if (
244
+ request.startsWith('.') ||
245
+ request.startsWith('/') ||
246
+ request.includes(':') ||
247
+ path.isAbsolute(request)
248
+ ) {
249
+ return undefined;
250
+ }
251
+ const segments = request.split('/');
252
+ return request.startsWith('@')
253
+ ? segments.length > 1
254
+ ? `${segments[0]}/${segments[1]}`
255
+ : undefined
256
+ : segments[0];
257
+ };
258
+
259
+ interface OptionalDependencyManifest {
260
+ optionalDependencies?: Record<string, string>;
261
+ peerDependenciesMeta?: Record<string, { optional?: boolean }>;
262
+ [field: string]: unknown;
263
+ }
264
+
265
+ type AliasField = string | readonly string[];
266
+
267
+ // Mirrors the resolver's `resolve.aliasFields` lookup: a field is a key or a
268
+ // nested key path into the importing package's manifest whose object maps
269
+ // exact requests to replacements (or to `false` for an empty module); an
270
+ // entry for a package name does not cover its subpaths.
271
+ const isRedirectedByAliasField = (
272
+ manifest: OptionalDependencyManifest,
273
+ aliasFields: readonly AliasField[],
274
+ request: string,
275
+ ) =>
276
+ aliasFields.some(field => {
277
+ let value: unknown = manifest;
278
+ for (const key of typeof field === 'string' ? [field] : field) {
279
+ value =
280
+ value && typeof value === 'object'
281
+ ? (value as Record<string, unknown>)[key]
282
+ : undefined;
283
+ }
284
+ return (
285
+ typeof value === 'object' &&
286
+ value !== null &&
287
+ Object.hasOwn(value, request)
288
+ );
289
+ });
290
+
291
+ const findOwningPackageManifest = (
292
+ directory: string,
293
+ cache: Map<string, OptionalDependencyManifest | undefined>,
294
+ ): OptionalDependencyManifest | undefined => {
295
+ if (cache.has(directory)) {
296
+ return cache.get(directory);
297
+ }
298
+ let manifest: OptionalDependencyManifest | undefined;
299
+ const packageJsonPath = path.join(directory, 'package.json');
300
+ if (fs.existsSync(packageJsonPath)) {
301
+ try {
302
+ manifest = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
303
+ } catch {
304
+ manifest = undefined;
305
+ }
306
+ } else if (path.dirname(directory) !== directory) {
307
+ manifest = findOwningPackageManifest(path.dirname(directory), cache);
308
+ }
309
+ cache.set(directory, manifest);
310
+ return manifest;
311
+ };
312
+
313
+ // Mirrors the resolver's `resolve.modules` lookup: absolute entries are
314
+ // searched directly, named entries in the context directory and each ancestor.
315
+ const isPackageInstalled = (
316
+ packageName: string,
317
+ directory: string,
318
+ moduleDirectories: readonly string[],
319
+ ) =>
320
+ moduleDirectories.some(moduleDirectory => {
321
+ if (path.isAbsolute(moduleDirectory)) {
322
+ return fs.existsSync(path.join(moduleDirectory, packageName));
323
+ }
324
+ let current = directory;
325
+ while (true) {
326
+ if (fs.existsSync(path.join(current, moduleDirectory, packageName))) {
327
+ return true;
328
+ }
329
+ const parent = path.dirname(current);
330
+ if (parent === current) {
331
+ return false;
332
+ }
333
+ current = parent;
334
+ }
335
+ });
336
+
337
+ /**
338
+ * Leaves an absent optional dependency missing at runtime, exactly as Node
339
+ * does, instead of failing the worker build. It applies only when the
340
+ * importing package itself declares the request as an optional peer
341
+ * (`peerDependenciesMeta.<name>.optional`) or an `optionalDependencies`
342
+ * entry, the package is not found in any `resolve.modules` directory, and
343
+ * neither the app (`resolve.alias`, `resolve.fallback`, `externals`) nor the
344
+ * importing package (an enabled `resolve.aliasFields` entry such as
345
+ * `browser`) redirects the request. The import
346
+ * then rejects with "Cannot find module" so the library's own fallback
347
+ * handles it (for example `@redis/client` guards `import('@node-rs/xxhash')`).
348
+ */
349
+ export const createAbsentOptionalDependencyFilter = (
350
+ isRedirected: (request: string) => boolean = () => false,
351
+ moduleDirectories: readonly string[] = ['node_modules'],
352
+ aliasFields: readonly AliasField[] = [],
353
+ ) => {
354
+ const manifests = new Map<string, OptionalDependencyManifest | undefined>();
355
+ return (request: string, context: string) => {
356
+ const packageName = getPackageNameFromRequest(request);
357
+ if (!packageName || !context || isRedirected(request)) {
358
+ return false;
359
+ }
360
+ const manifest = findOwningPackageManifest(context, manifests);
361
+ if (!manifest) {
362
+ return false;
363
+ }
364
+ const optional =
365
+ manifest.peerDependenciesMeta?.[packageName]?.optional === true ||
366
+ Object.hasOwn(manifest.optionalDependencies ?? {}, packageName);
367
+ return (
368
+ optional &&
369
+ !isRedirectedByAliasField(manifest, aliasFields, request) &&
370
+ !isPackageInstalled(packageName, context, moduleDirectories)
371
+ );
372
+ };
373
+ };
374
+
375
+ type AliasOption =
376
+ | Record<string, unknown>
377
+ | { name: string; onlyModule?: boolean }[]
378
+ | false
379
+ | undefined;
380
+
381
+ const getAliasNames = (alias: AliasOption) =>
382
+ !alias
383
+ ? []
384
+ : Array.isArray(alias)
385
+ ? alias.map(entry => (entry.onlyModule ? `${entry.name}$` : entry.name))
386
+ : Object.keys(alias);
387
+
388
+ type ExternalMatcher = (request: string) => boolean;
389
+
390
+ // String, RegExp and object-key externals are matched synchronously; function
391
+ // externals are consulted per request by `claimsByFunctionExternal`.
392
+ // An object entry mapped to `false` keeps the request bundled, so it is not
393
+ // a redirect.
394
+ const getExternalMatchers = (externals: unknown): ExternalMatcher[] => {
395
+ if (Array.isArray(externals)) {
396
+ return externals.flatMap(getExternalMatchers);
397
+ }
398
+ if (typeof externals === 'string') {
399
+ return [request => request === externals];
400
+ }
401
+ if (externals instanceof RegExp) {
402
+ return [
403
+ request => {
404
+ externals.lastIndex = 0;
405
+ return externals.test(request);
406
+ },
407
+ ];
408
+ }
409
+ if (externals && typeof externals === 'object') {
410
+ const names = new Set(
411
+ Object.entries(externals)
412
+ .filter(([, value]) => value !== false)
413
+ .map(([name]) => name),
414
+ );
415
+ return [request => names.has(request)];
416
+ }
417
+ return [];
418
+ };
419
+
420
+ /**
421
+ * Reads the final resolved `resolve.alias`, `resolve.fallback` and
422
+ * `externals` so an app's own
423
+ * replacement for an optional package still wins over the absent-module
424
+ * fallback (the fallback runs before resolution).
425
+ */
426
+ export const createRequestRedirectMatcher = (options: {
427
+ externals?: unknown;
428
+ resolve?: { alias?: AliasOption; fallback?: AliasOption };
429
+ }) => {
430
+ const aliasNames = [
431
+ ...getAliasNames(options.resolve?.alias),
432
+ ...getAliasNames(options.resolve?.fallback),
433
+ ];
434
+ const externalMatchers = getExternalMatchers(options.externals);
435
+ return (request: string) =>
436
+ externalMatchers.some(matches => matches(request)) ||
437
+ aliasNames.some(name =>
438
+ name.endsWith('$')
439
+ ? request === name.slice(0, -1)
440
+ : request === name || request.startsWith(`${name}/`),
441
+ );
442
+ };
443
+
444
+ type FunctionExternal = (
445
+ data: Rspack.ExternalItemFunctionData,
446
+ callback: (error?: Error | null, result?: unknown) => void,
447
+ ) => unknown;
448
+
449
+ const getFunctionExternals = (externals: unknown): FunctionExternal[] =>
450
+ Array.isArray(externals)
451
+ ? externals.flatMap(getFunctionExternals)
452
+ : typeof externals === 'function'
453
+ ? [externals as FunctionExternal]
454
+ : [];
455
+
456
+ // Calls a function external with the same callback, promise and synchronous
457
+ // return forms Rspack accepts. `undefined`, `null` and `false` decline.
458
+ const callFunctionExternal = (
459
+ external: FunctionExternal,
460
+ data: Rspack.ExternalItemFunctionData,
461
+ ) =>
462
+ new Promise<unknown>((resolve, reject) => {
463
+ const returned = external(data, (error, result) =>
464
+ error ? reject(error) : resolve(result),
465
+ );
466
+ if (
467
+ returned !== null &&
468
+ typeof returned === 'object' &&
469
+ typeof (returned as PromiseLike<unknown>).then === 'function'
470
+ ) {
471
+ (returned as PromiseLike<unknown>).then(resolve, reject);
472
+ } else if (external.length === 1) {
473
+ resolve(returned);
474
+ }
475
+ });
476
+
477
+ /**
478
+ * Reports whether any function external claims the request. A function that
479
+ * throws is treated as claiming it, so the request goes through ordinary
480
+ * external processing and surfaces that error instead of being ignored.
481
+ */
482
+ export const claimsByFunctionExternal = async (
483
+ externals: readonly FunctionExternal[],
484
+ data: Rspack.ExternalItemFunctionData,
485
+ ) => {
486
+ for (const external of externals) {
487
+ try {
488
+ const result = await callFunctionExternal(external, data);
489
+ if (result !== undefined && result !== null && result !== false) {
490
+ return true;
491
+ }
492
+ } catch {
493
+ return true;
494
+ }
495
+ }
496
+ return false;
497
+ };
498
+
499
+ const PLUGIN_NAME = 'AbsentOptionalDependencyPlugin';
500
+
501
+ class AbsentOptionalDependencyPlugin {
502
+ apply(compiler: Rspack.Compiler) {
503
+ const isAbsent = createAbsentOptionalDependencyFilter(
504
+ createRequestRedirectMatcher(compiler.options),
505
+ compiler.options.resolve.modules,
506
+ compiler.options.resolve.aliasFields,
507
+ );
508
+ const functionExternals = getFunctionExternals(compiler.options.externals);
509
+ compiler.hooks.normalModuleFactory.tap(PLUGIN_NAME, factory => {
510
+ factory.hooks.beforeResolve.tapPromise(PLUGIN_NAME, async data => {
511
+ if (!isAbsent(data.request, data.context)) {
512
+ return undefined;
513
+ }
514
+ const claimed = await claimsByFunctionExternal(functionExternals, {
515
+ context: data.context,
516
+ contextInfo: {
517
+ issuer: data.contextInfo.issuer,
518
+ issuerLayer: data.contextInfo.issuerLayer ?? null,
519
+ },
520
+ getResolve: options => {
521
+ const resolver = factory.getResolver('normal', options ?? {});
522
+ function resolveRequest(
523
+ context: string,
524
+ request: string,
525
+ callback: Parameters<typeof resolver.resolve>[4],
526
+ ): void;
527
+ function resolveRequest(
528
+ context: string,
529
+ request: string,
530
+ ): Promise<string | undefined>;
531
+ function resolveRequest(
532
+ context: string,
533
+ request: string,
534
+ callback?: Parameters<typeof resolver.resolve>[4],
535
+ ) {
536
+ if (callback) {
537
+ resolver.resolve({}, context, request, {}, callback);
538
+ return undefined;
539
+ }
540
+ return new Promise<string | undefined>((resolve, reject) => {
541
+ resolver.resolve({}, context, request, {}, (error, result) =>
542
+ error ? reject(error) : resolve(result || undefined),
543
+ );
544
+ });
545
+ }
546
+ return resolveRequest;
547
+ },
548
+ request: data.request,
549
+ });
550
+ return claimed ? undefined : false;
551
+ });
552
+ });
553
+ }
554
+ }
203
555
 
204
556
  const normalizeWorkerOutputName = (name: string) =>
205
557
  path.posix.normalize(name.replace(/\\/gu, '/')).toLowerCase();
@@ -282,18 +634,28 @@ export function applyCloudflareWorkerRspackConfig(
282
634
  chain.optimization.splitChunks(config.optimization.splitChunks);
283
635
  }
284
636
 
285
- export function applyCloudflareWorkerMfRuntimeBoundary(
637
+ /**
638
+ * The worker environment uses Rspack's web target for ESM output, which the
639
+ * data loader transform treats as a browser bundle. Remove every module rule
640
+ * that applies it, so SSR runs route loaders in-process as the Node server
641
+ * does instead of fetching the worker's own `?__loader=` URL.
642
+ */
643
+ function removeCloudflareWorkerDataLoaderClientTransform(
286
644
  chain: Parameters<ModifyBundlerChainFn>[0],
287
645
  ) {
288
- const runtimePlugin = getTemplatePath(
289
- 'cloudflare-worker-mf-ssr-runtime-plugin.mjs',
290
- );
291
- chain.plugins.delete('plugin-module-federation');
292
- chain.resolve.alias.set(
293
- `${MF_SSR_DATA_FETCH_RUNTIME_PLUGIN}$`,
294
- runtimePlugin,
295
- );
296
- chain.resolve.alias.set(`${MF_SSR_DEV_RUNTIME_PLUGIN}$`, runtimePlugin);
646
+ for (const [name, rule] of Object.entries(
647
+ chain.module.rules.entries() ?? {},
648
+ )) {
649
+ if (
650
+ rule.uses
651
+ .values()
652
+ .some(use =>
653
+ DATA_LOADER_CLIENT_TRANSFORM.test(String(use.get('loader'))),
654
+ )
655
+ ) {
656
+ chain.module.rules.delete(name);
657
+ }
658
+ }
297
659
  }
298
660
 
299
661
  const getEffectBffEntry = (
@@ -318,31 +680,6 @@ const getEffectBffEntry = (
318
680
  return resolveJsOrTsEntry(path.resolve(appContext.apiDirectory, 'index'));
319
681
  };
320
682
 
321
- const CLOUDFLARE_DEPLOY_PROVIDERS = new Set([
322
- 'cloudflare',
323
- 'cloudflare_pages',
324
- 'cloudflare_workers',
325
- ]);
326
-
327
- const resolveStdEnvDeployProvider: ResolveCloudflareDeployProvider = () =>
328
- provider;
329
-
330
- const isCloudflareWorkerDeploy = (
331
- normalizedConfig: CloudflareBuilderNormalizedConfig,
332
- resolveDeployProvider: ResolveCloudflareDeployProvider,
333
- ) => {
334
- const explicitTarget =
335
- normalizedConfig.deploy?.target || process.env.MODERNJS_DEPLOY;
336
- if (explicitTarget) {
337
- return explicitTarget === 'cloudflare';
338
- }
339
-
340
- const detectedProvider = resolveDeployProvider();
341
- return detectedProvider
342
- ? CLOUDFLARE_DEPLOY_PROVIDERS.has(detectedProvider)
343
- : false;
344
- };
345
-
346
683
  const rewriteWorkerEntryPath = (entry: string) =>
347
684
  entry
348
685
  .replace('index.jsx', 'index.server.jsx')
@@ -381,34 +718,94 @@ const rewriteWorkerEntries = (entries: RsbuildEntry): RsbuildEntry =>
381
718
  const prependBundlerChain = (
382
719
  environment: EnvironmentConfig,
383
720
  handler: ModifyBundlerChainFn,
721
+ finalHandler?: ModifyBundlerChainFn,
384
722
  ): EnvironmentConfig => {
385
723
  const bundlerChain = environment.tools?.bundlerChain;
724
+ const handlers = [
725
+ handler,
726
+ ...(bundlerChain
727
+ ? Array.isArray(bundlerChain)
728
+ ? bundlerChain
729
+ : [bundlerChain]
730
+ : []),
731
+ ...(finalHandler ? [finalHandler] : []),
732
+ ];
386
733
  return {
387
734
  ...environment,
388
735
  tools: {
389
736
  ...environment.tools,
390
737
  htmlPlugin: false,
391
- bundlerChain: bundlerChain
392
- ? Array.isArray(bundlerChain)
393
- ? [handler, ...bundlerChain]
394
- : [handler, bundlerChain]
395
- : handler,
738
+ bundlerChain: handlers,
396
739
  },
397
740
  };
398
741
  };
399
742
 
743
+ const createWorkerRouteDataModule = () =>
744
+ [
745
+ `import { routes } from './${ROUTE_SERVER_LOADERS_FILE}';`,
746
+ `import { createRouteDataRequestHandler } from ${JSON.stringify(
747
+ getTemplatePath(WORKER_ROUTE_DATA_TEMPLATE),
748
+ )};`,
749
+ '',
750
+ `export const ${CLOUDFLARE_ROUTE_DATA_HANDLER_EXPORT} =`,
751
+ ' createRouteDataRequestHandler(routes);',
752
+ '',
753
+ ].join('\n');
754
+
755
+ /**
756
+ * Route data requests reach the worker whenever the browser navigates to a
757
+ * route with a data loader. Bundle the route loaders of each entry that has a
758
+ * Node server loader bundle behind the worker route data handler, so the
759
+ * worker answers them instead of rendering the page. The worker imports the
760
+ * route loaders without the loader bundle's data loader runtime, which is
761
+ * built on react-router.
762
+ */
763
+ const getWorkerRouteDataEntries = (
764
+ appContext: CloudflareBuilderAppContext,
765
+ ): Record<string, string[]> => {
766
+ const { entrypoints, internalDirectory } = appContext;
767
+ if (!entrypoints || !internalDirectory) {
768
+ return {};
769
+ }
770
+
771
+ return Object.fromEntries(
772
+ entrypoints.flatMap(({ entryName }) => {
773
+ const entryDirectory = path.join(internalDirectory, entryName);
774
+ if (
775
+ !fs.existsSync(
776
+ path.join(entryDirectory, SERVER_LOADER_COMBINED_FILE),
777
+ ) ||
778
+ !fs.existsSync(path.join(entryDirectory, ROUTE_SERVER_LOADERS_FILE))
779
+ ) {
780
+ return [];
781
+ }
782
+
783
+ const routeDataFile = path.join(entryDirectory, WORKER_ROUTE_DATA_FILE);
784
+ fs.writeFileSync(routeDataFile, createWorkerRouteDataModule());
785
+ return [
786
+ [getCloudflareWorkerRouteDataEntryName(entryName), [routeDataFile]],
787
+ ];
788
+ }),
789
+ );
790
+ };
791
+
400
792
  const getWorkerEntries = (
401
793
  environment: EnvironmentConfig,
402
794
  normalizedConfig: CloudflareBuilderNormalizedConfig,
403
795
  appContext: CloudflareBuilderAppContext,
404
796
  ) => {
405
797
  const configuredEntries = environment.source?.entry;
406
- if (!configuredEntries) {
798
+ const effectApiEntry = getEffectBffEntry(normalizedConfig, appContext);
799
+ if (!configuredEntries && !effectApiEntry) {
407
800
  return undefined;
408
801
  }
409
802
 
410
- const entries = rewriteWorkerEntries(configuredEntries);
411
- const effectApiEntry = getEffectBffEntry(normalizedConfig, appContext);
803
+ const entries = configuredEntries
804
+ ? {
805
+ ...rewriteWorkerEntries(configuredEntries),
806
+ ...getWorkerRouteDataEntries(appContext),
807
+ }
808
+ : {};
412
809
  return effectApiEntry
413
810
  ? {
414
811
  ...entries,
@@ -422,23 +819,9 @@ const getWorkerEntries = (
422
819
  const createCloudflareBundlerChain = (
423
820
  appContext: CloudflareBuilderAppContext,
424
821
  workerEntryNames: Iterable<string>,
822
+ rscEnabled: boolean,
425
823
  ): ModifyBundlerChainFn => {
426
824
  const resolvePaths = [appContext.appDirectory, process.cwd()];
427
- const tanstackRouterSsrServerFile = resolvePackageFile(
428
- '@tanstack/router-core',
429
- 'dist/esm/ssr/ssr-server.js',
430
- resolvePaths,
431
- );
432
- const runtimeRscWorkerFile = resolvePackageFile(
433
- '@modern-js/runtime',
434
- 'dist/esm/rsc/server.worker.mjs',
435
- resolvePaths,
436
- );
437
- const renderRscWorkerFile = resolvePackageFile(
438
- '@modern-js/render',
439
- 'dist/esm/rscWorker.mjs',
440
- resolvePaths,
441
- );
442
825
  const reactFile = resolvePackageFile('react', 'index.js', resolvePaths);
443
826
  const reactJsxRuntimeFile = resolvePackageFile(
444
827
  'react',
@@ -465,17 +848,14 @@ const createCloudflareBundlerChain = (
465
848
  'dist/esm/loadable.esm.mjs',
466
849
  [...resolvePaths, getTemplatePath('')],
467
850
  );
468
- const loadableServerWorkerFile = getTemplatePath(
469
- 'cloudflare-worker-loadable-server.mjs',
470
- );
471
- const fsPromisesWorkerFile = getTemplatePath(
472
- 'cloudflare-worker-fs-promises.mjs',
473
- );
474
- const pathWorkerFile = getTemplatePath('cloudflare-worker-path.mjs');
475
851
  const entryNames = [...workerEntryNames];
476
852
 
477
853
  return chain => {
478
854
  applyCloudflareWorkerRspackConfig(chain, entryNames);
855
+ removeCloudflareWorkerDataLoaderClientTransform(chain);
856
+ // Native RSC manifests belong to each entry's runtime. A shared runtime
857
+ // can replace the page's client references with a loader-only manifest.
858
+ if (rscEnabled) chain.optimization.runtimeChunk(false);
479
859
  chain.output
480
860
  .module(true)
481
861
  .library({ type: 'module' })
@@ -483,105 +863,66 @@ const createCloudflareBundlerChain = (
483
863
  .chunkFormat('module')
484
864
  .chunkLoading('import')
485
865
  .workerChunkLoading('import');
866
+ // workerd has no `__dirname` / `__filename`, so CommonJS dependencies get
867
+ // Rspack's compile-time mock instead of runtime identifiers.
868
+ chain.node.set('__dirname', 'mock').set('__filename', 'mock');
486
869
 
487
870
  for (const condition of [
488
871
  'workerd',
489
872
  'worker',
490
873
  'webpack',
491
874
  isProd() ? 'production' : 'development',
492
- 'import',
493
- 'require',
494
875
  'module',
495
876
  ]) {
496
877
  chain.resolve.conditionNames.add(condition);
497
878
  }
498
-
499
- applyCloudflareWorkerMfRuntimeBoundary(chain);
500
- if (tanstackRouterSsrServerFile) {
501
- chain.resolve.alias.set(
502
- '@tanstack/router-core/ssr/server$',
503
- tanstackRouterSsrServerFile,
504
- );
505
- chain.resolve.alias.set(
506
- '@tanstack/router-core/ssr/server',
507
- tanstackRouterSsrServerFile,
508
- );
879
+ // Setting base conditions drops Rspack's per-dependency `import` /
880
+ // `require` defaults, and adding both globally lets a dual package's
881
+ // first-listed `import` branch win for `require()` (so `pg` received an
882
+ // ES module namespace for `pg-pool` and missed `pg-cloudflare`'s
883
+ // `workerd.require` entry). Restore Rspack's own per-dependency split.
884
+ for (const [dependencyType, condition] of WORKER_DEPENDENCY_CONDITIONS) {
885
+ chain.resolve.byDependency.set(dependencyType, {
886
+ conditionNames: [condition, '...'],
887
+ });
509
888
  }
510
- if (runtimeRscWorkerFile) {
511
- chain.resolve.alias.set(
512
- '@modern-js/runtime/rsc/server$',
513
- runtimeRscWorkerFile,
889
+ chain
890
+ .plugin('cloudflare-worker-absent-optional-dependencies')
891
+ .use(AbsentOptionalDependencyPlugin);
892
+
893
+ // The worker environment still uses the web target, so
894
+ // @module-federation/modern-js-v3 registers its browser federation plugin
895
+ // on it; Cloudflare workers load no native remotes. The `worker` export
896
+ // condition keeps MF's SSR runtime plugins out of the bundle
897
+ // (module-federation/core#5155). This deletion goes with the web-worker
898
+ // target, which needs @rsbuild/core 2.2.10 (web-infra-dev/rsbuild#8557).
899
+ // RSC entries resolve `@modern-js/render/rsc` and React through the
900
+ // `workerd`/`worker` export conditions instead of pinned files.
901
+ chain.plugins.delete('plugin-module-federation');
902
+ if (!rscEnabled) {
903
+ setAliasIfPresent(chain.resolve.alias, 'react$', reactFile);
904
+ setAliasIfPresent(
905
+ chain.resolve.alias,
906
+ 'react/jsx-runtime$',
907
+ reactJsxRuntimeFile,
514
908
  );
515
- chain.resolve.alias.set(
516
- '@modern-js/runtime/rsc/server',
517
- runtimeRscWorkerFile,
909
+ setAliasIfPresent(
910
+ chain.resolve.alias,
911
+ 'react/jsx-dev-runtime$',
912
+ reactJsxDevRuntimeFile,
518
913
  );
519
- }
520
- if (renderRscWorkerFile) {
521
- chain.resolve.alias.set('@modern-js/render/rsc$', renderRscWorkerFile);
522
- chain.resolve.alias.set('@modern-js/render/rsc', renderRscWorkerFile);
523
- chain.resolve.alias.set(
524
- '@modern-js/render/rsc-worker$',
525
- renderRscWorkerFile,
914
+ setAliasIfPresent(chain.resolve.alias, 'react-dom$', reactDomFile);
915
+ setAliasIfPresent(
916
+ chain.resolve.alias,
917
+ 'react-dom/server.edge$',
918
+ reactDomServerEdgeFile,
526
919
  );
527
920
  }
528
- setAliasIfPresent(chain.resolve.alias, 'react$', reactFile);
529
- setAliasIfPresent(
530
- chain.resolve.alias,
531
- 'react/jsx-runtime$',
532
- reactJsxRuntimeFile,
533
- );
534
- setAliasIfPresent(
535
- chain.resolve.alias,
536
- 'react/jsx-dev-runtime$',
537
- reactJsxDevRuntimeFile,
538
- );
539
- setAliasIfPresent(chain.resolve.alias, 'react-dom$', reactDomFile);
540
- setAliasIfPresent(
541
- chain.resolve.alias,
542
- 'react-dom/server.edge$',
543
- reactDomServerEdgeFile,
544
- );
545
921
  setAliasIfPresent(
546
922
  chain.resolve.alias,
547
923
  '@loadable/component$',
548
924
  loadableComponentFile,
549
925
  );
550
- setAliasIfPresent(
551
- chain.resolve.alias,
552
- '@loadable/server$',
553
- loadableServerWorkerFile,
554
- );
555
- setAliasIfPresent(
556
- chain.resolve.alias,
557
- 'fs/promises$',
558
- fsPromisesWorkerFile,
559
- );
560
- setAliasIfPresent(
561
- chain.resolve.alias,
562
- 'node:fs/promises$',
563
- fsPromisesWorkerFile,
564
- );
565
- setAliasIfPresent(chain.resolve.alias, 'path$', pathWorkerFile);
566
- setAliasIfPresent(chain.resolve.alias, 'node:path$', pathWorkerFile);
567
- chain.resolve.alias.set(
568
- 'react-server-dom-rspack/server.node$',
569
- 'react-server-dom-rspack/server.edge',
570
- );
571
- chain.resolve.alias.set(
572
- 'react-server-dom-rspack/server.node',
573
- 'react-server-dom-rspack/server.edge',
574
- );
575
- chain.resolve.alias.set(
576
- 'react-server-dom-rspack/client.node$',
577
- 'react-server-dom-rspack/client.edge',
578
- );
579
- chain.resolve.alias.set(
580
- 'react-server-dom-rspack/client.node',
581
- 'react-server-dom-rspack/client.edge',
582
- );
583
- chain.resolve.fallback.set('fs', false);
584
- chain.resolve.fallback.set('node:fs', false);
585
926
  };
586
927
  };
587
928
 
@@ -589,13 +930,19 @@ export function getCloudflareBuilderEnvironments({
589
930
  appContext,
590
931
  environments,
591
932
  normalizedConfig,
592
- resolveDeployProvider = resolveStdEnvDeployProvider,
593
933
  }: CloudflareBuilderEnvironmentsOptions): Record<string, EnvironmentConfig> {
594
- if (!isCloudflareWorkerDeploy(normalizedConfig, resolveDeployProvider)) {
934
+ if (appContext.deployTarget?.target !== 'cloudflare') {
595
935
  return environments;
596
936
  }
597
937
 
598
- const workerEnvironment = environments[SERVICE_WORKER_ENVIRONMENT_NAME];
938
+ const workerEnvironment =
939
+ environments[SERVICE_WORKER_ENVIRONMENT_NAME] ??
940
+ (appContext.apiOnly
941
+ ? {
942
+ output: { target: 'web-worker' as const },
943
+ source: { entry: {} },
944
+ }
945
+ : undefined);
599
946
  if (!workerEnvironment) {
600
947
  return environments;
601
948
  }
@@ -622,9 +969,38 @@ export function getCloudflareBuilderEnvironments({
622
969
  entry: workerEntries,
623
970
  },
624
971
  },
625
- createCloudflareBundlerChain(appContext, Object.keys(workerEntries)),
972
+ createCloudflareBundlerChain(
973
+ appContext,
974
+ Object.keys(workerEntries),
975
+ Boolean(normalizedConfig.server?.rsc),
976
+ ),
977
+ normalizedConfig.server?.rsc
978
+ ? chain => {
979
+ // Source builds may inherit consuming-React file pins. Native RSC
980
+ // issuer conditions must resolve package exports in this worker.
981
+ for (const [name, packageName, file] of [
982
+ ['react$', 'react', 'index.js'],
983
+ ['react/jsx-runtime$', 'react', 'jsx-runtime.js'],
984
+ ['react/jsx-dev-runtime$', 'react', 'jsx-dev-runtime.js'],
985
+ ['react-dom$', 'react-dom', 'index.js'],
986
+ ['react-dom/server.edge$', 'react-dom', 'server.edge.js'],
987
+ ]) {
988
+ const target = resolvePackageFile(packageName, file, [
989
+ appContext.appDirectory,
990
+ process.cwd(),
991
+ ]);
992
+ if (target && chain.resolve.alias.get(name) === target) {
993
+ chain.resolve.alias.delete(name);
994
+ }
995
+ }
996
+ }
997
+ : undefined,
626
998
  );
627
999
 
1000
+ if (appContext.apiOnly) {
1001
+ return { [SERVICE_WORKER_ENVIRONMENT_NAME]: cloudflareWorkerEnvironment };
1002
+ }
1003
+
628
1004
  return {
629
1005
  ...environments,
630
1006
  [SERVICE_WORKER_ENVIRONMENT_NAME]: cloudflareWorkerEnvironment,
@@ -637,9 +1013,7 @@ export function applyCloudflareBuilderEnvironments(
637
1013
  return { environments: getCloudflareBuilderEnvironments(options) };
638
1014
  }
639
1015
 
640
- export const createCloudflareBuilderPlugin = (
641
- resolveDeployProvider = resolveStdEnvDeployProvider,
642
- ): CloudflareBuilderPlugin => ({
1016
+ export const createCloudflareBuilderPlugin = (): CloudflareBuilderPlugin => ({
643
1017
  name: '@modern-js/cloudflare-builder',
644
1018
  setup(api) {
645
1019
  api.modifyBuilderEnvironments(({ environments }) =>
@@ -647,7 +1021,6 @@ export const createCloudflareBuilderPlugin = (
647
1021
  appContext: api.getAppContext(),
648
1022
  environments,
649
1023
  normalizedConfig: api.getNormalizedConfig(),
650
- resolveDeployProvider,
651
1024
  }),
652
1025
  );
653
1026
  },