@softarc/native-federation 4.0.0-RC8 → 4.0.0-RC9

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 (49) hide show
  1. package/package.json +1 -1
  2. package/src/index.d.ts +1 -2
  3. package/src/index.js +1 -2
  4. package/src/internal.d.ts +2 -1
  5. package/src/internal.js +1 -0
  6. package/src/lib/config/default-skip-list.js +3 -15
  7. package/src/lib/config/remove-unused-deps.d.ts +3 -0
  8. package/src/lib/config/remove-unused-deps.js +10 -0
  9. package/src/lib/config/share-utils.d.ts +0 -1
  10. package/src/lib/config/share-utils.js +7 -13
  11. package/src/lib/config/with-native-federation.js +40 -61
  12. package/src/lib/core/build-for-federation.js +21 -22
  13. package/src/lib/core/bundle-exposed-and-mappings.js +15 -16
  14. package/src/lib/core/bundle-shared.d.ts +1 -0
  15. package/src/lib/core/bundle-shared.js +8 -7
  16. package/src/lib/core/federation-builder.d.ts +6 -1
  17. package/src/lib/core/federation-builder.js +18 -8
  18. package/src/lib/core/get-externals.js +1 -1
  19. package/src/lib/core/normalize-options.d.ts +12 -0
  20. package/src/lib/core/normalize-options.js +90 -0
  21. package/src/lib/core/rebuild-for-federation.js +2 -2
  22. package/src/lib/domain/config/external-config.contract.d.ts +3 -1
  23. package/src/lib/domain/config/federation-config.contract.d.ts +8 -2
  24. package/src/lib/domain/config/with-native-federation.contract.d.ts +2 -0
  25. package/src/lib/domain/config/with-native-federation.contract.js +1 -0
  26. package/src/lib/domain/core/build-adapter.contract.d.ts +12 -6
  27. package/src/lib/domain/core/federation-options.contract.d.ts +4 -5
  28. package/src/lib/domain/core/index.d.ts +1 -1
  29. package/src/lib/domain/utils/index.d.ts +1 -1
  30. package/src/lib/domain/utils/mapped-path.contract.d.ts +1 -4
  31. package/src/lib/domain/utils/mapped-path.contract.js +4 -0
  32. package/src/lib/domain/utils/used-dependencies.contract.d.ts +5 -0
  33. package/src/lib/domain/utils/used-dependencies.contract.js +1 -0
  34. package/src/lib/utils/get-used-dependencies.d.ts +7 -0
  35. package/src/lib/utils/get-used-dependencies.js +123 -0
  36. package/src/lib/utils/mapped-paths.d.ts +7 -7
  37. package/src/lib/utils/mapped-paths.js +14 -12
  38. package/src/lib/utils/resolve-wildcard-keys.d.ts +29 -2
  39. package/src/lib/utils/resolve-wildcard-keys.js +105 -38
  40. package/src/lib/core/default-server-deps-list.d.ts +0 -2
  41. package/src/lib/core/default-server-deps-list.js +0 -6
  42. package/src/lib/core/load-federation-config.d.ts +0 -3
  43. package/src/lib/core/load-federation-config.js +0 -18
  44. package/src/lib/core/normalize-federation-options.d.ts +0 -4
  45. package/src/lib/core/normalize-federation-options.js +0 -10
  46. package/src/lib/core/remove-unused-deps.d.ts +0 -2
  47. package/src/lib/core/remove-unused-deps.js +0 -90
  48. package/src/lib/utils/config-utils.d.ts +0 -2
  49. package/src/lib/utils/config-utils.js +0 -9
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softarc/native-federation",
3
- "version": "4.0.0-RC8",
3
+ "version": "4.0.0-RC9",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "dependencies": {
package/src/index.d.ts CHANGED
@@ -1,11 +1,10 @@
1
1
  export { setBuildAdapter } from './lib/core/build-adapter.js';
2
2
  export { buildForFederation } from './lib/core/build-for-federation.js';
3
3
  export { rebuildForFederation } from './lib/core/rebuild-for-federation.js';
4
- export { normalizeFederationOptions } from './lib/core/normalize-federation-options.js';
5
4
  export { createFederationCache } from './lib/core/federation-cache.js';
6
5
  export { bundleExposedAndMappings } from './lib/core/bundle-exposed-and-mappings.js';
7
6
  export { getExternals } from './lib/core/get-externals.js';
8
- export { loadFederationConfig } from './lib/core/load-federation-config.js';
7
+ export { normalizeFederationOptions } from './lib/core/normalize-options.js';
9
8
  export { writeFederationInfo } from './lib/core/write-federation-info.js';
10
9
  export { type BuildHelperParams, federationBuilder } from './lib/core/federation-builder.js';
11
10
  export * from './domain.js';
package/src/index.js CHANGED
@@ -1,11 +1,10 @@
1
1
  export { setBuildAdapter } from './lib/core/build-adapter.js';
2
2
  export { buildForFederation } from './lib/core/build-for-federation.js';
3
3
  export { rebuildForFederation } from './lib/core/rebuild-for-federation.js';
4
- export { normalizeFederationOptions } from './lib/core/normalize-federation-options.js';
5
4
  export { createFederationCache } from './lib/core/federation-cache.js';
6
5
  export { bundleExposedAndMappings } from './lib/core/bundle-exposed-and-mappings.js';
7
6
  export { getExternals } from './lib/core/get-externals.js';
8
- export { loadFederationConfig } from './lib/core/load-federation-config.js';
7
+ export { normalizeFederationOptions } from './lib/core/normalize-options.js';
9
8
  export { writeFederationInfo } from './lib/core/write-federation-info.js';
10
9
  export { federationBuilder } from './lib/core/federation-builder.js';
11
10
  export * from './domain.js';
package/src/internal.d.ts CHANGED
@@ -2,7 +2,7 @@ export * from './lib/utils/build-result-map.js';
2
2
  export { hashFile } from './lib/utils/hash-file.js';
3
3
  export * from './lib/utils/errors.js';
4
4
  export { logger, setLogLevel } from './lib/utils/logger.js';
5
- export type { MappedPath } from './lib/domain/utils/mapped-path.contract.js';
5
+ export type { PathToImport } from './lib/domain/utils/mapped-path.contract.js';
6
6
  export { RebuildQueue, type TrackResult } from './lib/utils/rebuild-queue.js';
7
7
  export { AbortedError } from './lib/utils/errors.js';
8
8
  export { createBuildResultMap, lookupInResultMap, popFromResultMap, } from './lib/utils/build-result-map.js';
@@ -10,3 +10,4 @@ export { writeImportMap } from './lib/core/write-import-map.js';
10
10
  export type { NormalizedExternalConfig, NormalizedSharedExternalsConfig, } from './lib/domain/config/external-config.contract.js';
11
11
  export type { NormalizedFederationConfig } from './lib/domain/config/federation-config.contract.js';
12
12
  export { getDefaultCachePath, getChecksum } from './lib/utils/cache-persistence.js';
13
+ export { isInSkipList, prepareSkipList } from './lib/config/default-skip-list.js';
package/src/internal.js CHANGED
@@ -7,3 +7,4 @@ export { AbortedError } from './lib/utils/errors.js';
7
7
  export { createBuildResultMap, lookupInResultMap, popFromResultMap, } from './lib/utils/build-result-map.js';
8
8
  export { writeImportMap } from './lib/core/write-import-map.js';
9
9
  export { getDefaultCachePath, getChecksum } from './lib/utils/cache-persistence.js';
10
+ export { isInSkipList, prepareSkipList } from './lib/config/default-skip-list.js';
@@ -1,26 +1,14 @@
1
1
  export const DEFAULT_SKIP_LIST = [
2
- '@softarc/native-federation-runtime',
3
2
  '@softarc/native-federation',
4
3
  '@softarc/native-federation-core',
5
4
  '@softarc/native-federation-node',
6
5
  '@softarc/native-federation-esbuild',
7
- '@angular-architects/native-federation',
8
- '@angular-architects/native-federation-runtime',
6
+ '@softarc/native-federation-runtime',
7
+ '@softarc/native-federation-orchestrator',
8
+ 'vanilla-native-federation',
9
9
  'es-module-shims',
10
- 'zone.js',
11
10
  'tslib/',
12
- '@angular/localize',
13
- '@angular/localize/init',
14
- '@angular/localize/tools',
15
- // '@angular/platform-server',
16
- // '@angular/platform-server/init',
17
- // '@angular/ssr',
18
- /\/schematics(\/|$)/,
19
- /^@nx\/angular/,
20
- pkg => pkg.startsWith('@angular/') && !!pkg.match(/\/testing(\/|$)/),
21
11
  pkg => pkg.startsWith('@types/'),
22
- 'express',
23
- // (pkg) => pkg.startsWith('@angular/common/locales'),
24
12
  ];
25
13
  export function prepareSkipList(skipList) {
26
14
  return {
@@ -0,0 +1,3 @@
1
+ import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
2
+ import type { UsedDependencies } from '../domain/utils/used-dependencies.contract.js';
3
+ export declare function removeUnusedDeps(usedDependencies: UsedDependencies, config: NormalizedFederationConfig): NormalizedFederationConfig;
@@ -0,0 +1,10 @@
1
+ export function removeUnusedDeps(usedDependencies, config) {
2
+ const filteredDependencies = Object.entries(config.shared)
3
+ .filter(([shared, meta]) => !!meta.includeSecondaries || usedDependencies.external.has(shared))
4
+ .reduce((acc, [shared, meta]) => ({ ...acc, [shared]: meta }), {});
5
+ return {
6
+ ...config,
7
+ shared: filteredDependencies,
8
+ sharedMappings: usedDependencies.internal,
9
+ };
10
+ }
@@ -1,6 +1,5 @@
1
1
  import { type SkipList } from '../domain/config/skip-list.contract.js';
2
2
  import type { ShareAllExternalsOptions, ShareExternalsOptions } from '../domain/config/external-config.contract.js';
3
- export declare const DEFAULT_SECONDARIES_SKIP_LIST: string[];
4
3
  export declare function findRootTsConfigJson(): string;
5
4
  export declare function shareAll(config: ShareAllExternalsOptions, opts?: {
6
5
  skipList?: SkipList;
@@ -5,9 +5,8 @@ import { DEFAULT_SKIP_LIST, isInSkipList, prepareSkipList } from './default-skip
5
5
  import { findDepPackageJson, getVersionMaps } from '../utils/package-info.js';
6
6
  import { getConfigContext } from './configuration-context.js';
7
7
  import { logger } from '../utils/logger.js';
8
- import { resolveWildcardKeys } from '../utils/resolve-wildcard-keys.js';
8
+ import { resolvePackageJsonExportsWildcard } from '../utils/resolve-wildcard-keys.js';
9
9
  let inferVersion = false;
10
- export const DEFAULT_SECONDARIES_SKIP_LIST = ['@angular/router/upgrade', '@angular/common/upgrade'];
11
10
  export function findRootTsConfigJson() {
12
11
  const packageJson = findPackageJson(cwd());
13
12
  const projectRoot = path.dirname(packageJson);
@@ -49,9 +48,6 @@ function lookupVersionInMap(key, versions) {
49
48
  else {
50
49
  key = parts[0];
51
50
  }
52
- if (key.toLowerCase() === '@angular-architects/module-federation-runtime') {
53
- key = '@angular-architects/module-federation';
54
- }
55
51
  if (!versions[key]) {
56
52
  return null;
57
53
  }
@@ -82,7 +78,7 @@ function findSecondaries(libPath, excludes, shareObject, preparedSkipList) {
82
78
  return acc;
83
79
  }
84
80
  function getSecondaries(includeSecondaries, libPath, key, shareObject, preparedSkipList) {
85
- let exclude = [...DEFAULT_SECONDARIES_SKIP_LIST];
81
+ let exclude = [];
86
82
  let resolveGlob = false;
87
83
  if (typeof includeSecondaries === 'object') {
88
84
  if (Array.isArray(includeSecondaries.skip)) {
@@ -166,7 +162,7 @@ function resolveGlobSecondaries(key, libPath, parent, secondaryName, entry, excl
166
162
  if (key.includes('*')) {
167
163
  if (!resolveGlob)
168
164
  return items;
169
- const expanded = resolveWildcardKeys(key, entry, libPath);
165
+ const expanded = resolvePackageJsonExportsWildcard(key, entry, libPath);
170
166
  items = expanded
171
167
  .map(e => ({
172
168
  key: path.join(parent, e.key),
@@ -221,10 +217,10 @@ export function shareAll(config, opts = {}) {
221
217
  // }
222
218
  const versionMaps = getVersionMaps(projectPath, projectPath);
223
219
  const sharedExternals = {};
224
- const preparedSkipList = prepareSkipList(opts.skipList ?? DEFAULT_SKIP_LIST);
220
+ const skipList = opts.skipList ?? DEFAULT_SKIP_LIST;
225
221
  for (const versions of versionMaps) {
226
222
  for (const key in versions) {
227
- if (isInSkipList(key, preparedSkipList)) {
223
+ if (isInSkipList(key, prepareSkipList(skipList))) {
228
224
  continue;
229
225
  }
230
226
  if (!!opts.overrides && Object.keys(opts.overrides).some(o => key.startsWith(o))) {
@@ -238,10 +234,8 @@ export function shareAll(config, opts = {}) {
238
234
  }
239
235
  }
240
236
  return {
241
- ...share(sharedExternals, opts.projectPath, opts.skipList ?? DEFAULT_SKIP_LIST),
242
- ...(!opts.overrides
243
- ? {}
244
- : share(opts.overrides, opts.projectPath, opts.skipList ?? DEFAULT_SKIP_LIST)),
237
+ ...share(sharedExternals, opts.projectPath, skipList),
238
+ ...(!opts.overrides ? {} : share(opts.overrides, opts.projectPath, skipList)),
245
239
  };
246
240
  }
247
241
  function inferProjectPath(projectPath) {
@@ -1,91 +1,70 @@
1
- import { getMappedPaths } from '../utils/mapped-paths.js';
1
+ import { getRawMappedPaths } from '../utils/mapped-paths.js';
2
2
  import { shareAll, findRootTsConfigJson } from './share-utils.js';
3
3
  import { isInSkipList, prepareSkipList } from './default-skip-list.js';
4
4
  import { logger } from '../utils/logger.js';
5
- import { DEFAULT_SERVER_DEPS_LIST } from '../core/default-server-deps-list.js';
6
5
  export function withNativeFederation(config) {
7
6
  const skip = prepareSkipList(config.skip ?? []);
7
+ const chunks = config.chunks ?? true;
8
8
  const normalized = {
9
+ $type: 'classic',
9
10
  name: config.name ?? '',
10
11
  exposes: config.exposes ?? {},
11
- shared: normalizeShared(config, skip),
12
- sharedMappings: normalizeSharedMappings(config, skip),
12
+ shared: normalizeShared(config, skip, chunks),
13
+ sharedMappings: removeSkippedMappings(config, skip),
14
+ chunks,
13
15
  skip,
14
16
  externals: config.externals ?? [],
15
17
  features: {
16
18
  mappingVersion: config.features?.mappingVersion ?? false,
17
- ignoreUnusedDeps: config.features?.ignoreUnusedDeps ?? false,
19
+ ignoreUnusedDeps: config.features?.ignoreUnusedDeps ?? true,
20
+ denseChunking: config.features?.denseChunking ?? false,
18
21
  },
19
22
  ...(config.shareScope && { shareScope: config.shareScope }),
20
23
  };
21
- // This is for being backwards compatible
22
- if (!normalized.features.ignoreUnusedDeps) {
23
- normalized.shared = filterShared(normalized.shared);
24
- }
25
24
  return normalized;
26
25
  }
27
- function filterShared(shared) {
28
- const keys = Object.keys(shared).filter(k => !k.startsWith('@angular/common/locales'));
29
- const filtered = keys.reduce((acc, curr) => ({
30
- ...acc,
31
- [curr]: shared[curr],
32
- }), {});
33
- return filtered;
34
- }
35
- function normalizeShared(config, skip) {
26
+ function normalizeShared(config, skip, chunks) {
36
27
  let result = {};
37
- const shared = config.shared;
38
- if (!shared) {
39
- result = shareAll({
28
+ const shared = config.shared ??
29
+ shareAll({
40
30
  singleton: true,
41
31
  strictVersion: true,
42
32
  requiredVersion: 'auto',
43
33
  platform: 'browser',
44
34
  });
45
- }
46
- else {
47
- result = Object.keys(shared).reduce((acc, cur) => {
48
- const key = cur.replace(/\\/g, '/');
49
- const sharedConfig = shared[cur];
50
- const normalizedConfig = {
51
- requiredVersion: sharedConfig.requiredVersion ?? 'auto',
52
- singleton: sharedConfig.singleton ?? false,
53
- strictVersion: sharedConfig.strictVersion ?? false,
54
- version: sharedConfig.version,
55
- includeSecondaries: sharedConfig.includeSecondaries,
56
- packageInfo: sharedConfig.packageInfo,
57
- platform: sharedConfig.platform ?? getDefaultPlatform(cur),
58
- build: sharedConfig.build ?? 'default',
59
- ...(sharedConfig.shareScope && { shareScope: sharedConfig.shareScope }),
60
- };
61
- return {
62
- ...acc,
63
- [key]: normalizedConfig,
64
- };
65
- }, {});
66
- }
35
+ result = Object.keys(shared).reduce((acc, cur) => {
36
+ const key = cur.replace(/\\/g, '/');
37
+ const sharedConfig = shared[cur];
38
+ if (!!sharedConfig.chunks && !sharedConfig.build && sharedConfig.chunks !== chunks) {
39
+ logger.warn(`External '${cur}' has explicit chunk settings, consider switching build type to { build: 'package' }.`);
40
+ sharedConfig.chunks = chunks;
41
+ }
42
+ const normalizedConfig = {
43
+ requiredVersion: sharedConfig.requiredVersion ?? 'auto',
44
+ singleton: sharedConfig.singleton ?? false,
45
+ strictVersion: sharedConfig.strictVersion ?? false,
46
+ version: sharedConfig.version,
47
+ chunks: sharedConfig.chunks ?? chunks,
48
+ includeSecondaries: sharedConfig.includeSecondaries,
49
+ packageInfo: sharedConfig.packageInfo,
50
+ platform: sharedConfig.platform ?? config.platform ?? 'browser',
51
+ build: sharedConfig.build ?? 'default',
52
+ ...(sharedConfig.shareScope && { shareScope: sharedConfig.shareScope }),
53
+ };
54
+ return {
55
+ ...acc,
56
+ [key]: normalizedConfig,
57
+ };
58
+ }, {});
67
59
  result = Object.keys(result)
68
60
  .filter(key => !isInSkipList(key, skip))
69
61
  .reduce((acc, cur) => ({ ...acc, [cur]: result[cur] }), {});
70
62
  return result;
71
63
  }
72
- function normalizeSharedMappings(config, skip) {
64
+ function removeSkippedMappings(config, skipList) {
73
65
  const rootTsConfigPath = findRootTsConfigJson();
74
- const paths = getMappedPaths({
75
- rootTsConfigPath,
76
- sharedMappings: config.sharedMappings,
77
- });
78
- const result = paths.filter(p => !isInSkipList(p.key, skip) && !p.key.includes('*'));
79
- if (paths.find(p => p.key.includes('*'))) {
80
- logger.warn('Sharing mapped paths with wildcards (*) not supported');
81
- }
82
- return result;
83
- }
84
- function getDefaultPlatform(cur) {
85
- if (DEFAULT_SERVER_DEPS_LIST.find(e => cur.startsWith(e))) {
86
- return 'node';
87
- }
88
- else {
89
- return 'browser';
90
- }
66
+ const paths = getRawMappedPaths(rootTsConfigPath, config.sharedMappings);
67
+ return Object.entries(paths)
68
+ .filter(([, _import]) => !isInSkipList(_import, skipList))
69
+ .reduce((acc, [_path, _import]) => ({ ...acc, [_path]: _import }), {});
91
70
  }
@@ -5,13 +5,13 @@ import { writeImportMap } from './write-import-map.js';
5
5
  import { logger } from '../utils/logger.js';
6
6
  import { normalizePackageName } from '../utils/normalize.js';
7
7
  import { AbortedError } from '../utils/errors.js';
8
- import { resolveProjectName } from '../utils/config-utils.js';
9
8
  import { addExternalsToCache } from './federation-cache.js';
10
9
  import path from 'path';
11
10
  export async function buildForFederation(config, fedOptions, externals, signal) {
12
11
  // 1. Setup
13
- fedOptions.federationCache.cachePath = path.join(fedOptions.federationCache.cachePath, resolveProjectName(config));
12
+ fedOptions.federationCache.cachePath = path.join(fedOptions.federationCache.cachePath, fedOptions.projectName);
14
13
  logger.info('Building federation artifacts');
14
+ logger.notice("Skip packages you don't want to share in your federation config");
15
15
  // 2. Externals
16
16
  if (fedOptions.federationCache.externals.length > 0) {
17
17
  logger.info('Checksum matched, re-using cached externals.');
@@ -19,37 +19,37 @@ export async function buildForFederation(config, fedOptions, externals, signal)
19
19
  if (fedOptions.federationCache.externals.length === 0) {
20
20
  const { sharedBrowser, sharedServer, separateBrowser, separateServer } = splitShared(config.shared);
21
21
  if (Object.keys(sharedBrowser).length > 0) {
22
- notifyBundling('browser-shared');
22
+ logger.info(`Bundling external npm packages with bundle type 'browser-shared'`);
23
23
  const start = process.hrtime();
24
- const sharedPackageInfoBrowser = await bundleShared(sharedBrowser, config, fedOptions, externals, { platform: 'browser', bundleName: 'browser-shared' });
25
- logger.measure(start, '[build artifacts] - To bundle all shared browser externals');
24
+ const sharedPackageInfoBrowser = await bundleShared(sharedBrowser, config, fedOptions, externals, { platform: 'browser', bundleName: 'browser-shared', chunks: config.chunks });
25
+ logger.measure(start, 'Step 2.1) Bundling all shared browser externals');
26
26
  addExternalsToCache(fedOptions.federationCache, sharedPackageInfoBrowser);
27
27
  if (signal?.aborted)
28
28
  throw new AbortedError('[buildForFederation] After shared-browser bundle');
29
29
  }
30
30
  if (Object.keys(sharedServer).length > 0) {
31
- notifyBundling('browser-shared');
31
+ logger.info(`Bundling external npm packages with bundle type 'server-shared'`);
32
32
  const start = process.hrtime();
33
- const sharedPackageInfoServer = await bundleShared(sharedServer, config, fedOptions, externals, { platform: 'node', bundleName: 'node-shared' });
34
- logger.measure(start, '[build artifacts] - To bundle all shared node externals');
33
+ const sharedPackageInfoServer = await bundleShared(sharedServer, config, fedOptions, externals, { platform: 'node', bundleName: 'node-shared', chunks: config.chunks });
34
+ logger.measure(start, 'Step 2.1) Bundling all shared node externals');
35
35
  addExternalsToCache(fedOptions.federationCache, sharedPackageInfoServer);
36
36
  if (signal?.aborted)
37
37
  throw new AbortedError('[buildForFederation] After shared-node bundle');
38
38
  }
39
39
  if (Object.keys(separateBrowser).length > 0) {
40
- notifyBundling('browser-shared');
40
+ logger.info(`Bundling external npm packages with bundle type 'browser-separate'`);
41
41
  const start = process.hrtime();
42
42
  const separatePackageInfoBrowser = await bundleSeparatePackages(separateBrowser, externals, config, fedOptions, { platform: 'browser' });
43
- logger.measure(start, '[build artifacts] - To bundle all separate browser externals');
43
+ logger.measure(start, 'Step 2.2) Bundling all separate browser external packages');
44
44
  addExternalsToCache(fedOptions.federationCache, separatePackageInfoBrowser);
45
45
  if (signal?.aborted)
46
46
  throw new AbortedError('[buildForFederation] After separate-browser bundle');
47
47
  }
48
48
  if (Object.keys(separateServer).length > 0) {
49
- notifyBundling('browser-shared');
49
+ logger.info(`Bundling external npm packages with bundle type 'node-separate'`);
50
50
  const start = process.hrtime();
51
51
  const separatePackageInfoServer = await bundleSeparatePackages(separateServer, externals, config, fedOptions, { platform: 'node' });
52
- logger.measure(start, '[build artifacts] - To bundle all separate node externals');
52
+ logger.measure(start, 'Step 2.2) Bundling all separate node external packages');
53
53
  addExternalsToCache(fedOptions.federationCache, separatePackageInfoServer);
54
54
  }
55
55
  if (signal?.aborted)
@@ -58,7 +58,7 @@ export async function buildForFederation(config, fedOptions, externals, signal)
58
58
  // 2. Shared mappings and exposed modules
59
59
  const start = process.hrtime();
60
60
  const artifactInfo = await bundleExposedAndMappings(config, fedOptions, externals, undefined, signal);
61
- logger.measure(start, '[build artifacts] - To bundle all mappings and exposed.');
61
+ logger.measure(start, 'Step 3) Bundling all internal libraries and exposed modules.');
62
62
  if (signal?.aborted)
63
63
  throw new AbortedError('[buildForFederation] After exposed-and-mappings bundle');
64
64
  const exposedInfo = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
@@ -103,13 +103,17 @@ async function bundleSeparatePackages(separateBrowser, externals, config, fedOpt
103
103
  for (const [key, shared] of Object.entries(separateBrowser)) {
104
104
  const packageName = shared.build === 'separate' ? key : inferPackageFromSecondary(key);
105
105
  if (!groupedByPackage[packageName]) {
106
- groupedByPackage[packageName] = {};
106
+ groupedByPackage[packageName] = {
107
+ chunks: shared.chunks,
108
+ entries: {},
109
+ };
107
110
  }
108
- groupedByPackage[packageName][key] = shared;
111
+ groupedByPackage[packageName].entries[key] = shared;
109
112
  }
110
- const bundlePromises = Object.entries(groupedByPackage).map(async ([packageName, sharedGroup]) => {
111
- return bundleShared(sharedGroup, config, fedOptions, externals.filter(e => !e.startsWith(packageName)), {
113
+ const bundlePromises = Object.entries(groupedByPackage).map(async ([packageName, packageConfig]) => {
114
+ return bundleShared(packageConfig.entries, config, fedOptions, externals.filter(e => !e.startsWith(packageName)), {
112
115
  platform: buildOptions.platform,
116
+ chunks: packageConfig.chunks,
113
117
  bundleName: `${buildOptions.platform}-${normalizePackageName(packageName)}`,
114
118
  });
115
119
  });
@@ -122,11 +126,6 @@ async function bundleSeparatePackages(separateBrowser, externals, config, fedOpt
122
126
  return { externals: [...acc.externals, ...r.externals], chunks };
123
127
  }, { externals: [] });
124
128
  }
125
- function notifyBundling(platform) {
126
- logger.info('Preparing shared npm packages for the platform ' + platform);
127
- logger.notice('This only needs to be done once, as results are cached');
128
- logger.notice("Skip packages you don't want to share in your federation config");
129
- }
130
129
  function splitShared(shared) {
131
130
  const sharedServer = {};
132
131
  const sharedBrowser = {};
@@ -10,11 +10,12 @@ export async function bundleExposedAndMappings(config, fedOptions, externals, mo
10
10
  if (signal?.aborted) {
11
11
  throw new AbortedError('[bundle-exposed-and-mappings] Aborted before bundling');
12
12
  }
13
- const shared = config.sharedMappings.map(sm => {
14
- const entryPoint = sm.path;
15
- const tmp = sm.key.replace(/[^A-Za-z0-9]/g, '_');
16
- const outFilePath = tmp + '.js';
17
- return { fileName: entryPoint, outName: outFilePath, key: sm.key };
13
+ const shared = Object.entries(config.sharedMappings).map(([entryPoint, mappedImport]) => {
14
+ return {
15
+ fileName: entryPoint,
16
+ outName: mappedImport.replace(/[^A-Za-z0-9]/g, '_') + '.js',
17
+ key: mappedImport,
18
+ };
18
19
  });
19
20
  const exposes = Object.entries(config.exposes).map(([key, entry]) => {
20
21
  const outFilePath = key + '.js';
@@ -25,7 +26,7 @@ export async function bundleExposedAndMappings(config, fedOptions, externals, mo
25
26
  let result;
26
27
  try {
27
28
  if (!modifiedFiles) {
28
- await getBuildAdapter().setup({
29
+ await getBuildAdapter().setup('mapping-or-exposed', {
29
30
  entryPoints,
30
31
  outdir: fedOptions.outputPath,
31
32
  tsConfigPath: fedOptions.tsConfig,
@@ -33,18 +34,16 @@ export async function bundleExposedAndMappings(config, fedOptions, externals, mo
33
34
  dev: !!fedOptions.dev,
34
35
  watch: fedOptions.watch,
35
36
  mappedPaths: config.sharedMappings,
36
- bundleName: 'mapping-or-exposed',
37
- chunks: (typeof fedOptions.chunks === 'boolean' && fedOptions.chunks) ||
38
- (typeof fedOptions.chunks === 'object' && !!fedOptions.chunks.enable),
37
+ chunks: config.chunks,
39
38
  hash,
40
39
  optimizedMappings: config.features.ignoreUnusedDeps,
41
- isNodeModules: false,
40
+ isMappingOrExposed: true,
42
41
  cache: fedOptions.federationCache,
43
42
  });
44
43
  }
45
44
  result = await getBuildAdapter().build('mapping-or-exposed', {
46
45
  signal,
47
- files: modifiedFiles,
46
+ modifiedFiles,
48
47
  });
49
48
  if (signal?.aborted) {
50
49
  throw new AbortedError('[bundle-exposed-and-mappings] Aborted after bundle');
@@ -98,7 +97,7 @@ export async function bundleExposedAndMappings(config, fedOptions, externals, mo
98
97
  .forEach(f => popFromResultMap(resultMap, f));
99
98
  // Process remaining chunks and lazy loaded internal modules
100
99
  let exportedChunks = undefined;
101
- if (typeof fedOptions.chunks === 'object' && fedOptions.chunks.dense === true) {
100
+ if (config.chunks && config.features.denseChunking) {
102
101
  for (const entryFile of entryFiles)
103
102
  rewriteChunkImports(entryFile);
104
103
  exportedChunks = {
@@ -125,18 +124,18 @@ export function describeExposed(config, options) {
125
124
  }
126
125
  export function describeSharedMappings(config, fedOptions) {
127
126
  const result = [];
128
- for (const m of config.sharedMappings) {
127
+ for (const [mappedPath, mappedImport] of Object.entries(config.sharedMappings)) {
129
128
  result.push({
130
- packageName: m.key,
129
+ packageName: mappedImport,
131
130
  outFileName: '',
132
131
  requiredVersion: '',
133
132
  singleton: true,
134
133
  strictVersion: false,
135
- version: config.features.mappingVersion ? getMappingVersion(m.path) : '',
134
+ version: config.features.mappingVersion ? getMappingVersion(mappedPath) : '',
136
135
  dev: !fedOptions.dev
137
136
  ? undefined
138
137
  : {
139
- entryPoint: normalize(path.normalize(m.path)),
138
+ entryPoint: normalize(path.normalize(mappedPath)),
140
139
  },
141
140
  });
142
141
  }
@@ -5,6 +5,7 @@ import type { NormalizedExternalConfig } from '../domain/config/external-config.
5
5
  export declare function bundleShared(sharedBundles: Record<string, NormalizedExternalConfig>, config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], buildOptions: {
6
6
  platform: 'browser' | 'node';
7
7
  bundleName: string;
8
+ chunks: boolean;
8
9
  }): Promise<{
9
10
  externals: SharedInfo[];
10
11
  chunks?: Record<string, string[]>;
@@ -53,18 +53,16 @@ export async function bundleShared(sharedBundles, config, fedOptions, externals,
53
53
  const additionalExternals = useDefaultExternalList ? DEFAULT_EXTERNAL_LIST : [];
54
54
  let bundleResult = null;
55
55
  try {
56
- await getBuildAdapter().setup({
56
+ await getBuildAdapter().setup(buildOptions.bundleName, {
57
57
  entryPoints,
58
58
  tsConfigPath: fedOptions.tsConfig,
59
59
  external: [...additionalExternals, ...externals],
60
60
  outdir: fedOptions.federationCache.cachePath,
61
61
  mappedPaths: config.sharedMappings,
62
62
  dev: fedOptions.dev,
63
- bundleName: buildOptions.bundleName,
64
- isNodeModules: true,
63
+ isMappingOrExposed: false,
65
64
  hash: false,
66
- chunks: (typeof fedOptions.chunks === 'boolean' && fedOptions.chunks) ||
67
- (typeof fedOptions.chunks === 'object' && !!fedOptions.chunks.enable),
65
+ chunks: buildOptions.chunks,
68
66
  platform: buildOptions.platform,
69
67
  optimizedMappings: config.features.ignoreUnusedDeps,
70
68
  cache: fedOptions.federationCache,
@@ -102,11 +100,13 @@ export async function bundleShared(sharedBundles, config, fedOptions, externals,
102
100
  * Chunking
103
101
  */
104
102
  let exportedChunks = undefined;
105
- if (typeof fedOptions.chunks === 'object' && fedOptions.chunks.dense === true) {
103
+ if (buildOptions.chunks && config.features.denseChunking) {
106
104
  result.forEach(external => {
107
105
  external.bundle = buildOptions.bundleName;
108
106
  });
109
- exportedChunks = { [buildOptions.bundleName]: getChunkFileNames(chunks) };
107
+ if (chunks.length > 0) {
108
+ exportedChunks = { [buildOptions.bundleName]: getChunkFileNames(chunks) };
109
+ }
110
110
  }
111
111
  else {
112
112
  addChunksToResult(chunks, result);
@@ -115,6 +115,7 @@ export async function bundleShared(sharedBundles, config, fedOptions, externals,
115
115
  checksum,
116
116
  externals: result,
117
117
  files: bundleResult.map(r => r.fileName.split(path.sep).pop() ?? r.fileName),
118
+ chunks: exportedChunks,
118
119
  });
119
120
  bundleCache.copyFiles(path.join(fedOptions.workspaceRoot, fedOptions.outputPath));
120
121
  return { externals: result, chunks: exportedChunks };
@@ -7,10 +7,15 @@ export interface BuildHelperParams {
7
7
  adapter: NFBuildAdapter;
8
8
  }
9
9
  declare function init(params: BuildHelperParams): Promise<void>;
10
- declare function build(signal?: AbortSignal): Promise<void>;
10
+ declare function build(opts?: {
11
+ modifiedFiles?: string[];
12
+ signal?: AbortSignal;
13
+ }): Promise<void>;
14
+ declare function close(): Promise<void>;
11
15
  export declare const federationBuilder: {
12
16
  init: typeof init;
13
17
  build: typeof build;
18
+ close: typeof close;
14
19
  readonly federationInfo: FederationInfo;
15
20
  readonly externals: string[];
16
21
  readonly config: NormalizedFederationConfig;
@@ -1,28 +1,38 @@
1
1
  import { getConfigContext, usePackageJson, useWorkspace } from '../config/configuration-context.js';
2
- import { setBuildAdapter } from './build-adapter.js';
2
+ import { getBuildAdapter, setBuildAdapter } from './build-adapter.js';
3
3
  import { buildForFederation } from './build-for-federation.js';
4
4
  import { getExternals } from './get-externals.js';
5
- import { loadFederationConfig } from './load-federation-config.js';
6
- import { normalizeFederationOptions } from './normalize-federation-options.js';
5
+ import { normalizeFederationOptions } from './normalize-options.js';
6
+ import { rebuildForFederation } from './rebuild-for-federation.js';
7
7
  let externals = [];
8
8
  let config;
9
- let fedOptions;
9
+ let options;
10
10
  let fedInfo;
11
11
  async function init(params) {
12
12
  setBuildAdapter(params.adapter);
13
- fedOptions = normalizeFederationOptions(params.options);
14
13
  useWorkspace(params.options.workspaceRoot);
15
14
  usePackageJson(params.options.packageJson);
16
- config = await loadFederationConfig(fedOptions);
17
15
  params.options.workspaceRoot = getConfigContext().workspaceRoot ?? params.options.workspaceRoot;
16
+ const normalized = await normalizeFederationOptions(params.options);
17
+ options = normalized.options;
18
+ config = normalized.config;
18
19
  externals = getExternals(config);
19
20
  }
20
- async function build(signal) {
21
- fedInfo = await buildForFederation(config, fedOptions, externals, signal);
21
+ async function build(opts = {}) {
22
+ if (!fedInfo) {
23
+ fedInfo = await buildForFederation(config, options, externals, opts.signal);
24
+ }
25
+ else {
26
+ fedInfo = await rebuildForFederation(config, options, externals, opts.modifiedFiles ?? [], opts.signal);
27
+ }
28
+ }
29
+ async function close() {
30
+ return getBuildAdapter().dispose();
22
31
  }
23
32
  export const federationBuilder = {
24
33
  init,
25
34
  build,
35
+ close,
26
36
  get federationInfo() {
27
37
  return fedInfo;
28
38
  },
@@ -1,6 +1,6 @@
1
1
  export function getExternals(config) {
2
2
  const shared = Object.keys(config.shared);
3
- const sharedMappings = config.sharedMappings.map(m => m.key);
3
+ const sharedMappings = Object.values(config.sharedMappings);
4
4
  const externals = [...shared, ...sharedMappings, ...config.externals];
5
5
  return externals;
6
6
  }