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

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softarc/native-federation",
3
- "version": "4.0.0-RC3",
3
+ "version": "4.0.0-RC5",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "dependencies": {
package/src/internal.d.ts CHANGED
@@ -5,7 +5,7 @@ export { logger, setLogLevel } from './lib/utils/logger.js';
5
5
  export type { MappedPath } from './lib/domain/utils/mapped-path.contract.js';
6
6
  export { RebuildQueue } from './lib/utils/rebuild-queue.js';
7
7
  export { AbortedError } from './lib/utils/errors.js';
8
- export { createBuildResultMap, lookupInResultMap } from './lib/utils/build-result-map.js';
8
+ export { createBuildResultMap, lookupInResultMap, popFromResultMap, } from './lib/utils/build-result-map.js';
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';
package/src/internal.js CHANGED
@@ -4,5 +4,5 @@ export * from './lib/utils/errors.js';
4
4
  export { logger, setLogLevel } from './lib/utils/logger.js';
5
5
  export { RebuildQueue } from './lib/utils/rebuild-queue.js';
6
6
  export { AbortedError } from './lib/utils/errors.js';
7
- export { createBuildResultMap, lookupInResultMap } from './lib/utils/build-result-map.js';
7
+ export { createBuildResultMap, lookupInResultMap, popFromResultMap, } from './lib/utils/build-result-map.js';
8
8
  export { writeImportMap } from './lib/core/write-import-map.js';
@@ -6,6 +6,7 @@ import { logger } from '../utils/logger.js';
6
6
  import { getCachePath } from './../utils/bundle-caching.js';
7
7
  import { normalizePackageName } from '../utils/normalize.js';
8
8
  import { AbortedError } from '../utils/errors.js';
9
+ import { resolveProjectName } from '../utils/config-utils.js';
9
10
  export const defaultBuildParams = {
10
11
  skipMappingsAndExposed: false,
11
12
  skipShared: false,
@@ -22,11 +23,7 @@ export async function buildForFederation(config, fedOptions, externals, buildPar
22
23
  throw new AbortedError('[buildForFederation] After exposed-and-mappings bundle');
23
24
  }
24
25
  const exposedInfo = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
25
- const normalizedCacheFolder = normalizePackageName(config.name);
26
- if (normalizedCacheFolder.length < 1) {
27
- logger.warn("Project name in 'federation.config.js' is empty, defaulting to 'shell' cache folder (could collide with other projects in the workspace).");
28
- }
29
- const cacheProjectFolder = normalizedCacheFolder.length < 1 ? 'shell' : normalizedCacheFolder;
26
+ const cacheProjectFolder = resolveProjectName(config);
30
27
  const pathToCache = getCachePath(fedOptions.workspaceRoot, cacheProjectFolder);
31
28
  if (!buildParams.skipShared && sharedCache.externals.length > 0) {
32
29
  logger.info('Checksum matched, re-using cached externals.');
@@ -92,6 +89,9 @@ export async function buildForFederation(config, fedOptions, externals, buildPar
92
89
  if (sharedCache.chunks) {
93
90
  federationInfo.chunks = sharedCache.chunks;
94
91
  }
92
+ if (artifactInfo?.chunks) {
93
+ federationInfo.chunks = { ...(federationInfo.chunks ?? {}), ...artifactInfo?.chunks };
94
+ }
95
95
  writeFederationInfo(federationInfo, fedOptions);
96
96
  writeImportMap(sharedCache, fedOptions);
97
97
  return federationInfo;
@@ -1,10 +1,11 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
- import { createBuildResultMap, lookupInResultMap } from '../utils/build-result-map.js';
3
+ import { createBuildResultMap, popFromResultMap } from '../utils/build-result-map.js';
4
4
  import { bundle } from '../utils/build-utils.js';
5
5
  import { logger } from '../utils/logger.js';
6
6
  import { normalize } from '../utils/normalize.js';
7
7
  import { AbortedError } from '../utils/errors.js';
8
+ import { rewriteChunkImports } from '../utils/rewrite-chunk-imports.js';
8
9
  export async function bundleExposedAndMappings(config, fedOptions, externals, signal) {
9
10
  if (signal?.aborted) {
10
11
  throw new AbortedError('[bundle-exposed-and-mappings] Aborted before bundling');
@@ -33,6 +34,8 @@ export async function bundleExposedAndMappings(config, fedOptions, externals, si
33
34
  watch: fedOptions.watch,
34
35
  mappedPaths: config.sharedMappings,
35
36
  kind: 'mapping-or-exposed',
37
+ chunks: (typeof fedOptions.chunks === 'boolean' && fedOptions.chunks) ||
38
+ (typeof fedOptions.chunks === 'object' && !!fedOptions.chunks.enable),
36
39
  hash,
37
40
  optimizedMappings: config.features.ignoreUnusedDeps,
38
41
  signal,
@@ -49,10 +52,13 @@ export async function bundleExposedAndMappings(config, fedOptions, externals, si
49
52
  }
50
53
  const resultMap = createBuildResultMap(result, hash);
51
54
  const sharedResult = [];
55
+ const entryFiles = [];
56
+ // Pick shared-mappings
52
57
  for (const item of shared) {
58
+ const distEntryFile = popFromResultMap(resultMap, item.outName);
53
59
  sharedResult.push({
54
60
  packageName: item.key,
55
- outFileName: lookupInResultMap(resultMap, item.outName),
61
+ outFileName: path.basename(distEntryFile),
56
62
  requiredVersion: '',
57
63
  singleton: true,
58
64
  strictVersion: false,
@@ -63,20 +69,37 @@ export async function bundleExposedAndMappings(config, fedOptions, externals, si
63
69
  entryPoint: normalize(path.normalize(item.fileName)),
64
70
  },
65
71
  });
72
+ entryFiles.push(distEntryFile);
66
73
  }
67
74
  const exposedResult = [];
75
+ // Pick exposed-modules
68
76
  for (const item of exposes) {
77
+ const distEntryFile = popFromResultMap(resultMap, item.outName);
69
78
  exposedResult.push({
70
79
  key: item.key,
71
- outFileName: lookupInResultMap(resultMap, item.outName),
80
+ outFileName: path.basename(distEntryFile),
72
81
  dev: !fedOptions.dev
73
82
  ? undefined
74
83
  : {
75
84
  entryPoint: normalize(path.join(fedOptions.workspaceRoot, item.fileName)),
76
85
  },
77
86
  });
87
+ entryFiles.push(distEntryFile);
78
88
  }
79
- return { mappings: sharedResult, exposes: exposedResult };
89
+ // Remove .map files
90
+ Object.keys(resultMap)
91
+ .filter(f => f.endsWith('.map'))
92
+ .forEach(f => popFromResultMap(resultMap, f));
93
+ // Process remaining chunks and lazy loaded internal modules
94
+ let exportedChunks = undefined;
95
+ if (typeof fedOptions.chunks === 'object' && fedOptions.chunks.dense === true) {
96
+ for (const entryFile of entryFiles)
97
+ rewriteChunkImports(entryFile);
98
+ exportedChunks = {
99
+ ['mapping-or-exposed']: Object.values(resultMap).map(chunk => path.basename(chunk)),
100
+ };
101
+ }
102
+ return { mappings: sharedResult, exposes: exposedResult, chunks: exportedChunks };
80
103
  }
81
104
  export function describeExposed(config, options) {
82
105
  const result = [];
@@ -92,7 +92,6 @@ export async function bundleShared(sharedBundles, config, fedOptions, externals,
92
92
  }
93
93
  const outFileNames = [...expectedResults];
94
94
  const result = buildResult(packageInfos, sharedBundles, outFileNames);
95
- // TODO: Decide whether/when to add .map files
96
95
  const chunks = bundleResult.filter(br => !br.fileName.endsWith('.map') &&
97
96
  !result.find(r => r.outFileName === path.basename(br.fileName)));
98
97
  /**
@@ -5,6 +5,7 @@ import { cwd } from 'process';
5
5
  import { getPackageInfo } from '../utils/package-info.js';
6
6
  import { getExternalImports as extractExternalImports } from '../utils/get-external-imports.js';
7
7
  import { normalizePackageName } from '../utils/normalize.js';
8
+ import { resolveProjectName } from '../utils/config-utils.js';
8
9
  export function removeUnusedDeps(config, main, workspaceRoot) {
9
10
  const fileInfos = getProjectData(main, cwd(), {
10
11
  includeExternalLibraries: true,
@@ -12,7 +13,8 @@ export function removeUnusedDeps(config, main, workspaceRoot) {
12
13
  const usedDeps = findUsedDeps(fileInfos, workspaceRoot, config);
13
14
  const usedPackageNames = usedDeps.usedPackageNames;
14
15
  const usedMappings = usedDeps.usedMappings;
15
- const usedPackageNamesWithTransient = addTransientDeps(usedPackageNames, workspaceRoot);
16
+ const projectName = resolveProjectName(config);
17
+ const usedPackageNamesWithTransient = addTransientDeps(usedPackageNames, workspaceRoot, projectName);
16
18
  const filteredShared = filterShared(config, usedPackageNamesWithTransient);
17
19
  return {
18
20
  ...config,
@@ -45,7 +47,7 @@ function findUsedDeps(fileInfos, workspaceRoot, config) {
45
47
  }
46
48
  return { usedPackageNames, usedMappings };
47
49
  }
48
- function addTransientDeps(packages, workspaceRoot) {
50
+ function addTransientDeps(packages, workspaceRoot, projectName) {
49
51
  const packagesAndPeers = new Set([...packages]);
50
52
  const discovered = new Set(packagesAndPeers);
51
53
  const stack = [...packagesAndPeers];
@@ -58,7 +60,7 @@ function addTransientDeps(packages, workspaceRoot) {
58
60
  if (!pInfo) {
59
61
  continue;
60
62
  }
61
- const peerDeps = getExternalImports(pInfo, workspaceRoot);
63
+ const peerDeps = getExternalImports(pInfo, workspaceRoot, projectName);
62
64
  for (const peerDep of peerDeps) {
63
65
  if (!discovered.has(peerDep)) {
64
66
  discovered.add(peerDep);
@@ -69,10 +71,10 @@ function addTransientDeps(packages, workspaceRoot) {
69
71
  }
70
72
  return packagesAndPeers;
71
73
  }
72
- function getExternalImports(pInfo, workspaceRoot) {
74
+ function getExternalImports(pInfo, workspaceRoot, projectName) {
73
75
  const encodedPackageName = normalizePackageName(pInfo.packageName);
74
76
  const cacheFileName = `${encodedPackageName}-${pInfo.version}.deps.json`;
75
- const cachePath = path.join(workspaceRoot, 'node_modules/.cache/native-federation/_externals-metadata');
77
+ const cachePath = path.join(workspaceRoot, 'node_modules/.cache/native-federation/_externals-metadata', projectName);
76
78
  const cacheFilePath = path.join(cachePath, cacheFileName);
77
79
  const cacheHit = fs.existsSync(cacheFilePath);
78
80
  let peerDeps;
@@ -29,4 +29,5 @@ export interface ExposesInfo {
29
29
  export interface ArtifactInfo {
30
30
  mappings: SharedInfo[];
31
31
  exposes: ExposesInfo[];
32
+ chunks?: ChunkInfo;
32
33
  }
@@ -1,3 +1,4 @@
1
1
  import type { NFBuildAdapterResult } from '../domain/core/build-adapter.contract.js';
2
2
  export declare function createBuildResultMap(buildResult: NFBuildAdapterResult[], isHashed: boolean): Record<string, string>;
3
3
  export declare function lookupInResultMap(map: Record<string, string>, requestName: string): string;
4
+ export declare function popFromResultMap(map: Record<string, string>, requestName: string): string;
@@ -11,11 +11,18 @@ export function createBuildResultMap(buildResult, isHashed) {
11
11
  const part2 = resultName.substring(end);
12
12
  requestName = part1 + part2;
13
13
  }
14
- map[requestName] = resultName;
14
+ map[requestName] = item.fileName;
15
15
  }
16
16
  return map;
17
17
  }
18
18
  export function lookupInResultMap(map, requestName) {
19
19
  const key = path.basename(requestName);
20
- return map[key];
20
+ // path.basename is to maintain backwards compatible
21
+ return path.basename(map[key]);
22
+ }
23
+ export function popFromResultMap(map, requestName) {
24
+ const key = path.basename(requestName);
25
+ const out = map[key];
26
+ delete map[key];
27
+ return out;
21
28
  }
@@ -0,0 +1,2 @@
1
+ import { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
2
+ export declare function resolveProjectName(config: NormalizedFederationConfig): string;
@@ -0,0 +1,9 @@
1
+ import { logger } from './logger.js';
2
+ import { normalizePackageName } from './normalize.js';
3
+ export function resolveProjectName(config) {
4
+ const normalizedProjectName = normalizePackageName(config.name);
5
+ if (normalizedProjectName.length < 1) {
6
+ logger.warn("Project name in 'federation.config.js' is empty, defaulting to 'shell' cache folder (could collide with other projects in the workspace).");
7
+ }
8
+ return normalizedProjectName.length < 1 ? 'shell' : normalizedProjectName;
9
+ }