@softarc/native-federation 4.1.3 → 4.2.0

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 (198) hide show
  1. package/LICENSE.md +7 -0
  2. package/README.md +638 -0
  3. package/dist/config.js +17 -0
  4. package/dist/domain.js +2 -0
  5. package/dist/index.d.ts +10 -0
  6. package/dist/index.js +21 -0
  7. package/{src → dist}/internal.d.ts +6 -7
  8. package/dist/internal.js +26 -0
  9. package/dist/lib/config/configuration-context.js +15 -0
  10. package/dist/lib/config/default-skip-list.js +36 -0
  11. package/dist/lib/config/get-external-imports.d.ts +2 -0
  12. package/dist/lib/config/get-external-imports.js +60 -0
  13. package/dist/lib/config/get-used-dependencies.d.ts +24 -0
  14. package/dist/lib/config/get-used-dependencies.js +126 -0
  15. package/{src/lib/utils → dist/lib/config}/mapped-paths.d.ts +2 -2
  16. package/dist/lib/config/mapped-paths.js +31 -0
  17. package/dist/lib/config/remove-unused-deps.js +11 -0
  18. package/dist/lib/config/share-utils.d.ts +20 -0
  19. package/dist/lib/config/share-utils.js +344 -0
  20. package/dist/lib/config/with-native-federation.js +80 -0
  21. package/{src/lib/core → dist/lib/core/build}/build-adapter.d.ts +1 -1
  22. package/dist/lib/core/build/build-adapter.js +16 -0
  23. package/dist/lib/core/build/build-for-federation.d.ts +4 -0
  24. package/dist/lib/core/build/build-for-federation.js +207 -0
  25. package/{src/lib/utils → dist/lib/core/build}/build-result-map.d.ts +1 -1
  26. package/dist/lib/core/build/build-result-map.js +39 -0
  27. package/dist/lib/core/build/bundle-exposed-and-mappings.d.ts +13 -0
  28. package/dist/lib/core/build/bundle-exposed-and-mappings.js +178 -0
  29. package/dist/lib/core/build/bundle-shared.d.ts +34 -0
  30. package/dist/lib/core/build/bundle-shared.js +261 -0
  31. package/dist/lib/core/build/compute-integrity.d.ts +11 -0
  32. package/dist/lib/core/build/compute-integrity.js +20 -0
  33. package/dist/lib/core/build/default-external-list.js +32 -0
  34. package/dist/lib/core/build/get-externals.d.ts +2 -0
  35. package/dist/lib/core/build/get-externals.js +9 -0
  36. package/dist/lib/core/build/rebuild-for-federation.d.ts +4 -0
  37. package/dist/lib/core/build/rebuild-for-federation.js +52 -0
  38. package/dist/lib/core/build/rewrite-chunk-imports.d.ts +5 -0
  39. package/dist/lib/core/build/rewrite-chunk-imports.js +74 -0
  40. package/dist/lib/core/cache/cache-persistence.d.ts +22 -0
  41. package/dist/lib/core/cache/cache-persistence.js +63 -0
  42. package/{src/lib/core → dist/lib/core/cache}/federation-cache.d.ts +2 -2
  43. package/dist/lib/core/cache/federation-cache.js +22 -0
  44. package/dist/lib/core/federation-builder.js +53 -0
  45. package/{src → dist}/lib/core/normalize-options.d.ts +13 -1
  46. package/dist/lib/core/normalize-options.js +67 -0
  47. package/dist/lib/core/output/write-federation-info.d.ts +5 -0
  48. package/dist/lib/core/output/write-federation-info.js +17 -0
  49. package/dist/lib/core/output/write-import-map.d.ts +11 -0
  50. package/dist/lib/core/output/write-import-map.js +42 -0
  51. package/dist/lib/core/rebuild-queue.js +61 -0
  52. package/{src → dist}/lib/domain/config/external-config.contract.d.ts +2 -2
  53. package/dist/lib/domain/config/external-config.contract.js +0 -0
  54. package/dist/lib/domain/config/federation-config.contract.js +0 -0
  55. package/dist/lib/domain/config/index.js +0 -0
  56. package/dist/lib/domain/config/skip-list.contract.js +0 -0
  57. package/dist/lib/domain/core/build-adapter.contract.js +0 -0
  58. package/dist/lib/domain/core/build-notification-options.contract.js +9 -0
  59. package/dist/lib/domain/core/chunk.js +12 -0
  60. package/dist/lib/domain/core/federation-cache.contract.js +0 -0
  61. package/dist/lib/domain/core/federation-info.contract.js +0 -0
  62. package/dist/lib/domain/core/federation-options.contract.js +0 -0
  63. package/dist/lib/domain/core/index.js +9 -0
  64. package/dist/lib/domain/core/manifest.contract.js +0 -0
  65. package/dist/lib/domain/utils/file-watcher.contract.js +0 -0
  66. package/dist/lib/domain/utils/io-port.contract.d.ts +45 -0
  67. package/dist/lib/domain/utils/io-port.contract.js +0 -0
  68. package/dist/lib/domain/utils/keyvaluepair.contract.js +0 -0
  69. package/dist/lib/domain/utils/mapped-path.contract.js +0 -0
  70. package/dist/lib/domain/utils/package-json.contract.d.ts +27 -0
  71. package/dist/lib/domain/utils/package-json.contract.js +0 -0
  72. package/dist/lib/domain/utils/used-dependencies.contract.js +0 -0
  73. package/dist/lib/utils/errors.js +10 -0
  74. package/{src → dist}/lib/utils/file-watcher.d.ts +2 -0
  75. package/dist/lib/utils/file-watcher.js +51 -0
  76. package/dist/lib/utils/hash-file.d.ts +7 -0
  77. package/dist/lib/utils/hash-file.js +15 -0
  78. package/dist/lib/utils/io/node-io-adapter.d.ts +2 -0
  79. package/dist/lib/utils/io/node-io-adapter.js +68 -0
  80. package/dist/lib/utils/io/package-json-repository.d.ts +5 -0
  81. package/dist/lib/utils/io/package-json-repository.js +79 -0
  82. package/dist/lib/utils/logger.js +29 -0
  83. package/dist/lib/utils/normalize.js +22 -0
  84. package/dist/lib/utils/package/entry-point-resolver.d.ts +2 -0
  85. package/dist/lib/utils/package/entry-point-resolver.js +78 -0
  86. package/dist/lib/utils/package/esm-detection.d.ts +5 -0
  87. package/dist/lib/utils/package/esm-detection.js +10 -0
  88. package/dist/lib/utils/package/exports-resolver.d.ts +13 -0
  89. package/dist/lib/utils/package/exports-resolver.js +55 -0
  90. package/dist/lib/utils/package/package-info.d.ts +7 -0
  91. package/dist/lib/utils/package/package-info.js +31 -0
  92. package/dist/lib/utils/package/resolve-wildcard-keys.d.ts +3 -0
  93. package/dist/lib/utils/package/resolve-wildcard-keys.js +22 -0
  94. package/dist/lib/utils/package/version-maps.d.ts +3 -0
  95. package/dist/lib/utils/package/version-maps.js +8 -0
  96. package/dist/lib/utils/path-patterns.d.ts +14 -0
  97. package/dist/lib/utils/path-patterns.js +28 -0
  98. package/package.json +45 -22
  99. package/src/config.js +0 -4
  100. package/src/domain.js +0 -2
  101. package/src/index.d.ts +0 -10
  102. package/src/index.js +0 -10
  103. package/src/internal.js +0 -11
  104. package/src/lib/config/configuration-context.js +0 -10
  105. package/src/lib/config/default-skip-list.js +0 -31
  106. package/src/lib/config/remove-unused-deps.js +0 -10
  107. package/src/lib/config/share-utils.d.ts +0 -10
  108. package/src/lib/config/share-utils.js +0 -302
  109. package/src/lib/config/with-native-federation.js +0 -79
  110. package/src/lib/core/build-adapter.js +0 -12
  111. package/src/lib/core/build-for-federation.d.ts +0 -4
  112. package/src/lib/core/build-for-federation.js +0 -173
  113. package/src/lib/core/bundle-exposed-and-mappings.d.ts +0 -7
  114. package/src/lib/core/bundle-exposed-and-mappings.js +0 -177
  115. package/src/lib/core/bundle-shared.d.ts +0 -13
  116. package/src/lib/core/bundle-shared.js +0 -222
  117. package/src/lib/core/default-external-list.js +0 -29
  118. package/src/lib/core/federation-builder.js +0 -45
  119. package/src/lib/core/federation-cache.js +0 -16
  120. package/src/lib/core/get-externals.d.ts +0 -2
  121. package/src/lib/core/get-externals.js +0 -6
  122. package/src/lib/core/normalize-options.js +0 -58
  123. package/src/lib/core/rebuild-for-federation.d.ts +0 -4
  124. package/src/lib/core/rebuild-for-federation.js +0 -43
  125. package/src/lib/core/write-federation-info.d.ts +0 -3
  126. package/src/lib/core/write-federation-info.js +0 -6
  127. package/src/lib/core/write-import-map.d.ts +0 -6
  128. package/src/lib/core/write-import-map.js +0 -33
  129. package/src/lib/domain/config/external-config.contract.js +0 -1
  130. package/src/lib/domain/config/federation-config.contract.js +0 -1
  131. package/src/lib/domain/config/index.js +0 -1
  132. package/src/lib/domain/config/skip-list.contract.js +0 -1
  133. package/src/lib/domain/config/with-native-federation.contract.d.ts +0 -2
  134. package/src/lib/domain/config/with-native-federation.contract.js +0 -1
  135. package/src/lib/domain/core/build-adapter.contract.js +0 -1
  136. package/src/lib/domain/core/build-notification-options.contract.js +0 -6
  137. package/src/lib/domain/core/chunk.js +0 -8
  138. package/src/lib/domain/core/federation-cache.contract.js +0 -1
  139. package/src/lib/domain/core/federation-info.contract.js +0 -1
  140. package/src/lib/domain/core/federation-options.contract.js +0 -1
  141. package/src/lib/domain/core/index.js +0 -2
  142. package/src/lib/domain/core/manifest.contract.js +0 -1
  143. package/src/lib/domain/utils/file-watcher.contract.js +0 -1
  144. package/src/lib/domain/utils/index.d.ts +0 -2
  145. package/src/lib/domain/utils/index.js +0 -1
  146. package/src/lib/domain/utils/keyvaluepair.contract.js +0 -1
  147. package/src/lib/domain/utils/mapped-path.contract.js +0 -5
  148. package/src/lib/domain/utils/used-dependencies.contract.js +0 -1
  149. package/src/lib/utils/build-result-map.js +0 -29
  150. package/src/lib/utils/cache-persistence.d.ts +0 -19
  151. package/src/lib/utils/cache-persistence.js +0 -66
  152. package/src/lib/utils/errors.js +0 -7
  153. package/src/lib/utils/file-watcher.js +0 -51
  154. package/src/lib/utils/get-external-imports.d.ts +0 -1
  155. package/src/lib/utils/get-external-imports.js +0 -80
  156. package/src/lib/utils/get-used-dependencies.d.ts +0 -8
  157. package/src/lib/utils/get-used-dependencies.js +0 -123
  158. package/src/lib/utils/hash-file.d.ts +0 -3
  159. package/src/lib/utils/hash-file.js +0 -13
  160. package/src/lib/utils/logger.js +0 -27
  161. package/src/lib/utils/mapped-paths.js +0 -33
  162. package/src/lib/utils/normalize.js +0 -17
  163. package/src/lib/utils/package-info.d.ts +0 -30
  164. package/src/lib/utils/package-info.js +0 -268
  165. package/src/lib/utils/rebuild-queue.js +0 -63
  166. package/src/lib/utils/resolve-glob.d.ts +0 -1
  167. package/src/lib/utils/resolve-glob.js +0 -29
  168. package/src/lib/utils/resolve-wildcard-keys.d.ts +0 -29
  169. package/src/lib/utils/resolve-wildcard-keys.js +0 -126
  170. package/src/lib/utils/rewrite-chunk-imports.d.ts +0 -2
  171. package/src/lib/utils/rewrite-chunk-imports.js +0 -48
  172. /package/{src → dist}/config.d.ts +0 -0
  173. /package/{src → dist}/domain.d.ts +0 -0
  174. /package/{src → dist}/lib/config/configuration-context.d.ts +0 -0
  175. /package/{src → dist}/lib/config/default-skip-list.d.ts +0 -0
  176. /package/{src → dist}/lib/config/remove-unused-deps.d.ts +0 -0
  177. /package/{src → dist}/lib/config/with-native-federation.d.ts +0 -0
  178. /package/{src/lib/core → dist/lib/core/build}/default-external-list.d.ts +0 -0
  179. /package/{src → dist}/lib/core/federation-builder.d.ts +0 -0
  180. /package/{src/lib/utils → dist/lib/core}/rebuild-queue.d.ts +0 -0
  181. /package/{src → dist}/lib/domain/config/federation-config.contract.d.ts +0 -0
  182. /package/{src → dist}/lib/domain/config/index.d.ts +0 -0
  183. /package/{src → dist}/lib/domain/config/skip-list.contract.d.ts +0 -0
  184. /package/{src → dist}/lib/domain/core/build-adapter.contract.d.ts +0 -0
  185. /package/{src → dist}/lib/domain/core/build-notification-options.contract.d.ts +0 -0
  186. /package/{src → dist}/lib/domain/core/chunk.d.ts +0 -0
  187. /package/{src → dist}/lib/domain/core/federation-cache.contract.d.ts +0 -0
  188. /package/{src → dist}/lib/domain/core/federation-info.contract.d.ts +0 -0
  189. /package/{src → dist}/lib/domain/core/federation-options.contract.d.ts +0 -0
  190. /package/{src → dist}/lib/domain/core/index.d.ts +0 -0
  191. /package/{src → dist}/lib/domain/core/manifest.contract.d.ts +0 -0
  192. /package/{src → dist}/lib/domain/utils/file-watcher.contract.d.ts +0 -0
  193. /package/{src → dist}/lib/domain/utils/keyvaluepair.contract.d.ts +0 -0
  194. /package/{src → dist}/lib/domain/utils/mapped-path.contract.d.ts +0 -0
  195. /package/{src → dist}/lib/domain/utils/used-dependencies.contract.d.ts +0 -0
  196. /package/{src → dist}/lib/utils/errors.d.ts +0 -0
  197. /package/{src → dist}/lib/utils/logger.d.ts +0 -0
  198. /package/{src → dist}/lib/utils/normalize.d.ts +0 -0
@@ -0,0 +1,39 @@
1
+ import path from "path";
2
+ function stripHash(fileName) {
3
+ const start = fileName.lastIndexOf("-");
4
+ const end = fileName.lastIndexOf(".");
5
+ if (start < 0 || end < 0 || start > end) return fileName;
6
+ return fileName.substring(0, start) + fileName.substring(end);
7
+ }
8
+ function createBuildResultMap(buildResult, isHashed, expectedNames = []) {
9
+ const expected = new Set(expectedNames.map((n) => path.basename(n)));
10
+ const map = {};
11
+ for (const item of buildResult) {
12
+ const resultName = path.basename(item.fileName);
13
+ const requestName = isHashed && expected.has(stripHash(resultName)) ? stripHash(resultName) : resultName;
14
+ map[requestName] = item.fileName;
15
+ }
16
+ return map;
17
+ }
18
+ function requireEntry(map, key) {
19
+ const value = map[key];
20
+ if (value === void 0) {
21
+ throw new Error(`No build result found for '${key}'.`);
22
+ }
23
+ return value;
24
+ }
25
+ function lookupInResultMap(map, requestName) {
26
+ const key = path.basename(requestName);
27
+ return path.basename(requireEntry(map, key));
28
+ }
29
+ function popFromResultMap(map, requestName) {
30
+ const key = path.basename(requestName);
31
+ const out = requireEntry(map, key);
32
+ delete map[key];
33
+ return out;
34
+ }
35
+ export {
36
+ createBuildResultMap,
37
+ lookupInResultMap,
38
+ popFromResultMap
39
+ };
@@ -0,0 +1,13 @@
1
+ import type { ArtifactInfo, ExposesInfo, SharedInfo } from '../../domain/core/federation-info.contract.js';
2
+ import type { FileReaderPort } from '../../domain/utils/io-port.contract.js';
3
+ import type { NormalizedFederationConfig } from '../../domain/config/federation-config.contract.js';
4
+ import { type NormalizedFederationOptions } from '../../domain/core/federation-options.contract.js';
5
+ import type { NFBuildAdapter } from '../../domain/core/build-adapter.contract.js';
6
+ export declare function bundleExposedAndMappings(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], modifiedFiles?: string[], signal?: AbortSignal): Promise<ArtifactInfo>;
7
+ export declare function bundleExposedAndMappingsCore(deps: {
8
+ adapter: NFBuildAdapter;
9
+ }, config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], modifiedFiles?: string[], signal?: AbortSignal): Promise<ArtifactInfo>;
10
+ export declare function describeExposed(config: NormalizedFederationConfig, options: NormalizedFederationOptions): Array<ExposesInfo>;
11
+ export declare function describeSharedMappings(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions): Array<SharedInfo>;
12
+ export declare function getMappingVersionCore(io: FileReaderPort, fileName: string, workspaceRoot: string): string;
13
+ export declare function getMappingVersion(fileName: string, workspaceRoot: string): string;
@@ -0,0 +1,178 @@
1
+ import path from "path";
2
+ import { createBuildResultMap, popFromResultMap } from "./build-result-map.js";
3
+ import { computeIntegrityMap } from "./compute-integrity.js";
4
+ import { logger } from "../../utils/logger.js";
5
+ import { normalize } from "../../utils/normalize.js";
6
+ import { nodeIo } from "../../utils/io/node-io-adapter.js";
7
+ import { AbortedError } from "../../utils/errors.js";
8
+ import { rewriteChunkImports } from "./rewrite-chunk-imports.js";
9
+ import { getBuildAdapter } from "./build-adapter.js";
10
+ async function bundleExposedAndMappings(config, fedOptions, externals, modifiedFiles, signal) {
11
+ return bundleExposedAndMappingsCore(
12
+ { adapter: getBuildAdapter() },
13
+ config,
14
+ fedOptions,
15
+ externals,
16
+ modifiedFiles,
17
+ signal
18
+ );
19
+ }
20
+ async function bundleExposedAndMappingsCore(deps, config, fedOptions, externals, modifiedFiles, signal) {
21
+ if (signal?.aborted) {
22
+ throw new AbortedError("[bundle-exposed-and-mappings] Aborted before bundling");
23
+ }
24
+ const shared = Object.entries(config.sharedMappings).map(
25
+ ([entryPoint, mappedImport]) => {
26
+ return {
27
+ fileName: entryPoint,
28
+ outName: mappedImport.replace(/[^A-Za-z0-9]/g, "_") + ".js",
29
+ key: mappedImport
30
+ };
31
+ }
32
+ );
33
+ const exposes = Object.entries(config.exposes).map(
34
+ ([key, expose]) => {
35
+ const outFilePath = key + ".js";
36
+ return { fileName: expose.file, outName: outFilePath, key, element: expose.element };
37
+ }
38
+ );
39
+ const entryPoints = [...shared, ...exposes];
40
+ const hash = !fedOptions.dev;
41
+ let result;
42
+ try {
43
+ if (!modifiedFiles) {
44
+ await deps.adapter.setup("mapping-or-exposed", {
45
+ entryPoints,
46
+ outdir: fedOptions.outputPath,
47
+ tsConfigPath: fedOptions.tsConfig,
48
+ external: externals,
49
+ dev: !!fedOptions.dev,
50
+ watch: fedOptions.watch,
51
+ mappedPaths: config.sharedMappings,
52
+ chunks: config.chunks,
53
+ hash,
54
+ optimizedMappings: config.features.ignoreUnusedDeps,
55
+ isMappingOrExposed: true,
56
+ cache: fedOptions.federationCache
57
+ });
58
+ }
59
+ result = await deps.adapter.build("mapping-or-exposed", {
60
+ signal,
61
+ modifiedFiles
62
+ });
63
+ if (signal?.aborted) {
64
+ throw new AbortedError("[bundle-exposed-and-mappings] Aborted after bundle");
65
+ }
66
+ } catch (error) {
67
+ if (!(error instanceof AbortedError)) {
68
+ logger.error("Error building federation artifacts");
69
+ }
70
+ throw error;
71
+ }
72
+ const resultMap = createBuildResultMap(result, hash, [
73
+ ...shared.map((s) => s.outName),
74
+ ...exposes.map((e) => e.outName)
75
+ ]);
76
+ const sharedResult = [];
77
+ const entryFiles = [];
78
+ for (const item of shared) {
79
+ const distEntryFile = popFromResultMap(resultMap, item.outName);
80
+ sharedResult.push(
81
+ toSharedMappingInfo(item.fileName, item.key, path.basename(distEntryFile), config, fedOptions)
82
+ );
83
+ entryFiles.push(distEntryFile);
84
+ }
85
+ const exposedResult = [];
86
+ for (const item of exposes) {
87
+ const distEntryFile = popFromResultMap(resultMap, item.outName);
88
+ exposedResult.push({
89
+ key: item.key,
90
+ outFileName: path.basename(distEntryFile),
91
+ ...item.element && { element: item.element },
92
+ dev: !fedOptions.dev ? void 0 : {
93
+ entryPoint: normalize(path.join(fedOptions.workspaceRoot, item.fileName))
94
+ }
95
+ });
96
+ entryFiles.push(distEntryFile);
97
+ }
98
+ Object.keys(resultMap).filter((f) => f.endsWith(".map")).forEach((f) => popFromResultMap(resultMap, f));
99
+ let exportedChunks = void 0;
100
+ const chunkPaths = [];
101
+ if (config.chunks && config.features.denseChunking) {
102
+ for (const entryFile of entryFiles) rewriteChunkImports(entryFile);
103
+ chunkPaths.push(...Object.values(resultMap));
104
+ exportedChunks = {
105
+ ["mapping-or-exposed"]: chunkPaths.map((chunk) => path.basename(chunk))
106
+ };
107
+ }
108
+ const integrity = config.features.integrityHashes ? computeIntegrityMap([...entryFiles, ...chunkPaths], "") : void 0;
109
+ return { mappings: sharedResult, exposes: exposedResult, chunks: exportedChunks, integrity };
110
+ }
111
+ function describeExposed(config, options) {
112
+ const result = [];
113
+ for (const key in config.exposes) {
114
+ const expose = config.exposes[key];
115
+ const localPath = normalize(
116
+ path.normalize(path.join(options.workspaceRoot, expose.file))
117
+ );
118
+ result.push({
119
+ key,
120
+ outFileName: "",
121
+ ...expose.element && { element: expose.element },
122
+ dev: !options.dev ? void 0 : {
123
+ entryPoint: localPath
124
+ }
125
+ });
126
+ }
127
+ return result;
128
+ }
129
+ function describeSharedMappings(config, fedOptions) {
130
+ const result = [];
131
+ for (const [mappedPath, mappedImport] of Object.entries(config.sharedMappings)) {
132
+ result.push(toSharedMappingInfo(mappedPath, mappedImport, "", config, fedOptions));
133
+ }
134
+ return result;
135
+ }
136
+ function toSharedMappingInfo(mappedPath, mappedImport, outFileName, config, fedOptions) {
137
+ const mappingVersion = config.features.mappingVersion ? getMappingVersion(mappedPath, fedOptions.workspaceRoot) : "";
138
+ return {
139
+ packageName: mappedImport,
140
+ outFileName,
141
+ requiredVersion: mappingVersion.length > 0 ? "~" + mappingVersion : "",
142
+ singleton: true,
143
+ strictVersion: config.features.mappingVersion,
144
+ version: mappingVersion,
145
+ dev: !fedOptions.dev ? void 0 : {
146
+ entryPoint: normalize(path.normalize(mappedPath))
147
+ }
148
+ };
149
+ }
150
+ function getMappingVersionCore(io, fileName, workspaceRoot) {
151
+ const resolvedRoot = path.resolve(workspaceRoot);
152
+ let dir = path.dirname(path.resolve(fileName));
153
+ while (true) {
154
+ const candidate = path.join(dir, "package.json");
155
+ if (io.isFile(candidate)) {
156
+ try {
157
+ const json = JSON.parse(io.readText(candidate));
158
+ if (typeof json.version === "string" && json.version) return json.version;
159
+ } catch (err) {
160
+ logger.warn(`[getMappingVersion] Failed to parse ${candidate}: ${err.message}`);
161
+ }
162
+ }
163
+ const parent = path.dirname(dir);
164
+ if (dir === resolvedRoot || parent === dir) return "";
165
+ dir = parent;
166
+ }
167
+ }
168
+ function getMappingVersion(fileName, workspaceRoot) {
169
+ return getMappingVersionCore(nodeIo, fileName, workspaceRoot);
170
+ }
171
+ export {
172
+ bundleExposedAndMappings,
173
+ bundleExposedAndMappingsCore,
174
+ describeExposed,
175
+ describeSharedMappings,
176
+ getMappingVersion,
177
+ getMappingVersionCore
178
+ };
@@ -0,0 +1,34 @@
1
+ import type { NormalizedFederationConfig } from '../../domain/config/federation-config.contract.js';
2
+ import type { PackageJsonRepository } from '../../domain/utils/package-json.contract.js';
3
+ import type { IntegrityMap, SharedInfo } from '../../domain/core/federation-info.contract.js';
4
+ import type { HashPort, IoPort } from '../../domain/utils/io-port.contract.js';
5
+ import { type NormalizedFederationOptions } from '../../domain/core/federation-options.contract.js';
6
+ import type { NormalizedExternalConfig } from '../../domain/config/external-config.contract.js';
7
+ import type { NFBuildAdapter } from '../../domain/core/build-adapter.contract.js';
8
+ export declare function bundleShared(sharedBundles: Record<string, NormalizedExternalConfig>, config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], buildOptions: {
9
+ platform: 'browser' | 'node';
10
+ bundleName: string;
11
+ chunks: boolean;
12
+ }): Promise<{
13
+ externals: SharedInfo[];
14
+ chunks?: Record<string, string[]>;
15
+ integrity?: IntegrityMap;
16
+ }>;
17
+ interface BundleSharedDeps {
18
+ io: IoPort;
19
+ repo: PackageJsonRepository;
20
+ adapter: NFBuildAdapter;
21
+ }
22
+ export declare function bundleSharedCore(deps: BundleSharedDeps, sharedBundles: Record<string, NormalizedExternalConfig>, config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], buildOptions: {
23
+ platform: 'browser' | 'node';
24
+ bundleName: string;
25
+ chunks: boolean;
26
+ }): Promise<{
27
+ externals: SharedInfo[];
28
+ chunks?: Record<string, string[]>;
29
+ integrity?: IntegrityMap;
30
+ }>;
31
+ export declare function readBuilderPackageJson(io: IoPort, fromFile: string): string;
32
+ export declare function parseBuilderVersion(packageJson: string): string;
33
+ export declare function calcHashCore(hash: HashPort, hashBase: string): string;
34
+ export {};
@@ -0,0 +1,261 @@
1
+ import * as path from "path";
2
+ import {
3
+ sharedPackageJsonRepository,
4
+ getPackageInfo
5
+ } from "../../utils/package/package-info.js";
6
+ import { logger } from "../../utils/logger.js";
7
+ import { nodeIo } from "../../utils/io/node-io-adapter.js";
8
+ import { DEFAULT_EXTERNAL_LIST } from "./default-external-list.js";
9
+ import { isSourceFile, transformChunkImports } from "./rewrite-chunk-imports.js";
10
+ import { toChunkImport } from "../../domain/core/chunk.js";
11
+ import { cacheEntryCore, getChecksumCore, getFilename } from "../cache/cache-persistence.js";
12
+ import { computeIntegrityMapCore } from "./compute-integrity.js";
13
+ import { fileURLToPath } from "url";
14
+ import { getBuildAdapter } from "./build-adapter.js";
15
+ async function bundleShared(sharedBundles, config, fedOptions, externals, buildOptions) {
16
+ return bundleSharedCore(
17
+ { io: nodeIo, repo: sharedPackageJsonRepository, adapter: getBuildAdapter() },
18
+ sharedBundles,
19
+ config,
20
+ fedOptions,
21
+ externals,
22
+ buildOptions
23
+ );
24
+ }
25
+ async function bundleSharedCore(deps, sharedBundles, config, fedOptions, externals, buildOptions) {
26
+ const builderPackageJson = readBuilderPackageJson(deps.io, fileURLToPath(import.meta.url));
27
+ const builderVersion = parseBuilderVersion(builderPackageJson);
28
+ const checksum = getChecksumCore(
29
+ deps.io,
30
+ sharedBundles,
31
+ fedOptions.dev ? "1" : "0",
32
+ builderVersion
33
+ );
34
+ const folder = fedOptions.packageJson ? path.dirname(fedOptions.packageJson) : fedOptions.workspaceRoot;
35
+ const bundleCache = cacheEntryCore(
36
+ deps.io,
37
+ fedOptions.federationCache.cachePath,
38
+ getFilename(buildOptions.bundleName, fedOptions.dev)
39
+ );
40
+ if (fedOptions?.cacheExternalArtifacts) {
41
+ const cacheMetadata = bundleCache.getMetadata(checksum);
42
+ if (cacheMetadata) {
43
+ logger.info(`Checksum of ${buildOptions.bundleName} matched, re-using cached externals.`);
44
+ bundleCache.copyFiles(path.join(fedOptions.workspaceRoot, fedOptions.outputPath));
45
+ let integrity2 = cacheMetadata.integrity;
46
+ if (config.features.integrityHashes && !integrity2) {
47
+ integrity2 = computeIntegrityMapCore(
48
+ deps.io,
49
+ cacheMetadata.files,
50
+ fedOptions.federationCache.cachePath
51
+ );
52
+ }
53
+ return {
54
+ externals: cacheMetadata.externals,
55
+ chunks: cacheMetadata.chunks,
56
+ integrity: integrity2
57
+ };
58
+ }
59
+ }
60
+ bundleCache.clear();
61
+ const inferredPackageInfos = Object.keys(sharedBundles).filter((packageName) => !sharedBundles[packageName]?.packageInfo).map((packageName) => getPackageInfo(packageName, folder, deps.repo)).filter((pi) => !!pi);
62
+ const configuredPackageInfos = Object.keys(sharedBundles).filter((packageName) => !!sharedBundles[packageName]?.packageInfo).map((packageName) => ({
63
+ packageName,
64
+ ...sharedBundles[packageName]?.packageInfo
65
+ }));
66
+ const packageInfos = [...inferredPackageInfos, ...configuredPackageInfos];
67
+ const configState = `BUNDLER_CHUNKS;${builderVersion};${JSON.stringify(config)}`;
68
+ const entryPoints = packageInfos.map((pi) => {
69
+ const encName = pi.packageName.replace(/[^A-Za-z0-9]/g, "_");
70
+ const outName = createOutName(deps.io, pi, configState, fedOptions, encName);
71
+ return { fileName: pi.entryPoint, outName };
72
+ });
73
+ const fullOutputPath = path.join(fedOptions.workspaceRoot, fedOptions.outputPath);
74
+ const useDefaultExternalList = buildOptions.platform === "browser" && !config.features.ignoreUnusedDeps;
75
+ const additionalExternals = useDefaultExternalList ? DEFAULT_EXTERNAL_LIST : [];
76
+ let bundleResult;
77
+ try {
78
+ await deps.adapter.setup(buildOptions.bundleName, {
79
+ entryPoints,
80
+ tsConfigPath: fedOptions.tsConfig,
81
+ external: [...additionalExternals, ...externals],
82
+ outdir: fedOptions.federationCache.cachePath,
83
+ mappedPaths: config.sharedMappings,
84
+ dev: fedOptions.dev,
85
+ isMappingOrExposed: false,
86
+ hash: false,
87
+ chunks: buildOptions.chunks,
88
+ platform: buildOptions.platform,
89
+ optimizedMappings: config.features.ignoreUnusedDeps,
90
+ cache: fedOptions.federationCache
91
+ });
92
+ bundleResult = await deps.adapter.build(buildOptions.bundleName);
93
+ await deps.adapter.dispose(buildOptions.bundleName);
94
+ const cachedFiles = bundleResult.map((br) => path.basename(br.fileName));
95
+ const hashEntries = fedOptions.dev ? /* @__PURE__ */ new Set() : new Set(entryPoints.map((ep) => ep.outName));
96
+ const renamed = rewriteImports(
97
+ deps.io,
98
+ cachedFiles,
99
+ fedOptions.federationCache.cachePath,
100
+ hashEntries
101
+ );
102
+ applyRenames(bundleResult, entryPoints, renamed);
103
+ } catch (e) {
104
+ logger.error("Error bundling shared npm package ");
105
+ if (e instanceof Error) {
106
+ logger.error(e.message);
107
+ }
108
+ logger.error("For more information, run in verbose mode");
109
+ logger.notice("");
110
+ logger.notice("");
111
+ logger.notice("** Important Information: ***");
112
+ logger.notice("The error message above shows an issue with bundling a node_module.");
113
+ logger.notice("In most cases this is because you (indirectly) shared a Node.js package,");
114
+ logger.notice("while Native Federation builds for the browser.");
115
+ logger.notice(
116
+ "You can move such packages into devDependencies or skip them in your federation.config.js."
117
+ );
118
+ logger.notice("");
119
+ logger.notice("More Details: https://bit.ly/nf-issue");
120
+ logger.notice("");
121
+ logger.notice("");
122
+ logger.verbose(e);
123
+ throw e;
124
+ }
125
+ const outFileNames = entryPoints.map((ep) => path.join(fullOutputPath, ep.outName));
126
+ const result = buildResult(packageInfos, sharedBundles, outFileNames);
127
+ const chunks = bundleResult.filter(
128
+ (br) => !br.fileName.endsWith(".map") && !result.find((r) => r.outFileName === path.basename(br.fileName))
129
+ );
130
+ let exportedChunks = void 0;
131
+ if (buildOptions.chunks && config.features.denseChunking) {
132
+ result.forEach((external) => {
133
+ external.bundle = buildOptions.bundleName;
134
+ });
135
+ if (chunks.length > 0) {
136
+ exportedChunks = { [buildOptions.bundleName]: getChunkFileNames(chunks) };
137
+ }
138
+ } else {
139
+ addChunksToResult(chunks, result);
140
+ }
141
+ const persistedFiles = bundleResult.map((r) => r.fileName.split(path.sep).pop() ?? r.fileName);
142
+ const integrity = config.features.integrityHashes ? computeIntegrityMapCore(deps.io, persistedFiles, fedOptions.federationCache.cachePath) : void 0;
143
+ bundleCache.persist({
144
+ checksum,
145
+ externals: result,
146
+ files: persistedFiles,
147
+ chunks: exportedChunks,
148
+ integrity
149
+ });
150
+ bundleCache.copyFiles(path.join(fedOptions.workspaceRoot, fedOptions.outputPath));
151
+ return { externals: result, chunks: exportedChunks, integrity };
152
+ }
153
+ function rewriteImports(io, cachedFiles, cachePath, hashEntries) {
154
+ const renamed = /* @__PURE__ */ new Map();
155
+ for (const file of cachedFiles.filter(isSourceFile)) {
156
+ const filePath = path.join(cachePath, file);
157
+ const rewritten = transformChunkImports(io.readText(filePath), file);
158
+ if (hashEntries.has(file)) {
159
+ const hashedName = `${file.split(".")[0]}.${calcHashCore(io, rewritten)}.js`;
160
+ io.writeText(path.join(cachePath, hashedName), rewritten);
161
+ io.remove(filePath);
162
+ renamed.set(file, hashedName);
163
+ } else {
164
+ io.writeText(filePath, rewritten);
165
+ }
166
+ }
167
+ return renamed;
168
+ }
169
+ function applyRenames(bundleResult, entryPoints, renamed) {
170
+ if (renamed.size === 0) return;
171
+ for (const br of bundleResult) {
172
+ const next = renamed.get(path.basename(br.fileName));
173
+ if (next) br.fileName = path.join(path.dirname(br.fileName), next);
174
+ }
175
+ for (const ep of entryPoints) {
176
+ const next = renamed.get(ep.outName);
177
+ if (next) ep.outName = next;
178
+ }
179
+ }
180
+ function createOutName(io, pi, configState, fedOptions, encName) {
181
+ const hashBase = pi.version + "_" + pi.entryPoint + "_" + configState;
182
+ const hash = calcHashCore(io, hashBase);
183
+ const outName = fedOptions.dev ? `${encName}.${hash}-dev.js` : `${encName}.${hash}.js`;
184
+ return outName;
185
+ }
186
+ function buildResult(packageInfos, sharedBundles, outFileNames) {
187
+ return packageInfos.map((pi) => {
188
+ const shared = sharedBundles[pi.packageName];
189
+ return {
190
+ packageName: pi.packageName,
191
+ outFileName: path.basename(outFileNames.shift() || ""),
192
+ requiredVersion: shared?.requiredVersion,
193
+ singleton: shared?.singleton,
194
+ strictVersion: shared?.strictVersion,
195
+ version: pi.version,
196
+ ...shared?.shareScope && { shareScope: shared.shareScope }
197
+ // TODO: Decide whether/when we need debug infos
198
+ // dev: !fedOptions.dev
199
+ // ? undefined
200
+ // : {
201
+ // entryPoint: normalize(pi.entryPoint),
202
+ // },
203
+ };
204
+ });
205
+ }
206
+ function getChunkFileNames(chunks) {
207
+ return chunks.map((chunk) => path.basename(chunk.fileName));
208
+ }
209
+ function addChunksToResult(chunks, result) {
210
+ for (const item of chunks) {
211
+ const fileName = path.basename(item.fileName);
212
+ result.push({
213
+ singleton: false,
214
+ strictVersion: false,
215
+ // Here, the version does not matter because
216
+ // a) a chunk split off by the bundler does
217
+ // not have a version and b) it gets a hash
218
+ // code as part of the file name to be unique
219
+ // when requested via a _versioned_ package.
220
+ //
221
+ // For the same reason, we don't need to
222
+ // take care of singleton and strictVersion.
223
+ version: "0.0.0",
224
+ requiredVersion: "0.0.0",
225
+ packageName: toChunkImport(fileName),
226
+ outFileName: fileName
227
+ // dev: dev
228
+ // ? undefined
229
+ // : {
230
+ // entryPoint: normalize(fileName),
231
+ // },
232
+ });
233
+ }
234
+ }
235
+ function readBuilderPackageJson(io, fromFile) {
236
+ let dir = path.dirname(fromFile);
237
+ for (; ; ) {
238
+ const candidate = path.join(dir, "package.json");
239
+ if (io.exists(candidate)) return io.readText(candidate);
240
+ const parent = path.dirname(dir);
241
+ if (parent === dir || path.basename(parent) === "node_modules") return "{}";
242
+ dir = parent;
243
+ }
244
+ }
245
+ function parseBuilderVersion(packageJson) {
246
+ try {
247
+ return JSON.parse(packageJson).version ?? "";
248
+ } catch {
249
+ return "";
250
+ }
251
+ }
252
+ function calcHashCore(hash, hashBase) {
253
+ return hash.hash("sha256", hashBase).base64().replace(/\//g, "_").replace(/\+/g, "-").replace(/=/g, "").substring(0, 10);
254
+ }
255
+ export {
256
+ bundleShared,
257
+ bundleSharedCore,
258
+ calcHashCore,
259
+ parseBuilderVersion,
260
+ readBuilderPackageJson
261
+ };
@@ -0,0 +1,11 @@
1
+ import type { FileReaderPort, HashPort } from '../../domain/utils/io-port.contract.js';
2
+ import type { IntegrityMap } from '../../domain/core/federation-info.contract.js';
3
+ type IntegrityDeps = FileReaderPort & HashPort;
4
+ /**
5
+ * Build an SRI map for `files` (resolved against `baseDir`), skipping sourcemaps
6
+ * and files that no longer exist on disk. Keyed by basename. Must run after any
7
+ * post-bundle rewriting so the hashes match the final on-disk bytes.
8
+ */
9
+ export declare function computeIntegrityMapCore(io: IntegrityDeps, files: string[], baseDir: string): IntegrityMap;
10
+ export declare function computeIntegrityMap(files: string[], baseDir: string): IntegrityMap;
11
+ export {};
@@ -0,0 +1,20 @@
1
+ import * as path from "path";
2
+ import { nodeIo } from "../../utils/io/node-io-adapter.js";
3
+ import { integrityForFileCore } from "../../utils/hash-file.js";
4
+ function computeIntegrityMapCore(io, files, baseDir) {
5
+ const integrity = {};
6
+ for (const file of files) {
7
+ if (file.endsWith(".map")) continue;
8
+ const fullPath = path.join(baseDir, file);
9
+ if (!io.exists(fullPath)) continue;
10
+ integrity[path.basename(file)] = integrityForFileCore(io, fullPath);
11
+ }
12
+ return integrity;
13
+ }
14
+ function computeIntegrityMap(files, baseDir) {
15
+ return computeIntegrityMapCore(nodeIo, files, baseDir);
16
+ }
17
+ export {
18
+ computeIntegrityMap,
19
+ computeIntegrityMapCore
20
+ };
@@ -0,0 +1,32 @@
1
+ const NODE_PACKAGES = [
2
+ "assert",
3
+ "buffer",
4
+ "child_process",
5
+ "cluster",
6
+ "crypto",
7
+ "dgram",
8
+ "dns",
9
+ "events",
10
+ "fs",
11
+ "http",
12
+ "https",
13
+ "module",
14
+ "net",
15
+ "os",
16
+ "path",
17
+ "querystring",
18
+ "readline",
19
+ "stream",
20
+ "timers",
21
+ "tls",
22
+ "tty",
23
+ "url",
24
+ "util",
25
+ "v8",
26
+ "vm",
27
+ "zlib"
28
+ ];
29
+ const DEFAULT_EXTERNAL_LIST = NODE_PACKAGES.flatMap((p) => [p, "node:" + p]);
30
+ export {
31
+ DEFAULT_EXTERNAL_LIST
32
+ };
@@ -0,0 +1,2 @@
1
+ import type { NormalizedFederationConfig } from '../../domain/config/federation-config.contract.js';
2
+ export declare function getExternals(config: NormalizedFederationConfig): string[];
@@ -0,0 +1,9 @@
1
+ function getExternals(config) {
2
+ const shared = Object.keys(config.shared);
3
+ const sharedMappings = Object.values(config.sharedMappings);
4
+ const externals = [...shared, ...sharedMappings, ...config.externals];
5
+ return externals;
6
+ }
7
+ export {
8
+ getExternals
9
+ };
@@ -0,0 +1,4 @@
1
+ import type { FederationInfo } from '../../domain/core/federation-info.contract.js';
2
+ import type { NormalizedFederationOptions } from '../../domain/core/federation-options.contract.js';
3
+ import type { NormalizedFederationConfig } from '../../domain/config/federation-config.contract.js';
4
+ export declare function rebuildForFederation(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], modifiedFiles: string[], signal?: AbortSignal): Promise<FederationInfo>;
@@ -0,0 +1,52 @@
1
+ import {
2
+ bundleExposedAndMappings,
3
+ describeExposed,
4
+ describeSharedMappings
5
+ } from "./bundle-exposed-and-mappings.js";
6
+ import { writeFederationInfo } from "../output/write-federation-info.js";
7
+ import { writeImportMap } from "../output/write-import-map.js";
8
+ import { logger } from "../../utils/logger.js";
9
+ import { AbortedError } from "../../utils/errors.js";
10
+ async function rebuildForFederation(config, fedOptions, externals, modifiedFiles, signal) {
11
+ const federationCache = fedOptions.federationCache;
12
+ logger.info(`Re-bundling all internal libraries and exposed modules..'`);
13
+ const start = process.hrtime();
14
+ const artifactInfo = await bundleExposedAndMappings(
15
+ config,
16
+ fedOptions,
17
+ externals,
18
+ modifiedFiles,
19
+ signal
20
+ );
21
+ logger.measure(start, "To re-bundle all internal libraries and exposed modules.");
22
+ if (signal?.aborted)
23
+ throw new AbortedError("[buildForFederation] After exposed-and-mappings bundle");
24
+ const exposedInfo = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
25
+ const sharedMappingInfo = !artifactInfo ? describeSharedMappings(config, fedOptions) : artifactInfo.mappings;
26
+ const sharedExternals = [...federationCache.externals, ...sharedMappingInfo];
27
+ const buildNotificationsEndpoint = fedOptions.buildNotifications?.enable && fedOptions.dev ? fedOptions.buildNotifications?.endpoint : void 0;
28
+ const federationInfo = {
29
+ name: config.name,
30
+ shared: sharedExternals,
31
+ exposes: exposedInfo,
32
+ buildNotificationsEndpoint
33
+ };
34
+ if (federationCache.chunks) {
35
+ federationInfo.chunks = federationCache.chunks;
36
+ }
37
+ if (artifactInfo?.chunks) {
38
+ federationInfo.chunks = { ...federationInfo.chunks ?? {}, ...artifactInfo?.chunks };
39
+ }
40
+ if (config.features.integrityHashes) {
41
+ federationInfo.integrity = {
42
+ ...federationCache.integrity ?? {},
43
+ ...artifactInfo?.integrity ?? {}
44
+ };
45
+ }
46
+ writeFederationInfo(federationInfo, fedOptions);
47
+ writeImportMap(federationCache, fedOptions, federationInfo.integrity);
48
+ return federationInfo;
49
+ }
50
+ export {
51
+ rebuildForFederation
52
+ };
@@ -0,0 +1,5 @@
1
+ import type { FileReaderPort, FileWriterPort } from '../../domain/utils/io-port.contract.js';
2
+ export declare function rewriteChunkImports(filePath: string): void;
3
+ export declare function rewriteChunkImportsCore(io: FileReaderPort & FileWriterPort, filePath: string): void;
4
+ export declare function transformChunkImports(sourceCode: string, fileName: string): string;
5
+ export declare function isSourceFile(fileName: string): boolean;