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

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 +30 -165
  6. package/dist/cjs/build-config/internal-effect-discovery.js +231 -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 +180 -17
  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 +9 -6
  46. package/dist/cjs/renderer-build-identity.js +144 -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 +547 -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 +31 -163
  58. package/dist/esm/build-config/internal-effect-discovery.mjs +187 -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 +175 -18
  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 +9 -6
  98. package/dist/esm/renderer-build-identity.mjs +92 -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 +547 -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 +31 -163
  110. package/dist/esm-node/build-config/internal-effect-discovery.mjs +188 -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 +175 -18
  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 +9 -6
  150. package/dist/esm-node/renderer-build-identity.mjs +93 -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 +547 -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 +4 -10
  163. package/dist/types/build-config/internal-effect-discovery.d.ts +6 -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 +20 -4
  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 +102 -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 +28 -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 +13 -2
  199. package/dist/types/renderer-build-identity.d.ts +41 -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 +69 -293
  208. package/src/build-config/internal-effect-discovery.ts +458 -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 +115 -13
  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 +105 -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 +284 -27
  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 +30 -4
  248. package/src/renderer-build-identity.ts +208 -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 +547 -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,13 +1,89 @@
1
- import { execFile } from 'node:child_process';
2
- import { existsSync, realpathSync } from 'node:fs';
1
+ import { execFile, execFileSync } from 'node:child_process';
2
+ import { randomUUID } from 'node:crypto';
3
+ import {
4
+ existsSync,
5
+ readFileSync,
6
+ realpathSync,
7
+ unlinkSync,
8
+ writeFileSync,
9
+ } from 'node:fs';
10
+ import { createRequire } from 'node:module';
3
11
  import path from 'node:path';
4
12
  import { promisify } from 'node:util';
5
13
  import { refreshTsgoCheckerConfig } from '@modern-js/builder';
6
14
  import type { Rspack, RspackChain } from '@rsbuild/core';
15
+ import { type ParseError, parse, printParseErrorCode } from 'jsonc-parser';
16
+ import { resolveEffectTsgoCompiler } from './build-config/build-environment';
7
17
 
8
18
  const execute = promisify(execFile);
9
19
  const name = 'UltramodernNativeTypeChecker';
10
20
 
21
+ export interface NativeTypeCheckerCommand {
22
+ executable: string;
23
+ args: readonly string[];
24
+ /** The selected package's native config API. tsrs has no --showConfig CLI. */
25
+ configApi?: string;
26
+ }
27
+
28
+ /** Resolve the Effect-enabled native compiler from the application's origin. */
29
+ export function resolveNativeTypeCheckerCommand(
30
+ from: string,
31
+ ): NativeTypeCheckerCommand {
32
+ const executable = resolveEffectTsgoCompiler({ from });
33
+ const version = execFileSync(executable, ['--version'], {
34
+ encoding: 'utf8',
35
+ env: {
36
+ ...process.env,
37
+ TSRS_CHECKER_ASSIGNMENT: process.env.TSRS_CHECKER_ASSIGNMENT || 'go',
38
+ },
39
+ timeout: 10_000,
40
+ });
41
+ if (version.includes('(tsrs ')) {
42
+ return {
43
+ executable,
44
+ args: [],
45
+ configApi: createRequire(from).resolve('tsrs/sync'),
46
+ };
47
+ }
48
+ // TypeScript-Go supports --showConfig, including project references. Its
49
+ // older native config API lacks tsrs's diagnostics and reference fields.
50
+ return {
51
+ executable,
52
+ args: [],
53
+ };
54
+ }
55
+
56
+ // Use the real native parser, not a handwritten tsconfig/extends interpreter.
57
+ // Running the client in a child also applies assignment to its native API
58
+ // process without changing the build/dev server's environment.
59
+ const inspectNativeConfig = `
60
+ import { pathToFileURL } from 'node:url';
61
+ const [modulePath, compiler, configFile] = process.argv.slice(1);
62
+ const { API, formatDiagnostics } = await import(pathToFileURL(modulePath).href);
63
+ const api = new API({ tsserverPath: compiler });
64
+ try {
65
+ const config = api.parseConfigFile(configFile);
66
+ if (config.errors.length) {
67
+ process.stdout.write(formatDiagnostics(config.errors, api));
68
+ process.exitCode = 1;
69
+ } else {
70
+ process.stdout.write(JSON.stringify({
71
+ compilerOptions: config.options,
72
+ files: config.fileNames,
73
+ references: config.projectReferences,
74
+ }));
75
+ }
76
+ } finally {
77
+ api.close();
78
+ }
79
+ `;
80
+
81
+ interface TypeCheckerConfig {
82
+ compilerOptions?: Record<string, unknown>;
83
+ references?: Array<{ path: string }>;
84
+ [key: string]: unknown;
85
+ }
86
+
11
87
  // Windows file events expand 8.3 aliases; register the same native path spelling.
12
88
  // A path that does not exist yet (a project tsconfig mid-edit) cannot be
13
89
  // realpathed; it is registered as spelled so its return still triggers a build.
@@ -20,17 +96,151 @@ const watchDependencyPath = (file: string): string => {
20
96
  }
21
97
  };
22
98
 
99
+ /**
100
+ * TS2875 means JSX compiles against a jsxImportSource whose runtime is not
101
+ * installed: usually components still authored for a previous renderer.
102
+ */
103
+ export function missingJsxRuntimeHint(diagnostics: string): string {
104
+ const source =
105
+ /error TS2875: This JSX tag requires the module path '(.+?)\/jsx-(?:dev-)?runtime'/u.exec(
106
+ diagnostics,
107
+ )?.[1];
108
+ if (!source) return '';
109
+ 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`;
110
+ }
111
+
23
112
  export class UltramodernNativeTypeChecker {
24
113
  constructor(
25
114
  private readonly options: {
26
- compiler: () => string;
115
+ compiler: () => string | NativeTypeCheckerCommand;
27
116
  configFile: string;
28
117
  build: boolean;
118
+ configOverwrite?: TypeCheckerConfig;
29
119
  },
30
120
  ) {}
31
121
 
32
122
  async check(): Promise<void> {
33
- const { configFile, build } = this.options;
123
+ const configFile = await this.projectConfigFile();
124
+ try {
125
+ await this.checkProject(configFile);
126
+ } finally {
127
+ this.removeProjection(configFile);
128
+ }
129
+ }
130
+
131
+ private async projectConfigFile(): Promise<string> {
132
+ const { configFile, configOverwrite } = this.options;
133
+ if (!configOverwrite) return configFile;
134
+ // Merge before native parsing: an overwrite may supply the inputs that
135
+ // make an otherwise empty project valid. A same-directory projection
136
+ // preserves the original extends, relative paths and project references.
137
+ const errors: ParseError[] = [];
138
+ const source = readFileSync(configFile, 'utf8');
139
+ const project: TypeCheckerConfig = parse(
140
+ source.charCodeAt(0) === 0xfeff ? source.slice(1) : source,
141
+ errors,
142
+ { allowTrailingComma: true },
143
+ );
144
+ if (errors.length) {
145
+ throw new Error(
146
+ `${name} requires valid JSONC in ${configFile}: ${errors
147
+ .map(
148
+ error => `${printParseErrorCode(error.error)} at ${error.offset}`,
149
+ )
150
+ .join(', ')}`,
151
+ );
152
+ }
153
+ if (!project || typeof project !== 'object' || Array.isArray(project)) {
154
+ throw new Error(`${name} requires a JSONC object in ${configFile}.`);
155
+ }
156
+ const projectedConfig: TypeCheckerConfig = {
157
+ ...project,
158
+ ...configOverwrite,
159
+ compilerOptions: {
160
+ ...project.compilerOptions,
161
+ ...configOverwrite.compilerOptions,
162
+ },
163
+ };
164
+ const projected = path.join(
165
+ path.dirname(configFile),
166
+ `.ultramodern-native-checker.${randomUUID()}.json`,
167
+ );
168
+ writeFileSync(projected, `${JSON.stringify(projectedConfig, null, 2)}\n`, {
169
+ flag: 'wx',
170
+ });
171
+ try {
172
+ const resolved: TypeCheckerConfig = JSON.parse(
173
+ await this.run(['--showConfig', '--project', projected]),
174
+ );
175
+ const options = resolved.compilerOptions ?? {};
176
+ if (
177
+ (this.options.build ||
178
+ options.incremental === true ||
179
+ options.composite === true) &&
180
+ !options.tsBuildInfoFile
181
+ ) {
182
+ // Native TypeScript derives the default from the config filename.
183
+ // Retain that original identity while this invocation-private config
184
+ // is alive; referenced projects still use their own configurations.
185
+ const original = configFile.slice(
186
+ 0,
187
+ configFile.length - path.extname(configFile).length,
188
+ );
189
+ let buildInfo = original;
190
+ if (typeof options.outDir === 'string') {
191
+ const outDir = path.resolve(path.dirname(configFile), options.outDir);
192
+ buildInfo =
193
+ typeof options.rootDir === 'string'
194
+ ? path.resolve(
195
+ outDir,
196
+ path.relative(
197
+ path.resolve(path.dirname(configFile), options.rootDir),
198
+ original,
199
+ ),
200
+ )
201
+ : path.join(outDir, path.basename(original));
202
+ }
203
+ projectedConfig.compilerOptions = {
204
+ ...projectedConfig.compilerOptions,
205
+ tsBuildInfoFile: `${buildInfo}.tsbuildinfo`,
206
+ };
207
+ writeFileSync(
208
+ projected,
209
+ `${JSON.stringify(projectedConfig, null, 2)}\n`,
210
+ );
211
+ }
212
+ return projected;
213
+ } catch (error) {
214
+ unlinkSync(projected);
215
+ throw error;
216
+ }
217
+ }
218
+
219
+ private removeProjection(configFile: string): void {
220
+ if (configFile !== this.options.configFile) unlinkSync(configFile);
221
+ }
222
+
223
+ private async checkProject(configFile: string): Promise<void> {
224
+ const { build } = this.options;
225
+ if (!build) {
226
+ const config = JSON.parse(
227
+ await this.run(['--showConfig', '--project', configFile]),
228
+ ) as { references?: { path: string }[] };
229
+ // Project mode consumes referenced declarations but never builds them.
230
+ // Prepare only the references using their own emit contracts, then keep
231
+ // the app itself in no-emit mode. This also refreshes stale declarations.
232
+ if (config.references?.length) {
233
+ await this.run([
234
+ '--build',
235
+ ...config.references.map(reference =>
236
+ path.resolve(path.dirname(configFile), reference.path),
237
+ ),
238
+ '--stopBuildOnErrors',
239
+ '--pretty',
240
+ 'false',
241
+ ]);
242
+ }
243
+ }
34
244
  const args = build
35
245
  ? ['--build', configFile, '--stopBuildOnErrors']
36
246
  : ['--project', configFile, '--noEmit'];
@@ -39,28 +249,63 @@ export class UltramodernNativeTypeChecker {
39
249
 
40
250
  private async run(args: string[]): Promise<string> {
41
251
  try {
42
- const result = await execute(this.options.compiler(), args, {
43
- cwd: path.dirname(this.options.configFile),
44
- maxBuffer: 16 * 1024 * 1024,
45
- });
252
+ const selected = this.options.compiler();
253
+ const command: NativeTypeCheckerCommand =
254
+ typeof selected === 'string'
255
+ ? { executable: selected, args: [] }
256
+ : selected;
257
+ const inspect = args[0] === '--showConfig' && command.configApi;
258
+ const result = await execute(
259
+ inspect ? process.execPath : command.executable,
260
+ inspect
261
+ ? [
262
+ '--input-type=module',
263
+ '--eval',
264
+ inspectNativeConfig,
265
+ command.configApi!,
266
+ command.executable,
267
+ args[args.indexOf('--project') + 1],
268
+ ]
269
+ : [...command.args, ...args],
270
+ {
271
+ cwd: path.dirname(this.options.configFile),
272
+ // Apply to every command, including explicit paths and TSRS_BINARY.
273
+ // Keep caller selection; do not change the dev server's environment.
274
+ env: {
275
+ ...process.env,
276
+ TSRS_CHECKER_ASSIGNMENT:
277
+ process.env.TSRS_CHECKER_ASSIGNMENT || 'go',
278
+ },
279
+ maxBuffer: 16 * 1024 * 1024,
280
+ ...(inspect ? { timeout: 10_000 } : {}),
281
+ },
282
+ );
46
283
  return result.stdout;
47
284
  } catch (cause) {
48
- const output = cause as { stdout?: string; stderr?: string };
285
+ const output = cause as {
286
+ stdout?: string;
287
+ stderr?: string;
288
+ code?: number | string;
289
+ signal?: NodeJS.Signals;
290
+ };
49
291
  const diagnostics = `${output.stdout ?? ''}${output.stderr ?? ''}`;
50
- throw new Error(
51
- `${name} failed:\n${diagnostics || (cause instanceof Error ? cause.message : String(cause))}`,
52
- { cause },
292
+ throw Object.assign(
293
+ new Error(
294
+ `${name} failed:\n${diagnostics || (cause instanceof Error ? cause.message : String(cause))}${missingJsxRuntimeHint(diagnostics)}`,
295
+ { cause },
296
+ ),
297
+ { code: output.code, signal: output.signal },
53
298
  );
54
299
  }
55
300
  }
56
301
 
57
- private async watchInputs(): Promise<{
302
+ private async watchInputs(configFile: string): Promise<{
58
303
  inputs: Set<string>;
59
304
  missing: Set<string>;
60
305
  }> {
61
306
  const inputs = new Set<string>();
62
307
  const missing = new Set<string>();
63
- const projects = [this.options.configFile];
308
+ const projects = [configFile];
64
309
  const visited = new Set<string>();
65
310
  for (const configFile of projects) {
66
311
  if (visited.has(configFile)) continue;
@@ -68,7 +313,8 @@ export class UltramodernNativeTypeChecker {
68
313
  // Registered before it is parsed: a referenced config that is absent or
69
314
  // unparsable right now must still be watched, or creating or repairing
70
315
  // it never re-triggers the compilation. `check()` reports the error.
71
- inputs.add(configFile);
316
+ if (configFile === this.options.configFile || configFile !== projects[0])
317
+ inputs.add(configFile);
72
318
  if (!existsSync(configFile)) {
73
319
  missing.add(configFile);
74
320
  continue;
@@ -120,17 +366,20 @@ export class UltramodernNativeTypeChecker {
120
366
  compilation.missingDependencies.add(watched);
121
367
  }
122
368
  }
123
- const { inputs, missing } = await this.watchInputs();
124
- for (const file of inputs)
125
- compilation.fileDependencies.add(watchDependencyPath(file));
126
- for (const file of missing)
127
- compilation.missingDependencies.add(watchDependencyPath(file));
128
- await this.check();
369
+ const configFile = await this.projectConfigFile();
370
+ try {
371
+ const { inputs, missing } = await this.watchInputs(configFile);
372
+ for (const file of inputs)
373
+ compilation.fileDependencies.add(watchDependencyPath(file));
374
+ for (const file of missing)
375
+ compilation.missingDependencies.add(watchDependencyPath(file));
376
+ await this.checkProject(configFile);
377
+ } finally {
378
+ this.removeProjection(configFile);
379
+ }
129
380
  } catch (cause) {
130
381
  compilation.errors.push(
131
- new Error(cause instanceof Error ? cause.message : String(cause), {
132
- cause,
133
- }),
382
+ cause instanceof Error ? cause : new Error(String(cause)),
134
383
  );
135
384
  }
136
385
  });
@@ -138,15 +387,22 @@ export class UltramodernNativeTypeChecker {
138
387
  }
139
388
  }
140
389
 
141
- /** Preserve project-reference emit contracts in the native Effect compiler. */
390
+ /** Preserve project-reference emit contracts in the selected native compiler. */
142
391
  export function configureUltramodernTypeChecker(
143
392
  chain: RspackChain,
144
393
  pluginId: string,
145
- resolveCompiler: (configFile: string) => string,
394
+ resolveCompiler: (configFile: string) => string | NativeTypeCheckerCommand,
146
395
  ): void {
147
396
  if (!chain.plugins.has(pluginId)) return;
148
397
  const options = chain.plugin(pluginId).get('args')?.[0] as
149
- | { typescript?: { configFile?: string; build?: boolean; tsgo?: boolean } }
398
+ | {
399
+ typescript?: {
400
+ configFile?: string;
401
+ build?: boolean;
402
+ tsgo?: boolean;
403
+ configOverwrite?: TypeCheckerConfig;
404
+ };
405
+ }
150
406
  | undefined;
151
407
  const typescript = options?.typescript;
152
408
  if (typescript?.tsgo === false) return;
@@ -159,6 +415,7 @@ export function configureUltramodernTypeChecker(
159
415
  build: typescript.build === true,
160
416
  compiler: () => resolveCompiler(configFile),
161
417
  configFile,
418
+ configOverwrite: typescript.configOverwrite,
162
419
  },
163
420
  ]);
164
421
  }
@@ -22,6 +22,9 @@ export const RENDERER_EXTENSIONS_PLUGIN_NAME = 'rendererHead';
22
22
  export const SERVER_EXTENSIONS_PLUGIN_NAME =
23
23
  '@modern-js/app-tools/server-plugin';
24
24
 
25
+ export const ULTRAMODERN_SERVER_EXTENSIONS_PLUGIN_NAME =
26
+ '@modern-js/ultramodern-app-tools/server-plugin';
27
+
25
28
  export interface PolicyDefaultsOptions {
26
29
  /** Default `true`. Set to `false` to drop the fork's renderer policy. */
27
30
  rendererExtensions?: boolean;
@@ -29,6 +32,16 @@ export interface PolicyDefaultsOptions {
29
32
  serverExtensions?: boolean;
30
33
  }
31
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
+
32
45
  type RuntimePluginDescriptor = {
33
46
  name?: string;
34
47
  path: string;
@@ -62,6 +75,7 @@ export const POLICY_DEFAULTS_PLUGIN_NAME =
62
75
  export const applyPolicyDefaults = (
63
76
  api: PolicyDefaultsPluginApi,
64
77
  options: PolicyDefaultsOptions = {},
78
+ composition: PolicyDefaultsComposition = {},
65
79
  ): void => {
66
80
  if (options.rendererExtensions !== false) {
67
81
  api._internalRuntimePlugins(
@@ -80,28 +94,43 @@ export const applyPolicyDefaults = (
80
94
  return { entrypoint, plugins };
81
95
  },
82
96
  );
97
+ }
83
98
 
84
- const moduleDirectories = collectRuntimePackageModuleDirectories(
85
- [RENDERER_EXTENSIONS_PACKAGE],
86
- import.meta.url,
87
- );
88
- if (moduleDirectories.length > 0) {
89
- api.modifyResolvedConfig(config => ({
90
- ...config,
91
- builderPlugins: [
92
- ...(config.builderPlugins ?? []),
93
- createRuntimePackageResolutionPlugin(moduleDirectories),
94
- ],
95
- }));
96
- }
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
+ }));
97
119
  }
98
120
 
99
121
  if (options.serverExtensions !== false) {
122
+ const serverPluginName =
123
+ composition.serverPluginName ?? SERVER_EXTENSIONS_PLUGIN_NAME;
100
124
  api._internalServerPlugins(({ plugins }: ServerPluginsInput) => {
101
125
  if (
102
- !plugins.some(plugin => plugin.name === SERVER_EXTENSIONS_PLUGIN_NAME)
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
+ )
103
132
  ) {
104
- plugins.push({ name: SERVER_EXTENSIONS_PLUGIN_NAME });
133
+ plugins.push({ name: serverPluginName });
105
134
  }
106
135
  return { plugins };
107
136
  });
@@ -119,9 +148,10 @@ export const applyPolicyDefaults = (
119
148
  */
120
149
  export const createPolicyDefaultsPlugin = (
121
150
  options: PolicyDefaultsOptions = {},
151
+ composition: PolicyDefaultsComposition = {},
122
152
  ) => ({
123
- name: POLICY_DEFAULTS_PLUGIN_NAME,
153
+ name: composition.pluginName ?? POLICY_DEFAULTS_PLUGIN_NAME,
124
154
  setup(api: PolicyDefaultsPluginApi) {
125
- applyPolicyDefaults(api, options);
155
+ applyPolicyDefaults(api, options, composition);
126
156
  },
127
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
+ };