@softarc/native-federation 4.0.0-RC5 → 4.0.0-RC6

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 (32) hide show
  1. package/package.json +1 -1
  2. package/src/index.d.ts +3 -0
  3. package/src/index.js +3 -0
  4. package/src/internal.d.ts +1 -0
  5. package/src/internal.js +1 -0
  6. package/src/lib/core/build-adapter.js +5 -5
  7. package/src/lib/core/build-for-federation.d.ts +2 -4
  8. package/src/lib/core/build-for-federation.js +31 -42
  9. package/src/lib/core/bundle-exposed-and-mappings.d.ts +4 -4
  10. package/src/lib/core/bundle-exposed-and-mappings.js +22 -15
  11. package/src/lib/core/bundle-shared.d.ts +3 -3
  12. package/src/lib/core/bundle-shared.js +15 -11
  13. package/src/lib/core/default-external-list.js +1 -4
  14. package/src/lib/core/federation-builder.d.ts +1 -1
  15. package/src/lib/core/federation-builder.js +5 -4
  16. package/src/lib/core/federation-cache.d.ts +8 -0
  17. package/src/lib/core/federation-cache.js +11 -0
  18. package/src/lib/core/normalize-federation-options.d.ts +4 -0
  19. package/src/lib/core/normalize-federation-options.js +10 -0
  20. package/src/lib/core/rebuild-for-federation.d.ts +4 -0
  21. package/src/lib/core/rebuild-for-federation.js +37 -0
  22. package/src/lib/domain/core/build-adapter.contract.d.ts +14 -8
  23. package/src/lib/domain/core/federation-cache.contract.d.ts +7 -0
  24. package/src/lib/domain/core/federation-options.contract.d.ts +4 -0
  25. package/src/lib/domain/core/index.d.ts +3 -3
  26. package/src/lib/utils/{bundle-caching.d.ts → cache-persistence.d.ts} +1 -1
  27. package/src/lib/utils/{bundle-caching.js → cache-persistence.js} +2 -2
  28. package/src/lib/utils/resolve-glob.js +4 -4
  29. package/src/lib/domain/core/build-params.contract.d.ts +0 -5
  30. package/src/lib/utils/build-utils.d.ts +0 -2
  31. package/src/lib/utils/build-utils.js +0 -5
  32. /package/src/lib/domain/core/{build-params.contract.js → federation-cache.contract.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softarc/native-federation",
3
- "version": "4.0.0-RC5",
3
+ "version": "4.0.0-RC6",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "dependencies": {
package/src/index.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  export { setBuildAdapter } from './lib/core/build-adapter.js';
2
2
  export { buildForFederation } from './lib/core/build-for-federation.js';
3
+ export { rebuildForFederation } from './lib/core/rebuild-for-federation.js';
4
+ export { normalizeFederationOptions } from './lib/core/normalize-federation-options.js';
5
+ export { createFederationCache } from './lib/core/federation-cache.js';
3
6
  export { bundleExposedAndMappings } from './lib/core/bundle-exposed-and-mappings.js';
4
7
  export { getExternals } from './lib/core/get-externals.js';
5
8
  export { loadFederationConfig } from './lib/core/load-federation-config.js';
package/src/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  export { setBuildAdapter } from './lib/core/build-adapter.js';
2
2
  export { buildForFederation } from './lib/core/build-for-federation.js';
3
+ export { rebuildForFederation } from './lib/core/rebuild-for-federation.js';
4
+ export { normalizeFederationOptions } from './lib/core/normalize-federation-options.js';
5
+ export { createFederationCache } from './lib/core/federation-cache.js';
3
6
  export { bundleExposedAndMappings } from './lib/core/bundle-exposed-and-mappings.js';
4
7
  export { getExternals } from './lib/core/get-externals.js';
5
8
  export { loadFederationConfig } from './lib/core/load-federation-config.js';
package/src/internal.d.ts CHANGED
@@ -9,3 +9,4 @@ export { createBuildResultMap, lookupInResultMap, popFromResultMap, } from './li
9
9
  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
+ export { getDefaultCachePath, getChecksum } from './lib/utils/cache-persistence.js';
package/src/internal.js CHANGED
@@ -6,3 +6,4 @@ export { RebuildQueue } from './lib/utils/rebuild-queue.js';
6
6
  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
+ export { getDefaultCachePath, getChecksum } from './lib/utils/cache-persistence.js';
@@ -1,12 +1,12 @@
1
1
  import { logger } from '../utils/logger.js';
2
- let _buildAdapter = async () => {
3
- // TODO: add logger
4
- logger.error('NF is missing a build adapter!');
5
- return [];
6
- };
2
+ let _buildAdapter = null;
7
3
  export function setBuildAdapter(buildAdapter) {
8
4
  _buildAdapter = buildAdapter;
9
5
  }
10
6
  export function getBuildAdapter() {
7
+ if (!_buildAdapter) {
8
+ logger.error('Please set a BuildAdapter!');
9
+ throw new Error('BuildAdapter not set');
10
+ }
11
11
  return _buildAdapter;
12
12
  }
@@ -1,6 +1,4 @@
1
1
  import type { FederationInfo } from '../domain/core/federation-info.contract.js';
2
- import type { FederationOptions } from '../domain/core/federation-options.contract.js';
2
+ import type { NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
3
3
  import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
4
- import type { BuildParams } from '../domain/core/build-params.contract.js';
5
- export declare const defaultBuildParams: BuildParams;
6
- export declare function buildForFederation(config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], buildParams?: BuildParams): Promise<FederationInfo>;
4
+ export declare function buildForFederation(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], signal?: AbortSignal): Promise<FederationInfo>;
@@ -3,66 +3,60 @@ import { bundleShared } from './bundle-shared.js';
3
3
  import { writeFederationInfo } from './write-federation-info.js';
4
4
  import { writeImportMap } from './write-import-map.js';
5
5
  import { logger } from '../utils/logger.js';
6
- import { getCachePath } from './../utils/bundle-caching.js';
7
6
  import { normalizePackageName } from '../utils/normalize.js';
8
7
  import { AbortedError } from '../utils/errors.js';
9
8
  import { resolveProjectName } from '../utils/config-utils.js';
10
- export const defaultBuildParams = {
11
- skipMappingsAndExposed: false,
12
- skipShared: false,
13
- };
14
- const sharedCache = { externals: [] };
15
- export async function buildForFederation(config, fedOptions, externals, buildParams = defaultBuildParams) {
16
- const signal = buildParams.signal;
17
- let artifactInfo;
18
- if (!buildParams.skipMappingsAndExposed) {
19
- const start = process.hrtime();
20
- artifactInfo = await bundleExposedAndMappings(config, fedOptions, externals, signal);
21
- logger.measure(start, '[build artifacts] - To bundle all mappings and exposed.');
22
- if (signal?.aborted)
23
- throw new AbortedError('[buildForFederation] After exposed-and-mappings bundle');
24
- }
9
+ import { addExternalsToCache } from './federation-cache.js';
10
+ import path from 'path';
11
+ export async function buildForFederation(config, fedOptions, externals, signal) {
12
+ // 1. Caching
13
+ fedOptions.federationCache.cachePath = path.join(fedOptions.federationCache.cachePath, resolveProjectName(config));
14
+ const start = process.hrtime();
15
+ // 2. Shared mappings and exposed modules
16
+ const artifactInfo = await bundleExposedAndMappings(config, fedOptions, externals, undefined, signal);
17
+ logger.measure(start, '[build artifacts] - To bundle all mappings and exposed.');
18
+ if (signal?.aborted)
19
+ throw new AbortedError('[buildForFederation] After exposed-and-mappings bundle');
25
20
  const exposedInfo = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
26
- const cacheProjectFolder = resolveProjectName(config);
27
- const pathToCache = getCachePath(fedOptions.workspaceRoot, cacheProjectFolder);
28
- if (!buildParams.skipShared && sharedCache.externals.length > 0) {
21
+ // 3. Externals
22
+ if (fedOptions.federationCache.externals.length > 0) {
29
23
  logger.info('Checksum matched, re-using cached externals.');
30
24
  }
31
- if (!buildParams.skipShared && sharedCache.externals.length === 0) {
25
+ if (fedOptions.federationCache.externals.length === 0) {
32
26
  const { sharedBrowser, sharedServer, separateBrowser, separateServer } = splitShared(config.shared);
33
27
  if (Object.keys(sharedBrowser).length > 0) {
34
28
  notifyBundling('browser-shared');
35
29
  const start = process.hrtime();
36
- const sharedPackageInfoBrowser = await bundleShared(sharedBrowser, config, fedOptions, externals, 'browser', { pathToCache, bundleName: 'browser-shared' });
30
+ const sharedPackageInfoBrowser = await bundleShared(sharedBrowser, config, fedOptions, externals, { platform: 'browser', bundleName: 'browser-shared' });
37
31
  logger.measure(start, '[build artifacts] - To bundle all shared browser externals');
38
- addToCache(sharedPackageInfoBrowser);
32
+ addExternalsToCache(fedOptions.federationCache, sharedPackageInfoBrowser);
39
33
  if (signal?.aborted)
40
34
  throw new AbortedError('[buildForFederation] After shared-browser bundle');
41
35
  }
42
36
  if (Object.keys(sharedServer).length > 0) {
43
37
  notifyBundling('browser-shared');
44
38
  const start = process.hrtime();
45
- const sharedPackageInfoServer = await bundleShared(sharedServer, config, fedOptions, externals, 'node', { pathToCache, bundleName: 'node-shared' });
39
+ const sharedPackageInfoServer = await bundleShared(sharedServer, config, fedOptions, externals, { platform: 'node', bundleName: 'node-shared' });
46
40
  logger.measure(start, '[build artifacts] - To bundle all shared node externals');
47
- addToCache(sharedPackageInfoServer);
41
+ addExternalsToCache(fedOptions.federationCache, sharedPackageInfoServer);
48
42
  if (signal?.aborted)
49
43
  throw new AbortedError('[buildForFederation] After shared-node bundle');
50
44
  }
51
45
  if (Object.keys(separateBrowser).length > 0) {
52
46
  notifyBundling('browser-shared');
53
47
  const start = process.hrtime();
54
- const separatePackageInfoBrowser = await bundleSeparatePackages(separateBrowser, externals, config, fedOptions, 'browser', pathToCache);
48
+ const separatePackageInfoBrowser = await bundleSeparatePackages(separateBrowser, externals, config, fedOptions, { platform: 'browser' });
55
49
  logger.measure(start, '[build artifacts] - To bundle all separate browser externals');
56
- addToCache(separatePackageInfoBrowser);
50
+ addExternalsToCache(fedOptions.federationCache, separatePackageInfoBrowser);
57
51
  if (signal?.aborted)
58
52
  throw new AbortedError('[buildForFederation] After separate-browser bundle');
59
53
  }
60
54
  if (Object.keys(separateServer).length > 0) {
61
55
  notifyBundling('browser-shared');
62
56
  const start = process.hrtime();
63
- const separatePackageInfoServer = await bundleSeparatePackages(separateServer, externals, config, fedOptions, 'node', pathToCache);
57
+ const separatePackageInfoServer = await bundleSeparatePackages(separateServer, externals, config, fedOptions, { platform: 'node' });
64
58
  logger.measure(start, '[build artifacts] - To bundle all separate node externals');
65
- addToCache(separatePackageInfoServer);
59
+ addExternalsToCache(fedOptions.federationCache, separatePackageInfoServer);
66
60
  }
67
61
  if (signal?.aborted)
68
62
  throw new AbortedError('[buildForFederation] After separate-node bundle');
@@ -70,7 +64,7 @@ export async function buildForFederation(config, fedOptions, externals, buildPar
70
64
  const sharedMappingInfo = !artifactInfo
71
65
  ? describeSharedMappings(config, fedOptions)
72
66
  : artifactInfo.mappings;
73
- const sharedExternals = [...sharedCache.externals, ...sharedMappingInfo];
67
+ const sharedExternals = [...fedOptions.federationCache.externals, ...sharedMappingInfo];
74
68
  if (config?.shareScope) {
75
69
  Object.values(sharedExternals).forEach(external => {
76
70
  if (!external.shareScope)
@@ -86,24 +80,16 @@ export async function buildForFederation(config, fedOptions, externals, buildPar
86
80
  exposes: exposedInfo,
87
81
  buildNotificationsEndpoint,
88
82
  };
89
- if (sharedCache.chunks) {
90
- federationInfo.chunks = sharedCache.chunks;
83
+ if (fedOptions.federationCache.chunks) {
84
+ federationInfo.chunks = fedOptions.federationCache.chunks;
91
85
  }
92
86
  if (artifactInfo?.chunks) {
93
87
  federationInfo.chunks = { ...(federationInfo.chunks ?? {}), ...artifactInfo?.chunks };
94
88
  }
95
89
  writeFederationInfo(federationInfo, fedOptions);
96
- writeImportMap(sharedCache, fedOptions);
90
+ writeImportMap(fedOptions.federationCache, fedOptions);
97
91
  return federationInfo;
98
92
  }
99
- function addToCache({ externals, chunks }) {
100
- sharedCache.externals.push(...externals);
101
- if (chunks) {
102
- if (!sharedCache.chunks)
103
- sharedCache.chunks = {};
104
- sharedCache.chunks = { ...sharedCache.chunks, ...chunks };
105
- }
106
- }
107
93
  function inferPackageFromSecondary(secondary) {
108
94
  const parts = secondary.split('/');
109
95
  if (secondary.startsWith('@') && parts.length >= 2) {
@@ -111,7 +97,7 @@ function inferPackageFromSecondary(secondary) {
111
97
  }
112
98
  return parts[0];
113
99
  }
114
- async function bundleSeparatePackages(separateBrowser, externals, config, fedOptions, platform, pathToCache) {
100
+ async function bundleSeparatePackages(separateBrowser, externals, config, fedOptions, buildOptions) {
115
101
  const groupedByPackage = {};
116
102
  for (const [key, shared] of Object.entries(separateBrowser)) {
117
103
  const packageName = shared.build === 'separate' ? key : inferPackageFromSecondary(key);
@@ -121,7 +107,10 @@ async function bundleSeparatePackages(separateBrowser, externals, config, fedOpt
121
107
  groupedByPackage[packageName][key] = shared;
122
108
  }
123
109
  const bundlePromises = Object.entries(groupedByPackage).map(async ([packageName, sharedGroup]) => {
124
- return bundleShared(sharedGroup, config, fedOptions, externals.filter(e => !e.startsWith(packageName)), platform, { pathToCache, bundleName: `${platform}-${normalizePackageName(packageName)}` });
110
+ return bundleShared(sharedGroup, config, fedOptions, externals.filter(e => !e.startsWith(packageName)), {
111
+ platform: buildOptions.platform,
112
+ bundleName: `${buildOptions.platform}-${normalizePackageName(packageName)}`,
113
+ });
125
114
  });
126
115
  const buildResults = await Promise.all(bundlePromises);
127
116
  return buildResults.reduce((acc, r) => {
@@ -1,6 +1,6 @@
1
1
  import type { ArtifactInfo, ExposesInfo, SharedInfo } from '../domain/core/federation-info.contract.js';
2
2
  import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
3
- import { type FederationOptions } from '../domain/core/federation-options.contract.js';
4
- export declare function bundleExposedAndMappings(config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], signal?: AbortSignal): Promise<ArtifactInfo>;
5
- export declare function describeExposed(config: NormalizedFederationConfig, options: FederationOptions): Array<ExposesInfo>;
6
- export declare function describeSharedMappings(config: NormalizedFederationConfig, fedOptions: FederationOptions): Array<SharedInfo>;
3
+ import { type NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
4
+ export declare function bundleExposedAndMappings(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], modifiedFiles?: string[], signal?: AbortSignal): Promise<ArtifactInfo>;
5
+ export declare function describeExposed(config: NormalizedFederationConfig, options: NormalizedFederationOptions): Array<ExposesInfo>;
6
+ export declare function describeSharedMappings(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions): Array<SharedInfo>;
@@ -1,12 +1,12 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { createBuildResultMap, popFromResultMap } from '../utils/build-result-map.js';
4
- import { bundle } from '../utils/build-utils.js';
5
4
  import { logger } from '../utils/logger.js';
6
5
  import { normalize } from '../utils/normalize.js';
7
6
  import { AbortedError } from '../utils/errors.js';
8
7
  import { rewriteChunkImports } from '../utils/rewrite-chunk-imports.js';
9
- export async function bundleExposedAndMappings(config, fedOptions, externals, signal) {
8
+ import { getBuildAdapter } from './build-adapter.js';
9
+ export async function bundleExposedAndMappings(config, fedOptions, externals, modifiedFiles, signal) {
10
10
  if (signal?.aborted) {
11
11
  throw new AbortedError('[bundle-exposed-and-mappings] Aborted before bundling');
12
12
  }
@@ -25,20 +25,27 @@ export async function bundleExposedAndMappings(config, fedOptions, externals, si
25
25
  logger.info('Building federation artifacts');
26
26
  let result;
27
27
  try {
28
- result = await bundle({
29
- entryPoints,
30
- outdir: fedOptions.outputPath,
31
- tsConfigPath: fedOptions.tsConfig,
32
- external: externals,
33
- dev: !!fedOptions.dev,
34
- watch: fedOptions.watch,
35
- mappedPaths: config.sharedMappings,
36
- kind: 'mapping-or-exposed',
37
- chunks: (typeof fedOptions.chunks === 'boolean' && fedOptions.chunks) ||
38
- (typeof fedOptions.chunks === 'object' && !!fedOptions.chunks.enable),
39
- hash,
40
- optimizedMappings: config.features.ignoreUnusedDeps,
28
+ if (!modifiedFiles) {
29
+ await getBuildAdapter().setup({
30
+ entryPoints,
31
+ outdir: fedOptions.outputPath,
32
+ tsConfigPath: fedOptions.tsConfig,
33
+ external: externals,
34
+ dev: !!fedOptions.dev,
35
+ watch: fedOptions.watch,
36
+ mappedPaths: config.sharedMappings,
37
+ bundleName: 'mapping-or-exposed',
38
+ chunks: (typeof fedOptions.chunks === 'boolean' && fedOptions.chunks) ||
39
+ (typeof fedOptions.chunks === 'object' && !!fedOptions.chunks.enable),
40
+ hash,
41
+ optimizedMappings: config.features.ignoreUnusedDeps,
42
+ isNodeModules: false,
43
+ cache: fedOptions.federationCache,
44
+ });
45
+ }
46
+ result = await getBuildAdapter().build('mapping-or-exposed', {
41
47
  signal,
48
+ files: modifiedFiles,
42
49
  });
43
50
  if (signal?.aborted) {
44
51
  throw new AbortedError('[bundle-exposed-and-mappings] Aborted after bundle');
@@ -1,9 +1,9 @@
1
1
  import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
2
2
  import type { SharedInfo } from '../domain/core/federation-info.contract.js';
3
- import { type FederationOptions } from '../domain/core/federation-options.contract.js';
3
+ import { type NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
4
4
  import type { NormalizedExternalConfig } from '../domain/config/external-config.contract.js';
5
- export declare function bundleShared(sharedBundles: Record<string, NormalizedExternalConfig>, config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], platform: "browser" | "node" | undefined, buildOptions: {
6
- pathToCache: string;
5
+ export declare function bundleShared(sharedBundles: Record<string, NormalizedExternalConfig>, config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], buildOptions: {
6
+ platform: 'browser' | 'node';
7
7
  bundleName: string;
8
8
  }): Promise<{
9
9
  externals: SharedInfo[];
@@ -1,20 +1,20 @@
1
1
  import * as path from 'path';
2
2
  import * as fs from 'fs';
3
- import { bundle } from '../utils/build-utils.js';
4
3
  import { getPackageInfo } from '../utils/package-info.js';
5
4
  import { logger } from '../utils/logger.js';
6
5
  import crypto from 'crypto';
7
6
  import { DEFAULT_EXTERNAL_LIST } from './default-external-list.js';
8
7
  import { isSourceFile, rewriteChunkImports } from '../utils/rewrite-chunk-imports.js';
9
8
  import { toChunkImport } from '../domain/core/chunk.js';
10
- import { cacheEntry, getChecksum, getFilename } from './../utils/bundle-caching.js';
9
+ import { cacheEntry, getChecksum, getFilename } from '../utils/cache-persistence.js';
11
10
  import { fileURLToPath } from 'url';
12
- export async function bundleShared(sharedBundles, config, fedOptions, externals, platform = 'browser', buildOptions) {
11
+ import { getBuildAdapter } from './build-adapter.js';
12
+ export async function bundleShared(sharedBundles, config, fedOptions, externals, buildOptions) {
13
13
  const checksum = getChecksum(sharedBundles, fedOptions.dev ? '1' : '0');
14
14
  const folder = fedOptions.packageJson
15
15
  ? path.dirname(fedOptions.packageJson)
16
16
  : fedOptions.workspaceRoot;
17
- const bundleCache = cacheEntry(buildOptions.pathToCache, getFilename(buildOptions.bundleName, fedOptions.dev));
17
+ const bundleCache = cacheEntry(fedOptions.federationCache.cachePath, getFilename(buildOptions.bundleName, fedOptions.dev));
18
18
  if (fedOptions?.cacheExternalArtifacts) {
19
19
  const cacheMetadata = bundleCache.getMetadata(checksum);
20
20
  if (cacheMetadata) {
@@ -46,29 +46,33 @@ export async function bundleShared(sharedBundles, config, fedOptions, externals,
46
46
  });
47
47
  const fullOutputPath = path.join(fedOptions.workspaceRoot, fedOptions.outputPath);
48
48
  const expectedResults = allEntryPoints.map(ep => path.join(fullOutputPath, ep.outName));
49
- const entryPoints = allEntryPoints.filter(ep => !fs.existsSync(path.join(buildOptions.pathToCache, ep.outName)));
49
+ const entryPoints = allEntryPoints.filter(ep => !fs.existsSync(path.join(fedOptions.federationCache.cachePath, ep.outName)));
50
50
  // If we build for the browser and don't remote unused deps from the shared config,
51
51
  // we need to exclude typical node libs to avoid compilation issues
52
- const useDefaultExternalList = platform === 'browser' && !config.features.ignoreUnusedDeps;
52
+ const useDefaultExternalList = buildOptions.platform === 'browser' && !config.features.ignoreUnusedDeps;
53
53
  const additionalExternals = useDefaultExternalList ? DEFAULT_EXTERNAL_LIST : [];
54
54
  let bundleResult = null;
55
55
  try {
56
- bundleResult = await bundle({
56
+ await getBuildAdapter().setup({
57
57
  entryPoints,
58
58
  tsConfigPath: fedOptions.tsConfig,
59
59
  external: [...additionalExternals, ...externals],
60
- outdir: buildOptions.pathToCache,
60
+ outdir: fedOptions.federationCache.cachePath,
61
61
  mappedPaths: config.sharedMappings,
62
62
  dev: fedOptions.dev,
63
- kind: 'shared-package',
63
+ bundleName: buildOptions.bundleName,
64
+ isNodeModules: true,
64
65
  hash: false,
65
66
  chunks: (typeof fedOptions.chunks === 'boolean' && fedOptions.chunks) ||
66
67
  (typeof fedOptions.chunks === 'object' && !!fedOptions.chunks.enable),
67
- platform,
68
+ platform: buildOptions.platform,
68
69
  optimizedMappings: config.features.ignoreUnusedDeps,
70
+ cache: fedOptions.federationCache,
69
71
  });
72
+ bundleResult = await getBuildAdapter().build(buildOptions.bundleName);
73
+ await getBuildAdapter().dispose(buildOptions.bundleName);
70
74
  const cachedFiles = bundleResult.map(br => path.basename(br.fileName));
71
- rewriteImports(cachedFiles, buildOptions.pathToCache);
75
+ rewriteImports(cachedFiles, fedOptions.federationCache.cachePath);
72
76
  }
73
77
  catch (e) {
74
78
  logger.error('Error bundling shared npm package ');
@@ -26,7 +26,4 @@ const NODE_PACKAGES = [
26
26
  'vm',
27
27
  'zlib',
28
28
  ];
29
- export const DEFAULT_EXTERNAL_LIST = NODE_PACKAGES.flatMap((p) => [
30
- p,
31
- 'node:' + p,
32
- ]);
29
+ export const DEFAULT_EXTERNAL_LIST = NODE_PACKAGES.flatMap(p => [p, 'node:' + p]);
@@ -7,7 +7,7 @@ export interface BuildHelperParams {
7
7
  adapter: NFBuildAdapter;
8
8
  }
9
9
  declare function init(params: BuildHelperParams): Promise<void>;
10
- declare function build(buildParams?: import("../../domain.js").BuildParams): Promise<void>;
10
+ declare function build(signal?: AbortSignal): Promise<void>;
11
11
  export declare const federationBuilder: {
12
12
  init: typeof init;
13
13
  build: typeof build;
@@ -1,23 +1,24 @@
1
1
  import { getConfigContext, usePackageJson, useWorkspace } from '../config/configuration-context.js';
2
2
  import { setBuildAdapter } from './build-adapter.js';
3
- import { buildForFederation, defaultBuildParams } from './build-for-federation.js';
3
+ import { buildForFederation } from './build-for-federation.js';
4
4
  import { getExternals } from './get-externals.js';
5
5
  import { loadFederationConfig } from './load-federation-config.js';
6
+ import { normalizeFederationOptions } from './normalize-federation-options.js';
6
7
  let externals = [];
7
8
  let config;
8
9
  let fedOptions;
9
10
  let fedInfo;
10
11
  async function init(params) {
11
12
  setBuildAdapter(params.adapter);
12
- fedOptions = params.options;
13
+ fedOptions = normalizeFederationOptions(params.options);
13
14
  useWorkspace(params.options.workspaceRoot);
14
15
  usePackageJson(params.options.packageJson);
15
16
  config = await loadFederationConfig(fedOptions);
16
17
  params.options.workspaceRoot = getConfigContext().workspaceRoot ?? params.options.workspaceRoot;
17
18
  externals = getExternals(config);
18
19
  }
19
- async function build(buildParams = defaultBuildParams) {
20
- fedInfo = await buildForFederation(config, fedOptions, externals, buildParams);
20
+ async function build(signal) {
21
+ fedInfo = await buildForFederation(config, fedOptions, externals, signal);
21
22
  }
22
23
  export const federationBuilder = {
23
24
  init,
@@ -0,0 +1,8 @@
1
+ import type { FederationCache } from '../domain/core/federation-cache.contract.js';
2
+ import type { ChunkInfo, SharedInfo } from '../domain/core/federation-info.contract.js';
3
+ export declare function createFederationCache(cachePath: string): FederationCache<undefined>;
4
+ export declare function createFederationCache<TBundlerCache>(cachePath: string, bundlerCache: TBundlerCache): FederationCache<TBundlerCache>;
5
+ export declare function addExternalsToCache(cache: FederationCache, { externals, chunks }: {
6
+ externals: SharedInfo[];
7
+ chunks?: ChunkInfo;
8
+ }): void;
@@ -0,0 +1,11 @@
1
+ export function createFederationCache(cachePath, bundlerCache) {
2
+ return { externals: [], cachePath, bundlerCache };
3
+ }
4
+ export function addExternalsToCache(cache, { externals, chunks }) {
5
+ cache.externals.push(...externals);
6
+ if (chunks) {
7
+ if (!cache.chunks)
8
+ cache.chunks = {};
9
+ cache.chunks = { ...cache.chunks, ...chunks };
10
+ }
11
+ }
@@ -0,0 +1,4 @@
1
+ import type { FederationCache } from '../../domain.js';
2
+ import type { FederationOptions, NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
3
+ export declare function normalizeFederationOptions(options: FederationOptions): NormalizedFederationOptions<undefined>;
4
+ export declare function normalizeFederationOptions<TBundlerCache>(options: FederationOptions, cache: FederationCache<TBundlerCache>): NormalizedFederationOptions<TBundlerCache>;
@@ -0,0 +1,10 @@
1
+ import { getDefaultCachePath } from '../utils/cache-persistence.js';
2
+ import { createFederationCache } from './federation-cache.js';
3
+ export function normalizeFederationOptions(options, cache) {
4
+ const federationCache = cache ??
5
+ createFederationCache(getDefaultCachePath(options.workspaceRoot));
6
+ return {
7
+ ...options,
8
+ federationCache,
9
+ };
10
+ }
@@ -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,37 @@
1
+ import { bundleExposedAndMappings, describeExposed, describeSharedMappings, } from './bundle-exposed-and-mappings.js';
2
+ import { writeFederationInfo } from './write-federation-info.js';
3
+ import { writeImportMap } from './write-import-map.js';
4
+ import { logger } from '../utils/logger.js';
5
+ import { AbortedError } from '../utils/errors.js';
6
+ export async function rebuildForFederation(config, fedOptions, externals, modifiedFiles, signal) {
7
+ const federationCache = fedOptions.federationCache;
8
+ const start = process.hrtime();
9
+ // Shared mappings and exposed modules
10
+ const artifactInfo = await bundleExposedAndMappings(config, fedOptions, externals, modifiedFiles, signal);
11
+ logger.measure(start, '[build artifacts] - To re-bundle all mappings and exposed.');
12
+ if (signal?.aborted)
13
+ throw new AbortedError('[buildForFederation] After exposed-and-mappings bundle');
14
+ const exposedInfo = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
15
+ const sharedMappingInfo = !artifactInfo
16
+ ? describeSharedMappings(config, fedOptions)
17
+ : artifactInfo.mappings;
18
+ const sharedExternals = [...federationCache.externals, ...sharedMappingInfo];
19
+ const buildNotificationsEndpoint = fedOptions.buildNotifications?.enable && fedOptions.dev
20
+ ? fedOptions.buildNotifications?.endpoint
21
+ : undefined;
22
+ const federationInfo = {
23
+ name: config.name,
24
+ shared: sharedExternals,
25
+ exposes: exposedInfo,
26
+ buildNotificationsEndpoint,
27
+ };
28
+ if (federationCache.chunks) {
29
+ federationInfo.chunks = federationCache.chunks;
30
+ }
31
+ if (artifactInfo?.chunks) {
32
+ federationInfo.chunks = { ...(federationInfo.chunks ?? {}), ...artifactInfo?.chunks };
33
+ }
34
+ writeFederationInfo(federationInfo, fedOptions);
35
+ writeImportMap(federationCache, fedOptions);
36
+ return federationInfo;
37
+ }
@@ -1,27 +1,33 @@
1
1
  import type { MappedPath } from '../utils/mapped-path.contract.js';
2
- export type NFBuildAdapter = (options: NFBuildAdapterOptions) => Promise<NFBuildAdapterResult[]>;
3
- export type BuildKind = 'shared-package' | 'shared-mapping' | 'exposed' | 'mapping-or-exposed';
2
+ import type { FederationCache } from './federation-cache.contract.js';
3
+ export interface NFBuildAdapter {
4
+ setup(options: NFBuildAdapterOptions): Promise<void>;
5
+ build(name: string, opts?: {
6
+ files?: string[];
7
+ signal?: AbortSignal;
8
+ }): Promise<NFBuildAdapterResult[]>;
9
+ dispose(name?: string): Promise<void>;
10
+ }
4
11
  export interface EntryPoint {
5
12
  fileName: string;
6
13
  outName: string;
7
14
  key?: string;
8
15
  }
9
- export interface NFBuildAdapterOptions {
16
+ export interface NFBuildAdapterOptions<TBundlerCache = unknown> {
10
17
  entryPoints: EntryPoint[];
11
18
  tsConfigPath?: string;
12
- external: Array<string>;
19
+ external: string[];
13
20
  outdir: string;
14
21
  mappedPaths: MappedPath[];
15
- packageName?: string;
16
- esm?: boolean;
22
+ bundleName: string;
23
+ isNodeModules: boolean;
17
24
  dev?: boolean;
18
25
  watch?: boolean;
19
26
  chunks?: boolean;
20
- kind: BuildKind;
21
27
  hash: boolean;
22
28
  platform?: 'browser' | 'node';
23
29
  optimizedMappings?: boolean;
24
- signal?: AbortSignal;
30
+ cache: FederationCache<TBundlerCache>;
25
31
  }
26
32
  export interface NFBuildAdapterResult {
27
33
  fileName: string;
@@ -0,0 +1,7 @@
1
+ import type { ChunkInfo, SharedInfo } from './federation-info.contract.js';
2
+ export type FederationCache<TBundlerCache = unknown> = {
3
+ externals: SharedInfo[];
4
+ chunks?: ChunkInfo;
5
+ bundlerCache: TBundlerCache;
6
+ cachePath: string;
7
+ };
@@ -1,4 +1,5 @@
1
1
  import type { BuildNotificationOptions } from './build-notification-options.contract.js';
2
+ import type { FederationCache } from './federation-cache.contract.js';
2
3
  export interface FederationOptions {
3
4
  workspaceRoot: string;
4
5
  outputPath: string;
@@ -16,3 +17,6 @@ export interface FederationOptions {
16
17
  entryPoint?: string;
17
18
  buildNotifications?: BuildNotificationOptions;
18
19
  }
20
+ export interface NormalizedFederationOptions<TBundlerCache = unknown> extends FederationOptions {
21
+ federationCache: FederationCache<TBundlerCache>;
22
+ }
@@ -1,6 +1,6 @@
1
1
  export type { SharedInfo, FederationInfo, ExposesInfo, ArtifactInfo, ChunkInfo, } from './federation-info.contract.js';
2
2
  export { type BuildNotificationOptions, BuildNotificationType, } from './build-notification-options.contract.js';
3
- export type { FederationOptions } from './federation-options.contract.js';
4
- export type { BuildKind, EntryPoint, NFBuildAdapterOptions, NFBuildAdapter, NFBuildAdapterResult, } from './build-adapter.contract.js';
5
- export type { BuildParams } from './build-params.contract.js';
3
+ export type { FederationOptions, NormalizedFederationOptions, } from './federation-options.contract.js';
4
+ export type { EntryPoint, NFBuildAdapterOptions, NFBuildAdapter, NFBuildAdapterResult, } from './build-adapter.contract.js';
6
5
  export { CHUNK_PREFIX, toChunkImport } from './chunk.js';
6
+ export type { FederationCache } from './federation-cache.contract.js';
@@ -1,6 +1,6 @@
1
1
  import type { NormalizedExternalConfig } from '../domain/config/external-config.contract.js';
2
2
  import type { ChunkInfo, SharedInfo } from '../domain/core/federation-info.contract.js';
3
- export declare const getCachePath: (workspaceRoot: string, project: string) => string;
3
+ export declare const getDefaultCachePath: (workspaceRoot: string) => string;
4
4
  export declare const getFilename: (title: string, dev?: boolean) => string;
5
5
  export declare const getChecksum: (shared: Record<string, NormalizedExternalConfig>, dev: "1" | "0") => string;
6
6
  export declare const cacheEntry: (pathToCache: string, fileName: string) => {
@@ -1,8 +1,8 @@
1
1
  import path from 'path';
2
2
  import fs from 'fs';
3
3
  import crypto from 'crypto';
4
- import { logger } from '../utils/logger.js';
5
- export const getCachePath = (workspaceRoot, project) => path.join(workspaceRoot, 'node_modules/.cache/native-federation', project);
4
+ import { logger } from './logger.js';
5
+ export const getDefaultCachePath = (workspaceRoot) => path.join(workspaceRoot, 'node_modules/.cache/native-federation');
6
6
  export const getFilename = (title, dev) => {
7
7
  const devSuffix = dev ? '-dev' : '';
8
8
  return `${title}${devSuffix}.meta.json`;
@@ -15,13 +15,13 @@ export function resolveGlobSync(pattern, baseDir = process.cwd()) {
15
15
  const entries = fs.readdirSync(dir, { withFileTypes: true });
16
16
  if (segment === '*') {
17
17
  entries
18
- .filter((entry) => entry.isDirectory())
19
- .forEach((entry) => search(path.join(dir, entry.name), segmentIndex + 1));
18
+ .filter(entry => entry.isDirectory())
19
+ .forEach(entry => search(path.join(dir, entry.name), segmentIndex + 1));
20
20
  }
21
21
  else {
22
22
  entries
23
- .filter((entry) => entry.name === segment)
24
- .forEach((entry) => search(path.join(dir, entry.name), segmentIndex + 1));
23
+ .filter(entry => entry.name === segment)
24
+ .forEach(entry => search(path.join(dir, entry.name), segmentIndex + 1));
25
25
  }
26
26
  }
27
27
  search(baseDir, 0);
@@ -1,5 +0,0 @@
1
- export interface BuildParams {
2
- skipMappingsAndExposed: boolean;
3
- skipShared: boolean;
4
- signal?: AbortSignal;
5
- }
@@ -1,2 +0,0 @@
1
- import type { NFBuildAdapterOptions } from '../domain/core/build-adapter.contract.js';
2
- export declare function bundle(options: NFBuildAdapterOptions): Promise<import("../domain/core/build-adapter.contract.js").NFBuildAdapterResult[]>;
@@ -1,5 +0,0 @@
1
- import { getBuildAdapter } from '../core/build-adapter.js';
2
- export async function bundle(options) {
3
- const adapter = getBuildAdapter();
4
- return await adapter(options);
5
- }