@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.8 → 3.9.3-ultramodern.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 (280) 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 +164 -18
  37. package/dist/cjs/policy-defaults.js +110 -0
  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/runtime-package-resolution.js +118 -0
  48. package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  49. package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  50. package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  51. package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  52. package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  53. package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
  54. package/dist/cjs/workspace-topology.js +84 -0
  55. package/dist/esm/backend-federation-build/codegen.mjs +29 -18
  56. package/dist/esm/backend-federation-build/config.mjs +74 -38
  57. package/dist/esm/backend-federation-build/emit.mjs +64 -23
  58. package/dist/esm/build-config/build-environment.mjs +26 -160
  59. package/dist/esm/build-config/internal-effect-discovery.mjs +102 -0
  60. package/dist/esm/build-config/public.mjs +1 -1
  61. package/dist/esm/build-config/remote-address.mjs +20 -0
  62. package/dist/esm/cloudflare/artifacts.mjs +8 -19
  63. package/dist/esm/cloudflare/delivery-unit.mjs +124 -45
  64. package/dist/esm/cloudflare/index.mjs +15 -5
  65. package/dist/esm/cloudflare/native-renderer.mjs +48 -0
  66. package/dist/esm/cloudflare/renderer-identity.mjs +57 -0
  67. package/dist/esm/cloudflare/worker-manifest.mjs +55 -14
  68. package/dist/esm/cloudflare/worker-options.mjs +10 -0
  69. package/dist/esm/cloudflare/wrangler-config.mjs +203 -12
  70. package/dist/esm/cloudflare-builder.mjs +301 -89
  71. package/dist/esm/cloudflare-output-contract.mjs +29 -3
  72. package/dist/esm/cloudflare-output-verifier/identity.mjs +113 -24
  73. package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
  74. package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
  75. package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  76. package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  77. package/dist/esm/config.mjs +3 -1
  78. package/dist/esm/deploy-output/npmAliases.mjs +92 -6
  79. package/dist/esm/deploy-output/plugin.mjs +24 -6
  80. package/dist/esm/deploy-output/public-assets.mjs +82 -0
  81. package/dist/esm/deploy-output/target.mjs +50 -3
  82. package/dist/esm/deploy-output/trace-files.mjs +47 -0
  83. package/dist/esm/module-federation-config/exposes.mjs +18 -0
  84. package/dist/esm/module-federation-config/index.mjs +4 -0
  85. package/dist/esm/module-federation-config/inspect.mjs +65 -0
  86. package/dist/esm/module-federation-config/object-literal.mjs +43 -0
  87. package/dist/esm/module-federation-config/syntax.mjs +101 -0
  88. package/dist/esm/module-federation-config/types.mjs +0 -0
  89. package/dist/esm/native-type-checker.mjs +158 -18
  90. package/dist/esm/policy-defaults.mjs +51 -0
  91. package/dist/esm/release-envelope/canonical.mjs +2 -9
  92. package/dist/esm/release-envelope/contract.mjs +21 -0
  93. package/dist/esm/release-envelope/framework-output.mjs +297 -106
  94. package/dist/esm/release-envelope/index.mjs +149 -38
  95. package/dist/esm/release-envelope/plugin.mjs +24 -15
  96. package/dist/esm/release-envelope/renderer-output-stamp.mjs +112 -0
  97. package/dist/esm/release-envelope/types.mjs +2 -3
  98. package/dist/esm/release-identity.mjs +8 -5
  99. package/dist/esm/renderer-build-identity.mjs +82 -0
  100. package/dist/esm/runtime-package-resolution.mjs +60 -0
  101. package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  102. package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  103. package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  104. package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  105. package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  106. package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
  107. package/dist/esm/workspace-topology.mjs +21 -0
  108. package/dist/esm-node/backend-federation-build/codegen.mjs +29 -18
  109. package/dist/esm-node/backend-federation-build/config.mjs +74 -38
  110. package/dist/esm-node/backend-federation-build/emit.mjs +64 -23
  111. package/dist/esm-node/build-config/build-environment.mjs +26 -160
  112. package/dist/esm-node/build-config/internal-effect-discovery.mjs +103 -0
  113. package/dist/esm-node/build-config/public.mjs +1 -1
  114. package/dist/esm-node/build-config/remote-address.mjs +21 -0
  115. package/dist/esm-node/cloudflare/artifacts.mjs +8 -19
  116. package/dist/esm-node/cloudflare/delivery-unit.mjs +124 -45
  117. package/dist/esm-node/cloudflare/index.mjs +15 -5
  118. package/dist/esm-node/cloudflare/native-renderer.mjs +49 -0
  119. package/dist/esm-node/cloudflare/renderer-identity.mjs +58 -0
  120. package/dist/esm-node/cloudflare/worker-manifest.mjs +55 -14
  121. package/dist/esm-node/cloudflare/worker-options.mjs +11 -0
  122. package/dist/esm-node/cloudflare/wrangler-config.mjs +203 -12
  123. package/dist/esm-node/cloudflare-builder.mjs +301 -89
  124. package/dist/esm-node/cloudflare-output-contract.mjs +29 -3
  125. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +113 -24
  126. package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
  127. package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
  128. package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  129. package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +11 -6
  130. package/dist/esm-node/config.mjs +3 -1
  131. package/dist/esm-node/deploy-output/npmAliases.mjs +92 -6
  132. package/dist/esm-node/deploy-output/plugin.mjs +24 -6
  133. package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
  134. package/dist/esm-node/deploy-output/target.mjs +50 -3
  135. package/dist/esm-node/deploy-output/trace-files.mjs +48 -0
  136. package/dist/esm-node/module-federation-config/exposes.mjs +19 -0
  137. package/dist/esm-node/module-federation-config/index.mjs +5 -0
  138. package/dist/esm-node/module-federation-config/inspect.mjs +66 -0
  139. package/dist/esm-node/module-federation-config/object-literal.mjs +44 -0
  140. package/dist/esm-node/module-federation-config/syntax.mjs +102 -0
  141. package/dist/esm-node/module-federation-config/types.mjs +1 -0
  142. package/dist/esm-node/native-type-checker.mjs +158 -18
  143. package/dist/esm-node/policy-defaults.mjs +52 -0
  144. package/dist/esm-node/release-envelope/canonical.mjs +2 -9
  145. package/dist/esm-node/release-envelope/contract.mjs +22 -0
  146. package/dist/esm-node/release-envelope/framework-output.mjs +297 -106
  147. package/dist/esm-node/release-envelope/index.mjs +149 -38
  148. package/dist/esm-node/release-envelope/plugin.mjs +24 -15
  149. package/dist/esm-node/release-envelope/renderer-output-stamp.mjs +113 -0
  150. package/dist/esm-node/release-envelope/types.mjs +2 -3
  151. package/dist/esm-node/release-identity.mjs +8 -5
  152. package/dist/esm-node/renderer-build-identity.mjs +83 -0
  153. package/dist/esm-node/runtime-package-resolution.mjs +61 -0
  154. package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  155. package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  156. package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  157. package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  158. package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  159. package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
  160. package/dist/esm-node/workspace-topology.mjs +22 -0
  161. package/dist/types/backend-federation-build/codegen.d.ts +30 -0
  162. package/dist/types/backend-federation-build/config.d.ts +10 -57
  163. package/dist/types/backend-federation-build/emit.d.ts +12 -2
  164. package/dist/types/backend-federation-build/index.d.ts +1 -1
  165. package/dist/types/build-config/build-environment.d.ts +1 -9
  166. package/dist/types/build-config/internal-effect-discovery.d.ts +4 -0
  167. package/dist/types/build-config/public.d.ts +1 -1
  168. package/dist/types/build-config/remote-address.d.ts +3 -0
  169. package/dist/types/cloudflare/artifacts.d.ts +7 -6
  170. package/dist/types/cloudflare/constants.d.ts +1 -1
  171. package/dist/types/cloudflare/delivery-unit.d.ts +38 -7
  172. package/dist/types/cloudflare/native-renderer.d.ts +30 -0
  173. package/dist/types/cloudflare/renderer-identity.d.ts +25 -0
  174. package/dist/types/cloudflare/worker-manifest.d.ts +17 -2
  175. package/dist/types/cloudflare/worker-options.d.ts +20 -0
  176. package/dist/types/cloudflare/wrangler-config.d.ts +55 -2
  177. package/dist/types/cloudflare-builder.d.ts +51 -8
  178. package/dist/types/cloudflare-output-contract.d.ts +5 -3
  179. package/dist/types/cloudflare-output-verifier/identity.d.ts +7 -3
  180. package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
  181. package/dist/types/config.d.ts +98 -13
  182. package/dist/types/deploy-output/plugin.d.ts +29 -9
  183. package/dist/types/deploy-output/public-assets.d.ts +51 -0
  184. package/dist/types/deploy-output/target.d.ts +28 -5
  185. package/dist/types/deploy-output/trace-files.d.ts +17 -0
  186. package/dist/types/module-federation-config/exposes.d.ts +11 -0
  187. package/dist/types/module-federation-config/index.d.ts +5 -0
  188. package/dist/types/module-federation-config/inspect.d.ts +2 -0
  189. package/dist/types/module-federation-config/object-literal.d.ts +4 -0
  190. package/dist/types/module-federation-config/syntax.d.ts +7 -0
  191. package/dist/types/module-federation-config/types.d.ts +20 -0
  192. package/dist/types/native-type-checker.d.ts +26 -3
  193. package/dist/types/policy-defaults.d.ts +56 -0
  194. package/dist/types/release-envelope/canonical.d.ts +5 -5
  195. package/dist/types/release-envelope/contract.d.mts +8 -0
  196. package/dist/types/release-envelope/framework-output.d.ts +23 -13
  197. package/dist/types/release-envelope/index.d.ts +17 -5
  198. package/dist/types/release-envelope/plugin.d.ts +10 -6
  199. package/dist/types/release-envelope/renderer-output-stamp.d.ts +34 -0
  200. package/dist/types/release-envelope/types.d.ts +42 -4
  201. package/dist/types/release-identity.d.ts +3 -2
  202. package/dist/types/renderer-build-identity.d.ts +30 -0
  203. package/dist/types/runtime-package-resolution.d.ts +28 -0
  204. package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
  205. package/dist/types/workspace-topology.d.ts +91 -0
  206. package/package.json +97 -26
  207. package/src/backend-federation-build/codegen.ts +28 -19
  208. package/src/backend-federation-build/config.ts +189 -123
  209. package/src/backend-federation-build/emit.ts +119 -38
  210. package/src/backend-federation-build/index.ts +4 -1
  211. package/src/build-config/build-environment.ts +59 -285
  212. package/src/build-config/internal-effect-discovery.ts +239 -0
  213. package/src/build-config/public.ts +0 -1
  214. package/src/build-config/remote-address.ts +33 -0
  215. package/src/cloudflare/artifacts.ts +22 -43
  216. package/src/cloudflare/delivery-unit.ts +283 -60
  217. package/src/cloudflare/index.ts +35 -19
  218. package/src/cloudflare/native-renderer.ts +117 -0
  219. package/src/cloudflare/renderer-identity.ts +171 -0
  220. package/src/cloudflare/worker-manifest.ts +108 -10
  221. package/src/cloudflare/worker-options.ts +60 -0
  222. package/src/cloudflare/wrangler-config.ts +390 -14
  223. package/src/cloudflare-builder.ts +543 -170
  224. package/src/cloudflare-output-contract.ts +45 -2
  225. package/src/cloudflare-output-verifier/identity.ts +138 -7
  226. package/src/cloudflare-output-verifier/index.ts +20 -0
  227. package/src/cloudflare-output-verifier/manifest.ts +77 -0
  228. package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
  229. package/src/cloudflare-output-verifier/worker-bundles.ts +27 -11
  230. package/src/config.ts +101 -19
  231. package/src/deploy-output/npmAliases.ts +141 -4
  232. package/src/deploy-output/plugin.ts +55 -18
  233. package/src/deploy-output/public-assets.ts +224 -0
  234. package/src/deploy-output/target.ts +97 -14
  235. package/src/deploy-output/trace-files.ts +80 -0
  236. package/src/module-federation-config/exposes.ts +46 -0
  237. package/src/module-federation-config/index.ts +22 -0
  238. package/src/module-federation-config/inspect.ts +157 -0
  239. package/src/module-federation-config/object-literal.ts +76 -0
  240. package/src/module-federation-config/syntax.ts +212 -0
  241. package/src/module-federation-config/types.ts +22 -0
  242. package/src/native-type-checker.ts +281 -26
  243. package/src/policy-defaults.ts +157 -0
  244. package/src/release-envelope/canonical.ts +6 -18
  245. package/src/release-envelope/contract.mjs +28 -0
  246. package/src/release-envelope/framework-output.ts +681 -156
  247. package/src/release-envelope/index.ts +315 -60
  248. package/src/release-envelope/plugin.ts +54 -38
  249. package/src/release-envelope/renderer-output-stamp.ts +210 -0
  250. package/src/release-envelope/types.ts +75 -6
  251. package/src/release-identity.ts +19 -2
  252. package/src/renderer-build-identity.ts +168 -0
  253. package/src/runtime-package-resolution.ts +115 -0
  254. package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
  255. package/src/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
  256. package/src/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
  257. package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
  258. package/src/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
  259. package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
  260. package/src/workspace-topology.ts +125 -0
  261. package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +0 -11
  262. package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +0 -281
  263. package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  264. package/dist/cjs/templates/cloudflare-worker-path.mjs +0 -98
  265. package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +0 -11
  266. package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +0 -281
  267. package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  268. package/dist/esm/templates/cloudflare-worker-path.mjs +0 -98
  269. package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +0 -11
  270. package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +0 -281
  271. package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  272. package/dist/esm-node/templates/cloudflare-worker-path.mjs +0 -98
  273. package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +0 -5
  274. package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +0 -48
  275. package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +0 -13
  276. package/dist/types/templates/cloudflare-worker-path.d.mts +0 -21
  277. package/src/templates/cloudflare-worker-fs-promises.mjs +0 -11
  278. package/src/templates/cloudflare-worker-loadable-server.mjs +0 -281
  279. package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  280. package/src/templates/cloudflare-worker-path.mjs +0 -98
@@ -1,27 +1,226 @@
1
1
  import { execFile } from 'node:child_process';
2
- import { realpathSync } from 'node:fs';
2
+ import { randomUUID } from 'node:crypto';
3
+ import {
4
+ existsSync,
5
+ readFileSync,
6
+ realpathSync,
7
+ statSync,
8
+ unlinkSync,
9
+ writeFileSync,
10
+ } from 'node:fs';
3
11
  import path from 'node:path';
4
12
  import { promisify } from 'node:util';
13
+ import { refreshTsgoCheckerConfig } from '@modern-js/builder';
5
14
  import type { Rspack, RspackChain } from '@rsbuild/core';
15
+ import { type ParseError, parse, printParseErrorCode } from 'jsonc-parser';
6
16
 
7
17
  const execute = promisify(execFile);
8
18
  const name = 'UltramodernNativeTypeChecker';
9
19
 
20
+ export interface NativeTypeCheckerCommand {
21
+ executable: string;
22
+ args: readonly string[];
23
+ }
24
+
25
+ /** Launch the selected stable package through its declared public compiler bin. */
26
+ export function resolveNativeTypeCheckerCommand(
27
+ packageJsonPath: string,
28
+ ): NativeTypeCheckerCommand {
29
+ const manifestFile = realpathSync(packageJsonPath);
30
+ const directory = path.dirname(manifestFile);
31
+ const manifest = JSON.parse(readFileSync(manifestFile, 'utf8')) as {
32
+ name?: unknown;
33
+ version?: unknown;
34
+ bin?: { tsc?: unknown };
35
+ };
36
+ if (manifest.name !== 'typescript' || manifest.version !== '7.0.2') {
37
+ throw new Error(
38
+ `${name} requires typescript@7.0.2; found ${String(manifest.name)}@${String(manifest.version)} at ${manifestFile}.`,
39
+ );
40
+ }
41
+ const bin = manifest.bin?.tsc;
42
+ if (typeof bin !== 'string' || !bin || path.isAbsolute(bin)) {
43
+ throw new Error(`${name} requires the selected package's public tsc bin.`);
44
+ }
45
+ const launcher = realpathSync(path.resolve(directory, bin));
46
+ const relative = path.relative(directory, launcher);
47
+ if (
48
+ !relative ||
49
+ path.isAbsolute(relative) ||
50
+ relative === '..' ||
51
+ relative.startsWith(`..${path.sep}`) ||
52
+ !statSync(launcher).isFile()
53
+ ) {
54
+ throw new Error(
55
+ `${name} public tsc bin must belong to its selected package.`,
56
+ );
57
+ }
58
+ return { executable: process.execPath, args: [launcher] };
59
+ }
60
+
61
+ interface TypeCheckerConfig {
62
+ compilerOptions?: Record<string, unknown>;
63
+ references?: Array<{ path: string }>;
64
+ [key: string]: unknown;
65
+ }
66
+
10
67
  // Windows file events expand 8.3 aliases; register the same native path spelling.
11
- const watchDependencyPath = (file: string): string =>
12
- process.platform === 'win32' ? realpathSync.native(file) : file;
68
+ // A path that does not exist yet (a project tsconfig mid-edit) cannot be
69
+ // realpathed; it is registered as spelled so its return still triggers a build.
70
+ const watchDependencyPath = (file: string): string => {
71
+ if (process.platform !== 'win32') return file;
72
+ try {
73
+ return realpathSync.native(file);
74
+ } catch {
75
+ return file;
76
+ }
77
+ };
78
+
79
+ /**
80
+ * TS2875 means JSX compiles against a jsxImportSource whose runtime is not
81
+ * installed: usually components still authored for a previous renderer.
82
+ */
83
+ export function missingJsxRuntimeHint(diagnostics: string): string {
84
+ const source =
85
+ /error TS2875: This JSX tag requires the module path '(.+?)\/jsx-(?:dev-)?runtime'/u.exec(
86
+ diagnostics,
87
+ )?.[1];
88
+ if (!source) return '';
89
+ return `\nHint: JSX compiles against jsxImportSource '${source}', the selected renderer's runtime, but '${source}/jsx-runtime' cannot be resolved from this app. If you changed \`renderer\` in modern.config, install that renderer's packages and port components authored for the previous renderer, or switch \`renderer\` back.\n`;
90
+ }
13
91
 
14
92
  export class UltramodernNativeTypeChecker {
15
93
  constructor(
16
94
  private readonly options: {
17
- compiler: () => string;
95
+ compiler: () => string | NativeTypeCheckerCommand;
18
96
  configFile: string;
19
97
  build: boolean;
98
+ configOverwrite?: TypeCheckerConfig;
20
99
  },
21
100
  ) {}
22
101
 
23
102
  async check(): Promise<void> {
24
- const { configFile, build } = this.options;
103
+ const configFile = await this.projectConfigFile();
104
+ try {
105
+ await this.checkProject(configFile);
106
+ } finally {
107
+ this.removeProjection(configFile);
108
+ }
109
+ }
110
+
111
+ private async projectConfigFile(): Promise<string> {
112
+ const { configFile, configOverwrite } = this.options;
113
+ if (!configOverwrite) return configFile;
114
+ // Merge before native parsing: an overwrite may supply the inputs that
115
+ // make an otherwise empty project valid. A same-directory projection
116
+ // preserves the original extends, relative paths and project references.
117
+ const errors: ParseError[] = [];
118
+ const source = readFileSync(configFile, 'utf8');
119
+ const project: TypeCheckerConfig = parse(
120
+ source.charCodeAt(0) === 0xfeff ? source.slice(1) : source,
121
+ errors,
122
+ { allowTrailingComma: true },
123
+ );
124
+ if (errors.length) {
125
+ throw new Error(
126
+ `${name} requires valid JSONC in ${configFile}: ${errors
127
+ .map(
128
+ error => `${printParseErrorCode(error.error)} at ${error.offset}`,
129
+ )
130
+ .join(', ')}`,
131
+ );
132
+ }
133
+ if (!project || typeof project !== 'object' || Array.isArray(project)) {
134
+ throw new Error(`${name} requires a JSONC object in ${configFile}.`);
135
+ }
136
+ const projectedConfig: TypeCheckerConfig = {
137
+ ...project,
138
+ ...configOverwrite,
139
+ compilerOptions: {
140
+ ...project.compilerOptions,
141
+ ...configOverwrite.compilerOptions,
142
+ },
143
+ };
144
+ const projected = path.join(
145
+ path.dirname(configFile),
146
+ `.ultramodern-native-checker.${randomUUID()}.json`,
147
+ );
148
+ writeFileSync(projected, `${JSON.stringify(projectedConfig, null, 2)}\n`, {
149
+ flag: 'wx',
150
+ });
151
+ try {
152
+ const resolved: TypeCheckerConfig = JSON.parse(
153
+ await this.run(['--showConfig', '--project', projected]),
154
+ );
155
+ const options = resolved.compilerOptions ?? {};
156
+ if (
157
+ (this.options.build ||
158
+ options.incremental === true ||
159
+ options.composite === true) &&
160
+ !options.tsBuildInfoFile
161
+ ) {
162
+ // Native TypeScript derives the default from the config filename.
163
+ // Retain that original identity while this invocation-private config
164
+ // is alive; referenced projects still use their own configurations.
165
+ const original = configFile.slice(
166
+ 0,
167
+ configFile.length - path.extname(configFile).length,
168
+ );
169
+ let buildInfo = original;
170
+ if (typeof options.outDir === 'string') {
171
+ const outDir = path.resolve(path.dirname(configFile), options.outDir);
172
+ buildInfo =
173
+ typeof options.rootDir === 'string'
174
+ ? path.resolve(
175
+ outDir,
176
+ path.relative(
177
+ path.resolve(path.dirname(configFile), options.rootDir),
178
+ original,
179
+ ),
180
+ )
181
+ : path.join(outDir, path.basename(original));
182
+ }
183
+ projectedConfig.compilerOptions = {
184
+ ...projectedConfig.compilerOptions,
185
+ tsBuildInfoFile: `${buildInfo}.tsbuildinfo`,
186
+ };
187
+ writeFileSync(
188
+ projected,
189
+ `${JSON.stringify(projectedConfig, null, 2)}\n`,
190
+ );
191
+ }
192
+ return projected;
193
+ } catch (error) {
194
+ unlinkSync(projected);
195
+ throw error;
196
+ }
197
+ }
198
+
199
+ private removeProjection(configFile: string): void {
200
+ if (configFile !== this.options.configFile) unlinkSync(configFile);
201
+ }
202
+
203
+ private async checkProject(configFile: string): Promise<void> {
204
+ const { build } = this.options;
205
+ if (!build) {
206
+ const config = JSON.parse(
207
+ await this.run(['--showConfig', '--project', configFile]),
208
+ ) as { references?: { path: string }[] };
209
+ // Project mode consumes referenced declarations but never builds them.
210
+ // Prepare only the references using their own emit contracts, then keep
211
+ // the app itself in no-emit mode. This also refreshes stale declarations.
212
+ if (config.references?.length) {
213
+ await this.run([
214
+ '--build',
215
+ ...config.references.map(reference =>
216
+ path.resolve(path.dirname(configFile), reference.path),
217
+ ),
218
+ '--stopBuildOnErrors',
219
+ '--pretty',
220
+ 'false',
221
+ ]);
222
+ }
223
+ }
25
224
  const args = build
26
225
  ? ['--build', configFile, '--stopBuildOnErrors']
27
226
  : ['--project', configFile, '--noEmit'];
@@ -30,35 +229,58 @@ export class UltramodernNativeTypeChecker {
30
229
 
31
230
  private async run(args: string[]): Promise<string> {
32
231
  try {
33
- const result = await execute(this.options.compiler(), args, {
34
- cwd: path.dirname(this.options.configFile),
35
- maxBuffer: 16 * 1024 * 1024,
36
- });
232
+ const selected = this.options.compiler();
233
+ const command =
234
+ typeof selected === 'string'
235
+ ? { executable: selected, args: [] }
236
+ : selected;
237
+ const result = await execute(
238
+ command.executable,
239
+ [...command.args, ...args],
240
+ {
241
+ cwd: path.dirname(this.options.configFile),
242
+ maxBuffer: 16 * 1024 * 1024,
243
+ },
244
+ );
37
245
  return result.stdout;
38
246
  } catch (cause) {
39
247
  const output = cause as { stdout?: string; stderr?: string };
40
248
  const diagnostics = `${output.stdout ?? ''}${output.stderr ?? ''}`;
41
249
  throw new Error(
42
- `${name} failed:\n${diagnostics || (cause instanceof Error ? cause.message : String(cause))}`,
250
+ `${name} failed:\n${diagnostics || (cause instanceof Error ? cause.message : String(cause))}${missingJsxRuntimeHint(diagnostics)}`,
43
251
  { cause },
44
252
  );
45
253
  }
46
254
  }
47
255
 
48
- private async watchInputs(): Promise<Set<string>> {
256
+ private async watchInputs(configFile: string): Promise<{
257
+ inputs: Set<string>;
258
+ missing: Set<string>;
259
+ }> {
49
260
  const inputs = new Set<string>();
50
- const projects = [this.options.configFile];
261
+ const missing = new Set<string>();
262
+ const projects = [configFile];
51
263
  const visited = new Set<string>();
52
264
  for (const configFile of projects) {
53
265
  if (visited.has(configFile)) continue;
54
266
  visited.add(configFile);
55
- inputs.add(configFile);
56
- const config = JSON.parse(
57
- await this.run(['--showConfig', '--project', configFile]),
58
- ) as {
59
- files?: string[];
60
- references?: { path: string }[];
61
- };
267
+ // Registered before it is parsed: a referenced config that is absent or
268
+ // unparsable right now must still be watched, or creating or repairing
269
+ // it never re-triggers the compilation. `check()` reports the error.
270
+ if (configFile === this.options.configFile || configFile !== projects[0])
271
+ inputs.add(configFile);
272
+ if (!existsSync(configFile)) {
273
+ missing.add(configFile);
274
+ continue;
275
+ }
276
+ let config: { files?: string[]; references?: { path: string }[] };
277
+ try {
278
+ config = JSON.parse(
279
+ await this.run(['--showConfig', '--project', configFile]),
280
+ );
281
+ } catch {
282
+ continue;
283
+ }
62
284
  const directory = path.dirname(configFile);
63
285
  for (const file of config.files ?? [])
64
286
  inputs.add(path.resolve(directory, file));
@@ -71,7 +293,7 @@ export class UltramodernNativeTypeChecker {
71
293
  );
72
294
  }
73
295
  }
74
- return inputs;
296
+ return { inputs, missing };
75
297
  }
76
298
 
77
299
  apply(compiler: Rspack.Compiler): void {
@@ -81,9 +303,34 @@ export class UltramodernNativeTypeChecker {
81
303
  compilation.fileDependencies.add(
82
304
  watchDependencyPath(this.options.configFile),
83
305
  );
84
- for (const file of await this.watchInputs())
85
- compilation.fileDependencies.add(watchDependencyPath(file));
86
- await this.check();
306
+ // A generated checker config restates values TypeScript does not
307
+ // inherit through `extends` (project references above all). Rebuild
308
+ // it from the project's own tsconfig before every check, and watch
309
+ // that tsconfig, so an edit during `modern dev` reaches the next
310
+ // compilation instead of the next restart.
311
+ const projectConfigFile = refreshTsgoCheckerConfig(
312
+ this.options.configFile,
313
+ );
314
+ if (projectConfigFile) {
315
+ const watched = watchDependencyPath(projectConfigFile);
316
+ compilation.fileDependencies.add(watched);
317
+ // Absent mid-edit: a missing dependency makes its (re)appearance
318
+ // a rebuild trigger, so the dev server recovers on its own.
319
+ if (!existsSync(projectConfigFile)) {
320
+ compilation.missingDependencies.add(watched);
321
+ }
322
+ }
323
+ const configFile = await this.projectConfigFile();
324
+ try {
325
+ const { inputs, missing } = await this.watchInputs(configFile);
326
+ for (const file of inputs)
327
+ compilation.fileDependencies.add(watchDependencyPath(file));
328
+ for (const file of missing)
329
+ compilation.missingDependencies.add(watchDependencyPath(file));
330
+ await this.checkProject(configFile);
331
+ } finally {
332
+ this.removeProjection(configFile);
333
+ }
87
334
  } catch (cause) {
88
335
  compilation.errors.push(
89
336
  new Error(cause instanceof Error ? cause.message : String(cause), {
@@ -96,15 +343,22 @@ export class UltramodernNativeTypeChecker {
96
343
  }
97
344
  }
98
345
 
99
- /** Preserve project-reference emit contracts in the native Effect compiler. */
346
+ /** Preserve project-reference emit contracts in the selected native compiler. */
100
347
  export function configureUltramodernTypeChecker(
101
348
  chain: RspackChain,
102
349
  pluginId: string,
103
- resolveCompiler: (configFile: string) => string,
350
+ resolveCompiler: (configFile: string) => string | NativeTypeCheckerCommand,
104
351
  ): void {
105
352
  if (!chain.plugins.has(pluginId)) return;
106
353
  const options = chain.plugin(pluginId).get('args')?.[0] as
107
- | { typescript?: { configFile?: string; build?: boolean; tsgo?: boolean } }
354
+ | {
355
+ typescript?: {
356
+ configFile?: string;
357
+ build?: boolean;
358
+ tsgo?: boolean;
359
+ configOverwrite?: TypeCheckerConfig;
360
+ };
361
+ }
108
362
  | undefined;
109
363
  const typescript = options?.typescript;
110
364
  if (typescript?.tsgo === false) return;
@@ -117,6 +371,7 @@ export function configureUltramodernTypeChecker(
117
371
  build: typescript.build === true,
118
372
  compiler: () => resolveCompiler(configFile),
119
373
  configFile,
374
+ configOverwrite: typescript.configOverwrite,
120
375
  },
121
376
  ]);
122
377
  }
@@ -0,0 +1,157 @@
1
+ import {
2
+ collectRuntimePackageModuleDirectories,
3
+ createRuntimePackageResolutionPlugin,
4
+ } from './runtime-package-resolution';
5
+
6
+ /**
7
+ * The fork's renderer policy (head rendering, HTML template assembly and asset
8
+ * ordering) and server policy (localised loaders, error responses, telemetry,
9
+ * module-federation CSS and asset cache headers, static serving) are the
10
+ * default behaviour of an UltraModern.js app.
11
+ *
12
+ * They live in their own packages so they can also be composed explicitly, but
13
+ * a plain `appTools()` app gets them without opting in — that is what this fork
14
+ * has always done. Pass `appTools({ rendererExtensions: false })` or
15
+ * `appTools({ serverExtensions: false })` to run without them.
16
+ */
17
+ export const RENDERER_EXTENSIONS_PACKAGE =
18
+ '@modern-js/runtime-renderer-extensions';
19
+
20
+ export const RENDERER_EXTENSIONS_PLUGIN_NAME = 'rendererHead';
21
+
22
+ export const SERVER_EXTENSIONS_PLUGIN_NAME =
23
+ '@modern-js/app-tools/server-plugin';
24
+
25
+ export const ULTRAMODERN_SERVER_EXTENSIONS_PLUGIN_NAME =
26
+ '@modern-js/ultramodern-app-tools/server-plugin';
27
+
28
+ export interface PolicyDefaultsOptions {
29
+ /** Default `true`. Set to `false` to drop the fork's renderer policy. */
30
+ rendererExtensions?: boolean;
31
+ /** Default `true`. Set to `false` to drop the fork's server policy. */
32
+ serverExtensions?: boolean;
33
+ }
34
+
35
+ /** The native package that explicitly composes the policy. */
36
+ export interface PolicyDefaultsComposition {
37
+ pluginName?: string;
38
+ serverPluginName?: string;
39
+ /** Additional runtime packages that share the resolution fallback. */
40
+ runtimePackages?: readonly string[];
41
+ /** Resolve runtime packages from the composing package. */
42
+ registrarUrl?: string;
43
+ }
44
+
45
+ type RuntimePluginDescriptor = {
46
+ name?: string;
47
+ path: string;
48
+ config?: unknown;
49
+ };
50
+
51
+ type ServerPluginDescriptor = { name: string };
52
+
53
+ /**
54
+ * The subset of the CLI plugin API these defaults use. Typed structurally, and
55
+ * loosely at the hook boundary, so this package stays free of a dependency on
56
+ * the solution that consumes it — which depends on this one.
57
+ */
58
+ export interface PolicyDefaultsPluginApi {
59
+ _internalRuntimePlugins(fn: (input: any) => any): void;
60
+ _internalServerPlugins(fn: (input: any) => any): void;
61
+ modifyResolvedConfig(fn: (config: any) => any): void;
62
+ }
63
+
64
+ type RuntimePluginsInput = {
65
+ entrypoint: unknown;
66
+ plugins: RuntimePluginDescriptor[];
67
+ };
68
+
69
+ type ServerPluginsInput = { plugins: ServerPluginDescriptor[] };
70
+
71
+ export const POLICY_DEFAULTS_PLUGIN_NAME =
72
+ '@modern-js/ultramodern-policy-defaults';
73
+
74
+ /** Registers the fork's default renderer and server policy. */
75
+ export const applyPolicyDefaults = (
76
+ api: PolicyDefaultsPluginApi,
77
+ options: PolicyDefaultsOptions = {},
78
+ composition: PolicyDefaultsComposition = {},
79
+ ): void => {
80
+ if (options.rendererExtensions !== false) {
81
+ api._internalRuntimePlugins(
82
+ ({ entrypoint, plugins }: RuntimePluginsInput) => {
83
+ // An explicit composition (`ultramodernAppTools()`) may already have
84
+ // registered it; the descriptor list must stay free of duplicates.
85
+ if (
86
+ !plugins.some(plugin => plugin.path === RENDERER_EXTENSIONS_PACKAGE)
87
+ ) {
88
+ plugins.push({
89
+ name: RENDERER_EXTENSIONS_PLUGIN_NAME,
90
+ path: RENDERER_EXTENSIONS_PACKAGE,
91
+ config: {},
92
+ });
93
+ }
94
+ return { entrypoint, plugins };
95
+ },
96
+ );
97
+ }
98
+
99
+ const runtimePackages = [
100
+ ...(options.rendererExtensions !== false
101
+ ? [RENDERER_EXTENSIONS_PACKAGE]
102
+ : []),
103
+ ...(composition.runtimePackages ?? []).filter(
104
+ packageName => packageName !== RENDERER_EXTENSIONS_PACKAGE,
105
+ ),
106
+ ];
107
+ const moduleDirectories = collectRuntimePackageModuleDirectories(
108
+ runtimePackages,
109
+ composition.registrarUrl ?? import.meta.url,
110
+ );
111
+ if (moduleDirectories.length > 0) {
112
+ api.modifyResolvedConfig(config => ({
113
+ ...config,
114
+ builderPlugins: [
115
+ ...(config.builderPlugins ?? []),
116
+ createRuntimePackageResolutionPlugin(moduleDirectories),
117
+ ],
118
+ }));
119
+ }
120
+
121
+ if (options.serverExtensions !== false) {
122
+ const serverPluginName =
123
+ composition.serverPluginName ?? SERVER_EXTENSIONS_PLUGIN_NAME;
124
+ api._internalServerPlugins(({ plugins }: ServerPluginsInput) => {
125
+ if (
126
+ !plugins.some(
127
+ plugin =>
128
+ plugin.name === serverPluginName ||
129
+ plugin.name === SERVER_EXTENSIONS_PLUGIN_NAME ||
130
+ plugin.name === ULTRAMODERN_SERVER_EXTENSIONS_PLUGIN_NAME,
131
+ )
132
+ ) {
133
+ plugins.push({ name: serverPluginName });
134
+ }
135
+ return { plugins };
136
+ });
137
+ }
138
+ };
139
+
140
+ /**
141
+ * The fork defaults as a CLI plugin.
142
+ *
143
+ * The descriptor this registers may land before the ones it wraps — CLI
144
+ * ordering cannot be constrained here without a cycle, because the plugins
145
+ * that emit those descriptors are themselves ordered after `appTools()`.
146
+ * Ordering is settled on the runtime side instead: `rendererHeadPlugin`
147
+ * declares the runtime plugins that run before it.
148
+ */
149
+ export const createPolicyDefaultsPlugin = (
150
+ options: PolicyDefaultsOptions = {},
151
+ composition: PolicyDefaultsComposition = {},
152
+ ) => ({
153
+ name: composition.pluginName ?? POLICY_DEFAULTS_PLUGIN_NAME,
154
+ setup(api: PolicyDefaultsPluginApi) {
155
+ applyPolicyDefaults(api, options, composition);
156
+ },
157
+ });
@@ -1,8 +1,7 @@
1
1
  import { createHash } from 'node:crypto';
2
- import type {
3
- MicroVerticalReleaseEnvelope,
4
- MicroVerticalReleaseEnvelopePayload,
5
- } from './types';
2
+ import type { ReleaseEnvelope, ReleaseEnvelopePayload } from './types';
3
+
4
+ export { releaseEnvelopePayload } from './contract.mjs';
6
5
 
7
6
  type CanonicalValue =
8
7
  | null
@@ -31,27 +30,16 @@ const serializeCanonicalValue = (value: CanonicalValue): string => {
31
30
  .join(',')}}`;
32
31
  };
33
32
 
34
- export const releaseEnvelopePayload = (
35
- envelope: MicroVerticalReleaseEnvelope,
36
- ): MicroVerticalReleaseEnvelopePayload => ({
37
- schemaVersion: envelope.schemaVersion,
38
- kind: envelope.kind,
39
- target: envelope.target,
40
- identity: envelope.identity,
41
- artifacts: envelope.artifacts,
42
- surfaces: envelope.surfaces,
43
- });
44
-
45
33
  export const canonicalSerializeMicroVerticalReleaseEnvelopePayload = (
46
- payload: MicroVerticalReleaseEnvelopePayload,
34
+ payload: ReleaseEnvelopePayload,
47
35
  ) => serializeCanonicalValue(payload as unknown as CanonicalValue);
48
36
 
49
37
  export const canonicalSerializeMicroVerticalReleaseEnvelope = (
50
- envelope: MicroVerticalReleaseEnvelope,
38
+ envelope: ReleaseEnvelope,
51
39
  ) => serializeCanonicalValue(envelope as unknown as CanonicalValue);
52
40
 
53
41
  export const digestMicroVerticalReleaseEnvelopePayload = (
54
- payload: MicroVerticalReleaseEnvelopePayload,
42
+ payload: ReleaseEnvelopePayload,
55
43
  ) =>
56
44
  createHash('sha256')
57
45
  .update(canonicalSerializeMicroVerticalReleaseEnvelopePayload(payload))
@@ -0,0 +1,28 @@
1
+ export const MICROVERTICAL_RELEASE_ENVELOPE_SCHEMA_VERSION =
2
+ /** @type {const} */ (5);
3
+
4
+ export const MICROVERTICAL_RELEASE_ENVELOPE_KIND = /** @type {const} */ (
5
+ 'ultramodern-target-microvertical-release-envelope'
6
+ );
7
+
8
+ export const SHELL_RELEASE_ENVELOPE_KIND = /** @type {const} */ (
9
+ 'ultramodern-target-shell-release-envelope'
10
+ );
11
+
12
+ /**
13
+ * @param {import('./types.js').ReleaseEnvelope} envelope
14
+ * @returns {import('./types.js').ReleaseEnvelopePayload}
15
+ */
16
+ export const releaseEnvelopePayload = envelope => {
17
+ const payload = {
18
+ schemaVersion: envelope.schemaVersion,
19
+ target: envelope.target,
20
+ identity: envelope.identity,
21
+ ...(envelope.ui ? { ui: envelope.ui } : {}),
22
+ artifacts: envelope.artifacts,
23
+ };
24
+ if (envelope.kind === SHELL_RELEASE_ENVELOPE_KIND) {
25
+ return { ...payload, kind: envelope.kind, surfaces: envelope.surfaces };
26
+ }
27
+ return { ...payload, kind: envelope.kind, surfaces: envelope.surfaces };
28
+ };