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

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 (213) hide show
  1. package/dist/cjs/backend-federation-build/codegen.js +267 -0
  2. package/dist/cjs/backend-federation-build/config.js +239 -0
  3. package/dist/cjs/backend-federation-build/emit.js +154 -0
  4. package/dist/cjs/backend-federation-build/index.js +53 -0
  5. package/dist/cjs/build-config/build-environment.js +129 -0
  6. package/dist/cjs/build-config/precompress/plugin.js +124 -0
  7. package/dist/cjs/build-config/precompress/types.js +18 -0
  8. package/dist/cjs/build-config/public.js +43 -0
  9. package/dist/cjs/build-config/remote-address.js +58 -0
  10. package/dist/cjs/cloudflare/artifacts.js +8 -19
  11. package/dist/cjs/cloudflare/constants.js +1 -1
  12. package/dist/cjs/cloudflare/delivery-unit.js +23 -42
  13. package/dist/cjs/cloudflare/index.js +10 -2
  14. package/dist/cjs/cloudflare/worker-manifest.js +13 -4
  15. package/dist/cjs/cloudflare/wrangler-config.js +35 -7
  16. package/dist/cjs/cloudflare-builder.js +251 -75
  17. package/dist/cjs/cloudflare-output-contract.js +69 -3
  18. package/dist/cjs/cloudflare-output-verifier/identity.js +29 -27
  19. package/dist/cjs/cloudflare-output-verifier/index.js +10 -0
  20. package/dist/cjs/cloudflare-output-verifier/manifest.js +46 -0
  21. package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +7 -1
  22. package/dist/cjs/cloudflare-output-verifier/worker-bundles.js +14 -6
  23. package/dist/cjs/config.js +34 -1
  24. package/dist/cjs/deploy-output/npmAliases.js +251 -0
  25. package/dist/cjs/deploy-output/plugin.js +97 -0
  26. package/dist/cjs/deploy-output/public-assets.js +146 -0
  27. package/dist/cjs/deploy-output/target.js +104 -0
  28. package/dist/cjs/deploy-output/trace-files.js +90 -0
  29. package/dist/cjs/native-type-checker.js +193 -0
  30. package/dist/cjs/policy-defaults.js +101 -0
  31. package/dist/cjs/release-envelope/framework-output.js +153 -79
  32. package/dist/cjs/release-envelope/index.js +10 -4
  33. package/dist/cjs/release-envelope/plugin.js +18 -13
  34. package/dist/cjs/release-identity.js +5 -6
  35. package/dist/cjs/runtime-package-resolution.js +118 -0
  36. package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
  37. package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
  38. package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
  39. package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
  40. package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
  41. package/dist/esm/backend-federation-build/codegen.mjs +216 -0
  42. package/dist/esm/backend-federation-build/config.mjs +163 -0
  43. package/dist/esm/backend-federation-build/emit.mjs +103 -0
  44. package/dist/esm/backend-federation-build/index.mjs +1 -0
  45. package/dist/esm/build-config/build-environment.mjs +88 -0
  46. package/dist/esm/build-config/precompress/plugin.mjs +76 -0
  47. package/dist/esm/build-config/precompress/types.mjs +0 -0
  48. package/dist/esm/build-config/public.mjs +1 -0
  49. package/dist/esm/build-config/remote-address.mjs +20 -0
  50. package/dist/esm/cloudflare/artifacts.mjs +8 -19
  51. package/dist/esm/cloudflare/constants.mjs +1 -1
  52. package/dist/esm/cloudflare/delivery-unit.mjs +17 -36
  53. package/dist/esm/cloudflare/index.mjs +10 -2
  54. package/dist/esm/cloudflare/worker-manifest.mjs +13 -4
  55. package/dist/esm/cloudflare/wrangler-config.mjs +35 -7
  56. package/dist/esm/cloudflare-builder.mjs +245 -75
  57. package/dist/esm/cloudflare-output-contract.mjs +59 -2
  58. package/dist/esm/cloudflare-output-verifier/identity.mjs +27 -25
  59. package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
  60. package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
  61. package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  62. package/dist/esm/cloudflare-output-verifier/worker-bundles.mjs +15 -7
  63. package/dist/esm/config.mjs +3 -0
  64. package/dist/esm/deploy-output/npmAliases.mjs +199 -0
  65. package/dist/esm/deploy-output/plugin.mjs +46 -0
  66. package/dist/esm/deploy-output/public-assets.mjs +82 -0
  67. package/dist/esm/deploy-output/target.mjs +57 -0
  68. package/dist/esm/deploy-output/trace-files.mjs +38 -0
  69. package/dist/esm/native-type-checker.mjs +142 -0
  70. package/dist/esm/policy-defaults.mjs +45 -0
  71. package/dist/esm/release-envelope/framework-output.mjs +130 -56
  72. package/dist/esm/release-envelope/index.mjs +10 -4
  73. package/dist/esm/release-envelope/plugin.mjs +18 -13
  74. package/dist/esm/release-identity.mjs +5 -6
  75. package/dist/esm/runtime-package-resolution.mjs +60 -0
  76. package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
  77. package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
  78. package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
  79. package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
  80. package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
  81. package/dist/esm-node/backend-federation-build/codegen.mjs +217 -0
  82. package/dist/esm-node/backend-federation-build/config.mjs +164 -0
  83. package/dist/esm-node/backend-federation-build/emit.mjs +104 -0
  84. package/dist/esm-node/backend-federation-build/index.mjs +2 -0
  85. package/dist/esm-node/build-config/build-environment.mjs +89 -0
  86. package/dist/esm-node/build-config/precompress/plugin.mjs +77 -0
  87. package/dist/esm-node/build-config/precompress/types.mjs +1 -0
  88. package/dist/esm-node/build-config/public.mjs +2 -0
  89. package/dist/esm-node/build-config/remote-address.mjs +21 -0
  90. package/dist/esm-node/cloudflare/artifacts.mjs +8 -19
  91. package/dist/esm-node/cloudflare/constants.mjs +1 -1
  92. package/dist/esm-node/cloudflare/delivery-unit.mjs +17 -36
  93. package/dist/esm-node/cloudflare/index.mjs +10 -2
  94. package/dist/esm-node/cloudflare/worker-manifest.mjs +13 -4
  95. package/dist/esm-node/cloudflare/wrangler-config.mjs +35 -7
  96. package/dist/esm-node/cloudflare-builder.mjs +245 -75
  97. package/dist/esm-node/cloudflare-output-contract.mjs +59 -2
  98. package/dist/esm-node/cloudflare-output-verifier/identity.mjs +27 -25
  99. package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
  100. package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
  101. package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
  102. package/dist/esm-node/cloudflare-output-verifier/worker-bundles.mjs +15 -7
  103. package/dist/esm-node/config.mjs +3 -0
  104. package/dist/esm-node/deploy-output/npmAliases.mjs +200 -0
  105. package/dist/esm-node/deploy-output/plugin.mjs +49 -0
  106. package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
  107. package/dist/esm-node/deploy-output/target.mjs +58 -0
  108. package/dist/esm-node/deploy-output/trace-files.mjs +39 -0
  109. package/dist/esm-node/native-type-checker.mjs +143 -0
  110. package/dist/esm-node/policy-defaults.mjs +46 -0
  111. package/dist/esm-node/release-envelope/framework-output.mjs +130 -56
  112. package/dist/esm-node/release-envelope/index.mjs +10 -4
  113. package/dist/esm-node/release-envelope/plugin.mjs +18 -13
  114. package/dist/esm-node/release-identity.mjs +5 -6
  115. package/dist/esm-node/runtime-package-resolution.mjs +61 -0
  116. package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
  117. package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
  118. package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
  119. package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
  120. package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
  121. package/dist/types/backend-federation-build/codegen.d.ts +116 -0
  122. package/dist/types/backend-federation-build/config.d.ts +121 -0
  123. package/dist/types/backend-federation-build/emit.d.ts +21 -0
  124. package/dist/types/backend-federation-build/index.d.ts +2 -0
  125. package/dist/types/build-config/build-environment.d.ts +14 -0
  126. package/dist/types/build-config/precompress/plugin.d.ts +4 -0
  127. package/dist/types/build-config/precompress/types.d.ts +19 -0
  128. package/dist/types/build-config/public.d.ts +2 -0
  129. package/dist/types/build-config/remote-address.d.ts +3 -0
  130. package/dist/types/cloudflare/artifacts.d.ts +7 -6
  131. package/dist/types/cloudflare/constants.d.ts +1 -1
  132. package/dist/types/cloudflare/delivery-unit.d.ts +3 -3
  133. package/dist/types/cloudflare/worker-manifest.d.ts +2 -2
  134. package/dist/types/cloudflare/wrangler-config.d.ts +6 -0
  135. package/dist/types/cloudflare-builder.d.ts +49 -9
  136. package/dist/types/cloudflare-output-contract.d.ts +4 -1
  137. package/dist/types/cloudflare-output-verifier/identity.d.ts +1 -1
  138. package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
  139. package/dist/types/config.d.ts +29 -1
  140. package/dist/types/deploy-output/npmAliases.d.ts +14 -0
  141. package/dist/types/deploy-output/plugin.d.ts +35 -0
  142. package/dist/types/deploy-output/public-assets.d.ts +51 -0
  143. package/dist/types/deploy-output/target.d.ts +29 -0
  144. package/dist/types/deploy-output/trace-files.d.ts +17 -0
  145. package/dist/types/native-type-checker.d.ts +15 -0
  146. package/dist/types/policy-defaults.d.ts +46 -0
  147. package/dist/types/precompress/LICENSE +20 -0
  148. package/dist/types/precompress/index.d.ts +76 -0
  149. package/dist/types/release-envelope/framework-output.d.ts +8 -3
  150. package/dist/types/release-envelope/plugin.d.ts +7 -7
  151. package/dist/types/runtime-package-resolution.d.ts +28 -0
  152. package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
  153. package/package.json +97 -15
  154. package/src/backend-federation-build/codegen.ts +254 -0
  155. package/src/backend-federation-build/config.ts +492 -0
  156. package/src/backend-federation-build/emit.ts +213 -0
  157. package/src/backend-federation-build/index.ts +2 -0
  158. package/src/build-config/build-environment.ts +160 -0
  159. package/src/build-config/precompress/plugin.ts +140 -0
  160. package/src/build-config/precompress/types.ts +26 -0
  161. package/src/build-config/public.ts +5 -0
  162. package/src/build-config/remote-address.ts +33 -0
  163. package/src/cloudflare/artifacts.ts +22 -43
  164. package/src/cloudflare/constants.ts +1 -1
  165. package/src/cloudflare/delivery-unit.ts +42 -53
  166. package/src/cloudflare/index.ts +24 -11
  167. package/src/cloudflare/worker-manifest.ts +20 -3
  168. package/src/cloudflare/wrangler-config.ts +86 -10
  169. package/src/cloudflare-builder.ts +472 -151
  170. package/src/cloudflare-output-contract.ts +70 -0
  171. package/src/cloudflare-output-verifier/identity.ts +5 -1
  172. package/src/cloudflare-output-verifier/index.ts +20 -0
  173. package/src/cloudflare-output-verifier/manifest.ts +77 -0
  174. package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
  175. package/src/cloudflare-output-verifier/worker-bundles.ts +32 -14
  176. package/src/config.ts +34 -6
  177. package/src/deploy-output/npmAliases.ts +382 -0
  178. package/src/deploy-output/plugin.ts +86 -0
  179. package/src/deploy-output/public-assets.ts +224 -0
  180. package/src/deploy-output/target.ts +106 -0
  181. package/src/deploy-output/trace-files.ts +67 -0
  182. package/src/native-type-checker.ts +183 -0
  183. package/src/policy-defaults.ts +127 -0
  184. package/src/release-envelope/framework-output.ts +346 -94
  185. package/src/release-envelope/index.ts +26 -5
  186. package/src/release-envelope/plugin.ts +43 -40
  187. package/src/release-identity.ts +13 -2
  188. package/src/runtime-package-resolution.ts +115 -0
  189. package/src/templates/cloudflare-entry.002-assets-routes.mjs +1 -1
  190. package/src/templates/cloudflare-entry.004-rendering-css.mjs +5 -1
  191. package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +98 -10
  192. package/src/templates/cloudflare-entry.006-fetch-handler.mjs +9 -2
  193. package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
  194. package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +0 -11
  195. package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +0 -281
  196. package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  197. package/dist/cjs/templates/cloudflare-worker-path.mjs +0 -98
  198. package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +0 -11
  199. package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +0 -281
  200. package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  201. package/dist/esm/templates/cloudflare-worker-path.mjs +0 -98
  202. package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +0 -11
  203. package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +0 -281
  204. package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  205. package/dist/esm-node/templates/cloudflare-worker-path.mjs +0 -98
  206. package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +0 -5
  207. package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +0 -48
  208. package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +0 -13
  209. package/dist/types/templates/cloudflare-worker-path.d.mts +0 -21
  210. package/src/templates/cloudflare-worker-fs-promises.mjs +0 -11
  211. package/src/templates/cloudflare-worker-loadable-server.mjs +0 -281
  212. package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
  213. package/src/templates/cloudflare-worker-path.mjs +0 -98
@@ -0,0 +1,216 @@
1
+ import node_path from "node:path";
2
+ import { BACKEND_FEDERATION_CONTRACT_VERSION, BACKEND_FEDERATION_EFFECT_EXPOSE, BACKEND_FEDERATION_NODE_ADAPTER_VERSION, BACKEND_FEDERATION_REMOTE_ENTRY_FILE, formatBackendFederationValidationErrors, validateBackendFederationManifest } from "@modern-js/backend-federation-contracts";
3
+ import { build } from "esbuild";
4
+ import { normalizeRelativePath } from "./config.mjs";
5
+ const createRelativeImportSpecifier = (fromDirectory, toFile)=>{
6
+ const relativePath = normalizeRelativePath(node_path.relative(fromDirectory, toFile));
7
+ return relativePath.startsWith('.') ? relativePath : `./${relativePath}`;
8
+ };
9
+ const createPublicPath = (containerEntry, fallbackPort)=>{
10
+ try {
11
+ return new URL('.', containerEntry).href;
12
+ } catch {
13
+ const slashIndex = containerEntry.lastIndexOf('/');
14
+ if (slashIndex >= 0) return containerEntry.slice(0, slashIndex + 1);
15
+ return `http://localhost:${fallbackPort}/`;
16
+ }
17
+ };
18
+ const createBackendRemoteEntrySource = async (workspaceRoot, app, effectApiPath, entryPath)=>{
19
+ const effectApiSpecifier = createRelativeImportSpecifier(node_path.dirname(effectApiPath), effectApiPath);
20
+ const containerMetadata = {
21
+ buildVersion: app.buildVersion,
22
+ expose: BACKEND_FEDERATION_EFFECT_EXPOSE,
23
+ name: app.backendName,
24
+ packageName: app.packageName,
25
+ remoteType: app.remoteType,
26
+ source: normalizeRelativePath(node_path.relative(workspaceRoot, effectApiPath)),
27
+ version: app.version,
28
+ ...app.unitId ? {
29
+ unitId: app.unitId
30
+ } : {},
31
+ ...app.sourceRevision ? {
32
+ sourceRevision: app.sourceRevision
33
+ } : {}
34
+ };
35
+ const compatibility = {
36
+ build: app.buildVersion,
37
+ contractVersion: BACKEND_FEDERATION_CONTRACT_VERSION,
38
+ nodeAdapterVersion: BACKEND_FEDERATION_NODE_ADAPTER_VERSION,
39
+ packageName: app.packageName,
40
+ ...app.unitId ? {
41
+ unitId: app.unitId
42
+ } : {},
43
+ ...app.sourceRevision ? {
44
+ sourceRevision: app.sourceRevision
45
+ } : {}
46
+ };
47
+ const source = `// Generated by Modern.js backend federation build.
48
+ import { adoptEffectBffShareScope } from '@modern-js/bff-effect/effect-edge';
49
+
50
+ // The expose evaluates lazily so defineEffectBff registers its handler
51
+ // factories with the registry the host shares through init().
52
+ let exposedModule;
53
+ const loadExposedModule = () =>
54
+ (exposedModule ??= import(${JSON.stringify(effectApiSpecifier)}).then(exposedNamespace => ({
55
+ ...exposedNamespace,
56
+ backendFederationContract: {
57
+ ...(exposedNamespace.backendFederationContract ?? {}),
58
+ compatibility: {
59
+ ...(exposedNamespace.backendFederationContract?.compatibility ?? {}),
60
+ ...${JSON.stringify(compatibility)},
61
+ },
62
+ name: ${JSON.stringify(app.backendName)},
63
+ role: 'microvertical-server',
64
+ runtimeFramework: 'effect',
65
+ strictEffectApproach: true,
66
+ },
67
+ })));
68
+ const exposedModules = {
69
+ ${JSON.stringify(BACKEND_FEDERATION_EFFECT_EXPOSE)}: loadExposedModule,
70
+ };
71
+ const __modernjsBackendFederation = ${JSON.stringify(containerMetadata)};
72
+
73
+ function init(shareScope) {
74
+ return adoptEffectBffShareScope(shareScope);
75
+ }
76
+
77
+ function get(id) {
78
+ const load = exposedModules[id];
79
+ if (!load) {
80
+ throw new Error(\`Unexpected backend federation expose: \${id}\`);
81
+ }
82
+
83
+ return async () => load();
84
+ }
85
+
86
+ module.exports = { __modernjsBackendFederation, get, init };
87
+ `;
88
+ const result = await build({
89
+ absWorkingDir: node_path.dirname(effectApiPath),
90
+ bundle: true,
91
+ format: 'cjs',
92
+ logLevel: 'silent',
93
+ platform: 'node',
94
+ stdin: {
95
+ contents: source,
96
+ loader: 'ts',
97
+ resolveDir: node_path.dirname(effectApiPath),
98
+ sourcefile: node_path.basename(entryPath)
99
+ },
100
+ target: 'node20',
101
+ write: false
102
+ });
103
+ const output = result.outputFiles[0];
104
+ if (!output) throw new Error('[backend-federation-build] CommonJS backend federation container emitted no output.');
105
+ return output.text;
106
+ };
107
+ const createBackendManifest = (workspaceRoot, distDirectory, app, entryIntegrity)=>{
108
+ const sourceModule = `${app.directory}/api/effect-api.ts`;
109
+ const publicPath = createPublicPath(app.containerEntry, app.port);
110
+ const manifest = {
111
+ schemaVersion: 1,
112
+ id: app.backendName,
113
+ name: app.backendName,
114
+ version: app.version,
115
+ buildVersion: app.buildVersion,
116
+ metaData: {
117
+ name: app.backendName,
118
+ type: 'backend',
119
+ buildInfo: {
120
+ buildVersion: app.buildVersion,
121
+ buildName: app.packageName
122
+ },
123
+ remoteEntry: {
124
+ name: BACKEND_FEDERATION_REMOTE_ENTRY_FILE,
125
+ path: '',
126
+ type: app.remoteType
127
+ },
128
+ globalName: app.backendName,
129
+ publicPath,
130
+ ssrRemoteEntry: {
131
+ name: BACKEND_FEDERATION_REMOTE_ENTRY_FILE,
132
+ path: '',
133
+ type: app.remoteType
134
+ },
135
+ ssrPublicPath: publicPath
136
+ },
137
+ entry: {
138
+ ...entryIntegrity,
139
+ file: BACKEND_FEDERATION_REMOTE_ENTRY_FILE,
140
+ path: normalizeRelativePath(node_path.relative(workspaceRoot, node_path.join(distDirectory, BACKEND_FEDERATION_REMOTE_ENTRY_FILE))),
141
+ type: app.remoteType,
142
+ url: app.containerEntry
143
+ },
144
+ exposes: [
145
+ {
146
+ id: `${app.backendName}:${BACKEND_FEDERATION_EFFECT_EXPOSE}`,
147
+ name: BACKEND_FEDERATION_EFFECT_EXPOSE,
148
+ path: '',
149
+ assets: {
150
+ js: {
151
+ async: [],
152
+ sync: [
153
+ BACKEND_FEDERATION_REMOTE_ENTRY_FILE
154
+ ]
155
+ },
156
+ css: {
157
+ async: [],
158
+ sync: []
159
+ }
160
+ }
161
+ }
162
+ ],
163
+ shared: [],
164
+ backendFederation: {
165
+ role: 'microvertical-server',
166
+ appId: app.id,
167
+ name: app.backendName,
168
+ runtimeFramework: 'effect',
169
+ strictEffectApproach: true,
170
+ contractVersion: BACKEND_FEDERATION_CONTRACT_VERSION,
171
+ nodeAdapterVersion: BACKEND_FEDERATION_NODE_ADAPTER_VERSION,
172
+ remoteType: app.remoteType,
173
+ expose: BACKEND_FEDERATION_EFFECT_EXPOSE,
174
+ manifestUrl: app.manifestUrl,
175
+ containerEntry: app.containerEntry,
176
+ source: {
177
+ module: sourceModule
178
+ },
179
+ ...'rpc' === app.apiProtocol ? {
180
+ rpcPath: app.rpcPath,
181
+ rpcSerialization: 'json'
182
+ } : {
183
+ readinessPath: `${app.apiPrefix}/${app.apiStem}/readiness`,
184
+ openapiPath: `${app.apiPrefix}/openapi.json`
185
+ },
186
+ ...app.deliveryUnit ? {
187
+ deliveryUnit: app.deliveryUnit
188
+ } : {},
189
+ versionBoundary: {
190
+ invariant: 'web-and-api-same-build',
191
+ packageName: app.packageName,
192
+ version: app.version,
193
+ buildVersion: app.buildVersion,
194
+ ...app.uiManifestUrl ? {
195
+ uiManifestUrl: app.uiManifestUrl
196
+ } : {},
197
+ ...app.deliveryUnit ? {
198
+ deliveryUnit: {
199
+ unitId: app.deliveryUnit.unitId,
200
+ buildMarker: app.deliveryUnit.buildMarker,
201
+ sourceRevision: app.deliveryUnit.sourceRevision
202
+ }
203
+ } : {}
204
+ }
205
+ }
206
+ };
207
+ const validation = validateBackendFederationManifest(manifest, {
208
+ path: 'backendFederationManifest',
209
+ requireEffectExpose: true,
210
+ requireEffectRuntime: true,
211
+ requireVersionFields: true
212
+ });
213
+ if (!validation.ok) throw new Error(`[backend-federation-build] Invalid backend federation manifest: ${formatBackendFederationValidationErrors(validation.errors)}.`);
214
+ return manifest;
215
+ };
216
+ export { createBackendManifest, createBackendRemoteEntrySource };
@@ -0,0 +1,163 @@
1
+ import { existsSync } from "node:fs";
2
+ import promises from "node:fs/promises";
3
+ import node_path from "node:path";
4
+ import { resolveUltramodernSourceRevision } from "@modern-js/app-tools-extensions/release-identity";
5
+ import { BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_REMOTE_ENTRY_FILE, DELIVERY_UNIT_DEPLOY_PROFILE, DELIVERY_UNIT_KIND, DELIVERY_UNIT_SCHEMA_VERSION, ULTRAMODERN_BUILD_ARTIFACT_PATH, deliveryUnitContractBlock, isUltramodernBuildArtifact, validateDeliveryUnitRecord, validateUltramodernBuildArtifact } from "@modern-js/backend-federation-contracts";
6
+ const REFERENCE_TOPOLOGY_PATH = 'topology/reference-topology.json';
7
+ const DEVELOPMENT_OVERLAY_PATH = 'topology/local-overlays/development.json';
8
+ const normalizeRelativePath = (value)=>value.replace(/\\/gu, '/').replace(/^\.\/+/u, '');
9
+ const isRecord = (value)=>'object' == typeof value && null !== value;
10
+ const readJsonFile = async (filePath)=>JSON.parse(await promises.readFile(filePath, 'utf8'));
11
+ const buildArtifactPathFor = (appDirectory)=>node_path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH);
12
+ const readBuildIdentity = async (appDirectory)=>{
13
+ const buildArtifactPath = buildArtifactPathFor(appDirectory);
14
+ if (existsSync(buildArtifactPath)) {
15
+ const artifact = await readJsonFile(buildArtifactPath);
16
+ const artifactValidation = validateUltramodernBuildArtifact(artifact, buildArtifactPath);
17
+ if (!artifactValidation.ok) throw new Error(`[backend-federation-build] Invalid delivery-unit build artifact at ${buildArtifactPath}.`);
18
+ if (!isUltramodernBuildArtifact(artifact)) throw new Error(`[backend-federation-build] Invalid delivery-unit build artifact at ${buildArtifactPath}.`);
19
+ const deliveryUnit = artifact.deliveryUnit;
20
+ return {
21
+ artifact,
22
+ appId: deliveryUnit.appId,
23
+ buildVersion: deliveryUnit.buildMarker,
24
+ packageName: deliveryUnit.packageName,
25
+ version: deliveryUnit.version,
26
+ unitId: deliveryUnit.unitId,
27
+ sourceRevision: deliveryUnit.sourceRevision
28
+ };
29
+ }
30
+ throw new Error(`[backend-federation-build] Missing delivery-unit build artifact at ${buildArtifactPath}.`);
31
+ };
32
+ const toPascalCase = (value)=>value.split(/[^a-zA-Z0-9]+/u).filter(Boolean).map((part)=>part.charAt(0).toUpperCase() + part.slice(1)).join('');
33
+ const stringValue = (value)=>'string' == typeof value && value.length > 0 ? value : void 0;
34
+ const createBackendName = (app, id)=>{
35
+ const configuredName = stringValue(app.backendFederation?.name) ?? stringValue(app.backendFederation?.executionSurfaces?.node?.remoteName);
36
+ if (configuredName) return configuredName;
37
+ const mfName = stringValue(app.moduleFederation?.name);
38
+ return mfName ? `${mfName}Backend` : `vertical${toPascalCase(id)}Backend`;
39
+ };
40
+ const rebaseDefaultLocalUrl = (value, defaultPort, publicOrigin)=>{
41
+ let url;
42
+ try {
43
+ url = new URL(value);
44
+ } catch {
45
+ return value;
46
+ }
47
+ if ('http:' !== url.protocol || 'localhost' !== url.hostname || Number(url.port) !== defaultPort || url.username || url.password) return value;
48
+ return `${publicOrigin}${url.pathname}${url.search}${url.hash}`;
49
+ };
50
+ const findWorkspaceRoot = (appDirectory)=>{
51
+ let current = appDirectory;
52
+ while(true){
53
+ if (existsSync(node_path.join(current, REFERENCE_TOPOLOGY_PATH))) return current;
54
+ const parent = node_path.dirname(current);
55
+ if (parent === current) return;
56
+ current = parent;
57
+ }
58
+ };
59
+ const resolveWorkspaceSourceRevision = async (workspaceRoot)=>resolveUltramodernSourceRevision(workspaceRoot);
60
+ const createStampedDeliveryUnit = (input)=>{
61
+ if (!input.unitId || !input.buildMarker || !input.packageName || !input.version) return;
62
+ const record = {
63
+ schemaVersion: DELIVERY_UNIT_SCHEMA_VERSION,
64
+ kind: DELIVERY_UNIT_KIND,
65
+ appId: input.appId,
66
+ unitId: input.unitId,
67
+ packageName: input.packageName,
68
+ version: input.version,
69
+ buildMarker: input.buildMarker,
70
+ sourceRevision: input.sourceRevision,
71
+ deployProfile: DELIVERY_UNIT_DEPLOY_PROFILE
72
+ };
73
+ const validation = validateDeliveryUnitRecord(record);
74
+ if (!validation.ok) return;
75
+ return deliveryUnitContractBlock(record);
76
+ };
77
+ const createAppFromTopology = (workspaceRoot, appDirectory, topologyApp, overlay, packageManifest)=>{
78
+ const id = stringValue(topologyApp.id);
79
+ const appPath = stringValue(topologyApp.path);
80
+ if (!id || !appPath || 'vertical' !== topologyApp.kind || !isRecord(topologyApp.api) || !isRecord(topologyApp.backendFederation)) return;
81
+ const directory = normalizeRelativePath(appPath);
82
+ if (node_path.resolve(workspaceRoot, directory) !== node_path.resolve(appDirectory)) return;
83
+ const defaultPort = overlay.ports?.[id];
84
+ if ('number' != typeof defaultPort || !Number.isInteger(defaultPort) || defaultPort < 1 || defaultPort > 65535) throw new Error(`[backend-federation-build] Invalid development port for ${id}.`);
85
+ const portEnv = stringValue(topologyApp.portEnv) ?? `VERTICAL_${(stringValue(topologyApp.domain) ?? id).replace(/[^a-zA-Z0-9]/gu, '_').toUpperCase()}_PORT`;
86
+ const port = Number(process.env[portEnv] ?? defaultPort);
87
+ if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`[backend-federation-build] Invalid ${portEnv} development port for ${id}.`);
88
+ const publicUrlEnv = stringValue(topologyApp.cloudflare?.publicUrlEnv);
89
+ const configuredPublicUrl = publicUrlEnv ? process.env[publicUrlEnv]?.trim() : void 0;
90
+ let publicOrigin = `http://localhost:${port}`;
91
+ if (configuredPublicUrl) {
92
+ let url;
93
+ try {
94
+ url = new URL(configuredPublicUrl);
95
+ } catch {
96
+ throw new Error(`[backend-federation-build] Invalid ${publicUrlEnv} public URL for ${id}.`);
97
+ }
98
+ if (![
99
+ 'http:',
100
+ 'https:'
101
+ ].includes(url.protocol) || url.username || url.password) throw new Error(`[backend-federation-build] Invalid ${publicUrlEnv} public URL for ${id}.`);
102
+ publicOrigin = url.origin;
103
+ }
104
+ const apiPrefix = stringValue(topologyApp.api.bff?.prefix) ?? `/${id}-api`;
105
+ const apiProtocol = topologyApp.api.protocol ?? 'rest';
106
+ if ('rest' !== apiProtocol && 'rpc' !== apiProtocol) throw new Error(`[backend-federation-build] Unsupported API protocol for ${id}.`);
107
+ const rpcPath = 'rpc' === apiProtocol ? stringValue(topologyApp.api.rpcPath) : void 0;
108
+ if ('rpc' === apiProtocol && (!rpcPath || 'json' !== topologyApp.api.rpcSerialization)) throw new Error(`[backend-federation-build] Missing JSON RPC route metadata for ${id}.`);
109
+ const apiStem = stringValue(topologyApp.api.stem) ?? id;
110
+ const backendName = createBackendName(topologyApp, id);
111
+ const nodeOverlay = overlay.serverExecution?.[id]?.node;
112
+ if (!isRecord(nodeOverlay)) throw new Error(`[backend-federation-build] Missing declared Node server execution for ${id}.`);
113
+ const manifestUrl = rebaseDefaultLocalUrl(stringValue(nodeOverlay?.manifestUrl) ?? `http://localhost:${defaultPort}/${BACKEND_FEDERATION_MANIFEST_FILE}`, defaultPort, publicOrigin);
114
+ const containerEntry = rebaseDefaultLocalUrl(stringValue(nodeOverlay?.containerEntry) ?? `http://localhost:${defaultPort}/${BACKEND_FEDERATION_REMOTE_ENTRY_FILE}`, defaultPort, publicOrigin);
115
+ const configuredRemoteType = stringValue(nodeOverlay?.remoteType) ?? stringValue(topologyApp.backendFederation?.executionSurfaces?.node?.remoteType);
116
+ if (void 0 !== configuredRemoteType && 'commonjs-module' !== configuredRemoteType) throw new Error(`[backend-federation-build] Node backend federation remoteType must be "commonjs-module"; received "${configuredRemoteType}" for ${id}.`);
117
+ if (!new URL(containerEntry).pathname.endsWith(`/${BACKEND_FEDERATION_REMOTE_ENTRY_FILE}`)) throw new Error(`[backend-federation-build] Node backend federation containerEntry must end with "/${BACKEND_FEDERATION_REMOTE_ENTRY_FILE}"; received "${containerEntry}" for ${id}.`);
118
+ const remoteType = 'commonjs-module';
119
+ const packageName = stringValue(packageManifest.name);
120
+ const version = stringValue(packageManifest.version);
121
+ if (stringValue(topologyApp.package) !== packageName || !version) throw new Error(`[backend-federation-build] Topology package identity must match package.json for ${id}.`);
122
+ const declaredUiManifestUrl = stringValue(overlay.manifests?.[id]);
123
+ const uiManifestUrl = declaredUiManifestUrl ? rebaseDefaultLocalUrl(declaredUiManifestUrl, defaultPort, publicOrigin) : void 0;
124
+ const topologyDeliveryUnit = isRecord(topologyApp.deliveryUnit) ? {
125
+ unitId: stringValue(topologyApp.deliveryUnit.unitId),
126
+ buildMarker: stringValue(topologyApp.deliveryUnit.buildMarker),
127
+ sourceRevision: stringValue(topologyApp.deliveryUnit.sourceRevision),
128
+ packageName: stringValue(topologyApp.deliveryUnit.packageName),
129
+ version: stringValue(topologyApp.deliveryUnit.version)
130
+ } : void 0;
131
+ return {
132
+ id,
133
+ directory,
134
+ packageName,
135
+ version,
136
+ port,
137
+ apiPrefix,
138
+ apiProtocol,
139
+ rpcPath,
140
+ apiStem,
141
+ backendName,
142
+ manifestUrl,
143
+ containerEntry,
144
+ remoteType,
145
+ uiManifestUrl,
146
+ topologyDeliveryUnit
147
+ };
148
+ };
149
+ const findBackendFederationApp = async (workspaceRoot, appDirectory)=>{
150
+ const topologyPath = node_path.join(workspaceRoot, REFERENCE_TOPOLOGY_PATH);
151
+ const topology = await readJsonFile(topologyPath);
152
+ if (!isRecord(topology) || !Array.isArray(topology.verticals)) throw new Error(`[backend-federation-build] Invalid declared topology at ${topologyPath}.`);
153
+ const overlayPath = node_path.join(workspaceRoot, DEVELOPMENT_OVERLAY_PATH);
154
+ const overlay = await readJsonFile(overlayPath);
155
+ if (!isRecord(overlay) || !isRecord(overlay.ports)) throw new Error(`[backend-federation-build] Invalid development overlay at ${overlayPath}.`);
156
+ const packageManifest = await readJsonFile(node_path.join(appDirectory, 'package.json'));
157
+ for (const app of topology.verticals){
158
+ const resolved = createAppFromTopology(workspaceRoot, appDirectory, app, overlay, packageManifest);
159
+ if (resolved) return resolved;
160
+ }
161
+ throw new Error(`[backend-federation-build] ${appDirectory} is missing from the declared reference topology.`);
162
+ };
163
+ export { DEVELOPMENT_OVERLAY_PATH, REFERENCE_TOPOLOGY_PATH, buildArtifactPathFor, createAppFromTopology, createStampedDeliveryUnit, findBackendFederationApp, findWorkspaceRoot, normalizeRelativePath, readBuildIdentity, resolveWorkspaceSourceRevision };
@@ -0,0 +1,103 @@
1
+ import { existsSync } from "node:fs";
2
+ import promises from "node:fs/promises";
3
+ import node_path from "node:path";
4
+ import { resolveUltramodernReleaseIdentity } from "@modern-js/app-tools-extensions/release-identity";
5
+ import { BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_REMOTE_ENTRY_FILE, ULTRAMODERN_BUILD_ARTIFACT_FILE, stampUltramodernBuildArtifactIdentity } from "@modern-js/backend-federation-contracts";
6
+ import { createBackendFederationEntryIntegrity } from "@modern-js/server-runtime-extensions/backend-federation-security/node";
7
+ import { createBackendManifest, createBackendRemoteEntrySource } from "./codegen.mjs";
8
+ import { REFERENCE_TOPOLOGY_PATH, buildArtifactPathFor, createStampedDeliveryUnit, findBackendFederationApp, findWorkspaceRoot, readBuildIdentity, resolveWorkspaceSourceRevision } from "./config.mjs";
9
+ const emitBackendFederationArtifacts = async (appDirectory, distDirectory)=>{
10
+ const workspaceRoot = findWorkspaceRoot(appDirectory);
11
+ if (!workspaceRoot) return;
12
+ const effectApiPath = node_path.join(appDirectory, 'api/effect-api.ts');
13
+ const backendFederationConfigPath = node_path.join(appDirectory, 'backend-federation.config.ts');
14
+ if (!existsSync(effectApiPath) || !existsSync(backendFederationConfigPath)) return;
15
+ const app = await findBackendFederationApp(workspaceRoot, appDirectory);
16
+ if (!app) return;
17
+ const buildIdentity = await readBuildIdentity(appDirectory);
18
+ const topologyDeliveryUnit = app.topologyDeliveryUnit;
19
+ const hasTopologyDeliveryUnit = void 0 !== topologyDeliveryUnit && (void 0 !== topologyDeliveryUnit.unitId || void 0 !== topologyDeliveryUnit.buildMarker || void 0 !== topologyDeliveryUnit.packageName || void 0 !== topologyDeliveryUnit.version);
20
+ const hasBuildIdentity = void 0 !== buildIdentity.unitId || void 0 !== buildIdentity.buildVersion || void 0 !== buildIdentity.packageName || void 0 !== buildIdentity.version;
21
+ if (hasBuildIdentity) {
22
+ const topologyPath = node_path.join(workspaceRoot, REFERENCE_TOPOLOGY_PATH);
23
+ const buildIdentityPath = buildArtifactPathFor(appDirectory);
24
+ const mismatches = [];
25
+ const compare = (label, a, b, leftLabel = 'deliveryUnit')=>{
26
+ if (void 0 !== a && void 0 !== b && a !== b) mismatches.push(`${label}: ${leftLabel}=${a} vs ultramodern-build=${b}`);
27
+ };
28
+ compare('appId', app.id, buildIdentity.appId, 'topology');
29
+ if (hasTopologyDeliveryUnit) {
30
+ compare('unitId', topologyDeliveryUnit?.unitId, buildIdentity.unitId);
31
+ compare('buildMarker/build', topologyDeliveryUnit?.buildMarker, buildIdentity.buildVersion);
32
+ compare('packageName', topologyDeliveryUnit?.packageName, buildIdentity.packageName);
33
+ compare('version', topologyDeliveryUnit?.version, buildIdentity.version);
34
+ }
35
+ compare('packageName', app.packageName, buildIdentity.packageName, 'package.json');
36
+ compare('packageName', topologyDeliveryUnit?.packageName, app.packageName, 'topology');
37
+ compare('version', app.version, buildIdentity.version, 'package.json');
38
+ compare('version', topologyDeliveryUnit?.version, app.version, 'topology');
39
+ if (mismatches.length > 0) throw new Error(`[backend-federation-build] Delivery-unit identity drift between ${topologyPath}, package.json and ${buildIdentityPath}: ${mismatches.join('; ')}`);
40
+ }
41
+ const unitId = topologyDeliveryUnit?.unitId ?? buildIdentity.unitId;
42
+ const generationBuildMarker = topologyDeliveryUnit?.buildMarker ?? buildIdentity.buildVersion;
43
+ const sourceRevision = await resolveWorkspaceSourceRevision(workspaceRoot);
44
+ const buildVersion = generationBuildMarker && unitId ? resolveUltramodernReleaseIdentity({
45
+ generationBuildMarker,
46
+ unitId,
47
+ workspaceRoot
48
+ }).buildMarker : void 0;
49
+ const packageName = app.packageName;
50
+ const version = app.version;
51
+ const deliveryUnit = createStampedDeliveryUnit({
52
+ appId: app.id,
53
+ unitId,
54
+ buildMarker: buildVersion,
55
+ packageName,
56
+ version,
57
+ sourceRevision
58
+ });
59
+ const stampedBuildArtifact = buildIdentity.artifact ? stampUltramodernBuildArtifactIdentity(buildIdentity.artifact, {
60
+ buildMarker: buildVersion ?? buildIdentity.artifact.deliveryUnit.buildMarker,
61
+ sourceRevision
62
+ }) : void 0;
63
+ const resolvedApp = {
64
+ ...app,
65
+ packageName,
66
+ version,
67
+ buildVersion,
68
+ unitId,
69
+ sourceRevision,
70
+ deliveryUnit
71
+ };
72
+ const manifestPath = node_path.join(distDirectory, BACKEND_FEDERATION_MANIFEST_FILE);
73
+ const entryPath = node_path.join(distDirectory, BACKEND_FEDERATION_REMOTE_ENTRY_FILE);
74
+ const deliveryUnitArtifactPath = stampedBuildArtifact ? node_path.join(distDirectory, ULTRAMODERN_BUILD_ARTIFACT_FILE) : void 0;
75
+ const entrySource = await createBackendRemoteEntrySource(workspaceRoot, resolvedApp, effectApiPath, entryPath);
76
+ await promises.mkdir(distDirectory, {
77
+ recursive: true
78
+ });
79
+ await promises.writeFile(entryPath, entrySource);
80
+ await promises.writeFile(manifestPath, `${JSON.stringify(createBackendManifest(workspaceRoot, distDirectory, resolvedApp, createBackendFederationEntryIntegrity(entrySource)), null, 2)}\n`);
81
+ if (stampedBuildArtifact && deliveryUnitArtifactPath) await promises.writeFile(deliveryUnitArtifactPath, `${JSON.stringify(stampedBuildArtifact, null, 2)}\n`);
82
+ return {
83
+ appId: resolvedApp.id,
84
+ manifestPath,
85
+ containerPath: entryPath,
86
+ ...deliveryUnitArtifactPath ? {
87
+ deliveryUnitArtifactPath
88
+ } : {},
89
+ remoteName: resolvedApp.backendName,
90
+ remoteType: resolvedApp.remoteType
91
+ };
92
+ };
93
+ const emit = ()=>({
94
+ name: '@modern-js/backend-federation-build',
95
+ setup (api) {
96
+ api.onAfterBuild(async ()=>{
97
+ const { appDirectory, distDirectory } = api.getAppContext();
98
+ await emitBackendFederationArtifacts(appDirectory, distDirectory);
99
+ });
100
+ }
101
+ });
102
+ export default emit;
103
+ export { emitBackendFederationArtifacts };
@@ -0,0 +1 @@
1
+ export { default, emitBackendFederationArtifacts } from "./emit.mjs";
@@ -0,0 +1,88 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { accessSync, chmodSync, constants, lstatSync, mkdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import { tmpdir } from "node:os";
6
+ import { basename, dirname, join, resolve } from "node:path";
7
+ const EFFECT_TSGO_PACKAGE = '@effect/tsgo';
8
+ const EFFECT_TSGO_BIN = 'effect-tsgo';
9
+ const EFFECT_TSGO_RESOLUTION_ERROR = 'Unable to resolve the Effect TS-Go compiler. Install "@effect/tsgo" and a native TypeScript backend for this build config, or set EFFECT_TSGO_BIN.';
10
+ const executableEffectTsgoCompilers = new Map();
11
+ function resolveEffectTsgoCli(from) {
12
+ const projectRequire = createRequire(from);
13
+ const packageJsonPath = projectRequire.resolve(`${EFFECT_TSGO_PACKAGE}/package.json`);
14
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
15
+ const bin = 'string' == typeof packageJson.bin ? packageJson.bin : packageJson.bin?.[EFFECT_TSGO_BIN];
16
+ if (!bin) throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
17
+ return resolve(dirname(packageJsonPath), bin);
18
+ }
19
+ function resolveExecutableEffectTsgoCompiler(compilerPath) {
20
+ if ('win32' === process.platform) return compilerPath;
21
+ const sourcePath = realpathSync(compilerPath);
22
+ const source = lstatSync(sourcePath);
23
+ if (!source.isFile()) throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
24
+ try {
25
+ accessSync(sourcePath, constants.X_OK);
26
+ return compilerPath;
27
+ } catch {}
28
+ const cachedCompiler = executableEffectTsgoCompilers.get(sourcePath);
29
+ if (cachedCompiler) try {
30
+ accessSync(cachedCompiler, constants.X_OK);
31
+ return cachedCompiler;
32
+ } catch {
33
+ executableEffectTsgoCompilers.delete(sourcePath);
34
+ }
35
+ const compilerBytes = readFileSync(sourcePath);
36
+ const compilerDigest = createHash('sha256').update(compilerBytes).digest('hex');
37
+ const compilerDirectory = join(tmpdir(), 'modern-js', 'effect-tsgo', compilerDigest);
38
+ mkdirSync(compilerDirectory, {
39
+ recursive: true,
40
+ mode: 448
41
+ });
42
+ const executableCompiler = join(compilerDirectory, basename(sourcePath));
43
+ try {
44
+ accessSync(executableCompiler, constants.X_OK);
45
+ } catch {
46
+ const temporaryCompiler = join(compilerDirectory, `.${basename(sourcePath)}.${process.pid}.tmp`);
47
+ rmSync(temporaryCompiler, {
48
+ force: true
49
+ });
50
+ try {
51
+ writeFileSync(temporaryCompiler, compilerBytes, {
52
+ flag: 'wx',
53
+ mode: 448
54
+ });
55
+ chmodSync(temporaryCompiler, 448);
56
+ renameSync(temporaryCompiler, executableCompiler);
57
+ } finally{
58
+ rmSync(temporaryCompiler, {
59
+ force: true
60
+ });
61
+ }
62
+ }
63
+ accessSync(executableCompiler, constants.X_OK);
64
+ executableEffectTsgoCompilers.set(sourcePath, executableCompiler);
65
+ return executableCompiler;
66
+ }
67
+ function getBuildConfigEnvironment(name) {
68
+ return process.env[name];
69
+ }
70
+ function resolveEffectTsgoCompiler(options) {
71
+ const configuredCompiler = getBuildConfigEnvironment('EFFECT_TSGO_BIN')?.trim();
72
+ if (configuredCompiler) try {
73
+ return resolveExecutableEffectTsgoCompiler(configuredCompiler);
74
+ } catch {
75
+ throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
76
+ }
77
+ try {
78
+ const compiler = execFileSync(process.execPath, [
79
+ resolveEffectTsgoCli(options.from),
80
+ 'get-exe-path'
81
+ ], {
82
+ encoding: 'utf-8'
83
+ }).trim();
84
+ if (compiler) return resolveExecutableEffectTsgoCompiler(compiler);
85
+ } catch {}
86
+ throw new Error(EFFECT_TSGO_RESOLUTION_ERROR);
87
+ }
88
+ export { getBuildConfigEnvironment, resolveEffectTsgoCompiler };
@@ -0,0 +1,76 @@
1
+ import { constants } from "node:zlib";
2
+ import compression_webpack_plugin from "compression-webpack-plugin";
3
+ const DEFAULT_PRECOMPRESS_TEST = /\.(js|mjs|cjs|css|html|svg|json|map)$/i;
4
+ const DEFAULT_PRECOMPRESS_THRESHOLD = 10240;
5
+ const DEFAULT_PRECOMPRESS_MIN_RATIO = 0.8;
6
+ const createDefaultGzipOptions = ()=>({
7
+ algorithm: 'gzip',
8
+ filename: '[path][base].gz',
9
+ test: DEFAULT_PRECOMPRESS_TEST,
10
+ threshold: DEFAULT_PRECOMPRESS_THRESHOLD,
11
+ minRatio: DEFAULT_PRECOMPRESS_MIN_RATIO,
12
+ compressionOptions: {
13
+ level: 6
14
+ }
15
+ });
16
+ const createDefaultBrotliOptions = ()=>({
17
+ algorithm: 'brotliCompress',
18
+ filename: '[path][base].br',
19
+ test: DEFAULT_PRECOMPRESS_TEST,
20
+ threshold: DEFAULT_PRECOMPRESS_THRESHOLD,
21
+ minRatio: DEFAULT_PRECOMPRESS_MIN_RATIO,
22
+ compressionOptions: {
23
+ params: {
24
+ [constants.BROTLI_PARAM_QUALITY]: 9
25
+ }
26
+ }
27
+ });
28
+ const mergeCompressionOptions = (base, next)=>{
29
+ const merged = {
30
+ ...base,
31
+ ...next
32
+ };
33
+ if (base.compressionOptions || next.compressionOptions) {
34
+ const baseCompressionOptions = base.compressionOptions;
35
+ const nextCompressionOptions = next.compressionOptions;
36
+ const mergedCompressionOptions = {
37
+ ...baseCompressionOptions,
38
+ ...nextCompressionOptions
39
+ };
40
+ if (baseCompressionOptions?.params || nextCompressionOptions?.params) mergedCompressionOptions.params = {
41
+ ...baseCompressionOptions?.params,
42
+ ...nextCompressionOptions?.params
43
+ };
44
+ merged.compressionOptions = mergedCompressionOptions;
45
+ }
46
+ return merged;
47
+ };
48
+ const resolveCodecOptions = (codecConfig, createDefaultOptions)=>{
49
+ if (false === codecConfig) return null;
50
+ if (void 0 === codecConfig || true === codecConfig) return createDefaultOptions();
51
+ return mergeCompressionOptions(createDefaultOptions(), codecConfig);
52
+ };
53
+ const resolvePrecompressConfig = (precompress)=>{
54
+ if (false === precompress || void 0 === precompress) return false;
55
+ if (true === precompress) return {};
56
+ return precompress;
57
+ };
58
+ const builderPluginAdapterPrecompress = (precompress)=>({
59
+ name: 'builder-plugin-adapter-modern-precompress',
60
+ setup (api) {
61
+ api.modifyBundlerChain((chain, { isProd, target })=>{
62
+ if (!isProd || 'web' !== target) return;
63
+ const precompressConfig = resolvePrecompressConfig(precompress);
64
+ if (false === precompressConfig) return;
65
+ const gzipOptions = resolveCodecOptions(precompressConfig.gzip, createDefaultGzipOptions);
66
+ if (gzipOptions) chain.plugin('modern-precompress-gzip').use(compression_webpack_plugin, [
67
+ gzipOptions
68
+ ]);
69
+ const brotliOptions = resolveCodecOptions(precompressConfig.brotli, createDefaultBrotliOptions);
70
+ if (brotliOptions) chain.plugin('modern-precompress-brotli').use(compression_webpack_plugin, [
71
+ brotliOptions
72
+ ]);
73
+ });
74
+ }
75
+ });
76
+ export { builderPluginAdapterPrecompress };
File without changes
@@ -0,0 +1 @@
1
+ export { getBuildConfigEnvironment, resolveEffectTsgoCompiler } from "./build-environment.mjs";