From 8e061e7f517ddfbb5dd8ba68f106629818795cd1 Mon Sep 17 00:00:00 2001 From: Fabian Zankl Date: Fri, 6 Feb 2026 10:39:45 +0100 Subject: [PATCH 1/2] Fixed failing build caused by concurrent access to externals metadata. --- .../src/lib/core/remove-unused-deps.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libs/native-federation-core/src/lib/core/remove-unused-deps.ts b/libs/native-federation-core/src/lib/core/remove-unused-deps.ts index 88c6f24e..b76f0237 100644 --- a/libs/native-federation-core/src/lib/core/remove-unused-deps.ts +++ b/libs/native-federation-core/src/lib/core/remove-unused-deps.ts @@ -24,6 +24,7 @@ export function removeUnusedDeps( const usedPackageNamesWithTransient = addTransientDeps( usedPackageNames, workspaceRoot, + config.name ); const filteredShared = filterShared(config, usedPackageNamesWithTransient); @@ -82,7 +83,7 @@ function findUsedDeps( return { usedPackageNames, usedMappings }; } -function addTransientDeps(packages: Set, workspaceRoot: string) { +function addTransientDeps(packages: Set, workspaceRoot: string, configName: string) { const packagesAndPeers = new Set([...packages]); const discovered = new Set(packagesAndPeers); const stack = [...packagesAndPeers]; @@ -100,7 +101,7 @@ function addTransientDeps(packages: Set, workspaceRoot: string) { continue; } - const peerDeps = getExternalImports(pInfo, workspaceRoot); + const peerDeps = getExternalImports(pInfo, workspaceRoot, configName); for (const peerDep of peerDeps) { if (!discovered.has(peerDep)) { @@ -113,12 +114,13 @@ function addTransientDeps(packages: Set, workspaceRoot: string) { return packagesAndPeers; } -function getExternalImports(pInfo: PackageInfo, workspaceRoot: string) { +function getExternalImports(pInfo: PackageInfo, workspaceRoot: string, configName: string) { const encodedPackageName = normalizePackageName(pInfo.packageName); const cacheFileName = `${encodedPackageName}-${pInfo.version}.deps.json`; const cachePath = path.join( workspaceRoot, 'node_modules/.cache/native-federation/_externals-metadata', + configName ); const cacheFilePath = path.join(cachePath, cacheFileName); From 4988820bed52babd5d7b9e211f417273920b5151 Mon Sep 17 00:00:00 2001 From: Fabian Zankl Date: Fri, 6 Feb 2026 15:34:29 +0100 Subject: [PATCH 2/2] Fallback logic for project name. --- .../src/lib/core/build-for-federation.ts | 11 ++--------- .../src/lib/core/remove-unused-deps.ts | 12 +++++++----- .../src/lib/utils/config-utils.ts | 14 ++++++++++++++ 3 files changed, 23 insertions(+), 14 deletions(-) create mode 100644 libs/native-federation-core/src/lib/utils/config-utils.ts diff --git a/libs/native-federation-core/src/lib/core/build-for-federation.ts b/libs/native-federation-core/src/lib/core/build-for-federation.ts index adfb8be5..6b4e80a1 100644 --- a/libs/native-federation-core/src/lib/core/build-for-federation.ts +++ b/libs/native-federation-core/src/lib/core/build-for-federation.ts @@ -17,6 +17,7 @@ import { logger } from '../utils/logger'; import { getCachePath } from './../utils/bundle-caching'; import { normalizePackageName } from '../utils/normalize'; import { AbortedError } from '../utils/errors'; +import { resolveProjectName } from '../utils/config-utils'; export interface BuildParams { skipMappingsAndExposed: boolean; @@ -64,15 +65,7 @@ export async function buildForFederation( ? describeExposed(config, fedOptions) : artefactInfo.exposes; - const normalizedCacheFolder = normalizePackageName(config.name); - if (normalizedCacheFolder.length < 1) { - logger.warn( - "Project name in 'federation.config.js' is empty, defaulting to 'shell' cache folder (could collide with other projects in the workspace).", - ); - } - const cacheProjectFolder = - normalizedCacheFolder.length < 1 ? 'shell' : normalizedCacheFolder; - + const cacheProjectFolder = resolveProjectName(config); const pathToCache = getCachePath( fedOptions.workspaceRoot, cacheProjectFolder, diff --git a/libs/native-federation-core/src/lib/core/remove-unused-deps.ts b/libs/native-federation-core/src/lib/core/remove-unused-deps.ts index b76f0237..39f8cac1 100644 --- a/libs/native-federation-core/src/lib/core/remove-unused-deps.ts +++ b/libs/native-federation-core/src/lib/core/remove-unused-deps.ts @@ -7,6 +7,7 @@ import { getPackageInfo, PackageInfo } from '../utils/package-info'; import { getExternalImports as extractExternalImports } from '../utils/get-external-imports'; import { MappedPath } from '../utils/mapped-paths'; import { normalizePackageName } from '../utils/normalize'; +import { resolveProjectName } from '../utils/config-utils'; export function removeUnusedDeps( config: NormalizedFederationConfig, @@ -21,10 +22,11 @@ export function removeUnusedDeps( const usedPackageNames = usedDeps.usedPackageNames; const usedMappings = usedDeps.usedMappings; + const projectName = resolveProjectName(config); const usedPackageNamesWithTransient = addTransientDeps( usedPackageNames, workspaceRoot, - config.name + projectName ); const filteredShared = filterShared(config, usedPackageNamesWithTransient); @@ -83,7 +85,7 @@ function findUsedDeps( return { usedPackageNames, usedMappings }; } -function addTransientDeps(packages: Set, workspaceRoot: string, configName: string) { +function addTransientDeps(packages: Set, workspaceRoot: string, projectName: string) { const packagesAndPeers = new Set([...packages]); const discovered = new Set(packagesAndPeers); const stack = [...packagesAndPeers]; @@ -101,7 +103,7 @@ function addTransientDeps(packages: Set, workspaceRoot: string, configNa continue; } - const peerDeps = getExternalImports(pInfo, workspaceRoot, configName); + const peerDeps = getExternalImports(pInfo, workspaceRoot, projectName); for (const peerDep of peerDeps) { if (!discovered.has(peerDep)) { @@ -114,13 +116,13 @@ function addTransientDeps(packages: Set, workspaceRoot: string, configNa return packagesAndPeers; } -function getExternalImports(pInfo: PackageInfo, workspaceRoot: string, configName: string) { +function getExternalImports(pInfo: PackageInfo, workspaceRoot: string, projectName: string) { const encodedPackageName = normalizePackageName(pInfo.packageName); const cacheFileName = `${encodedPackageName}-${pInfo.version}.deps.json`; const cachePath = path.join( workspaceRoot, 'node_modules/.cache/native-federation/_externals-metadata', - configName + projectName ); const cacheFilePath = path.join(cachePath, cacheFileName); diff --git a/libs/native-federation-core/src/lib/utils/config-utils.ts b/libs/native-federation-core/src/lib/utils/config-utils.ts new file mode 100644 index 00000000..214b6e20 --- /dev/null +++ b/libs/native-federation-core/src/lib/utils/config-utils.ts @@ -0,0 +1,14 @@ +import { NormalizedFederationConfig } from '../config/federation-config'; +import { logger } from './logger'; +import { normalizePackageName } from './normalize'; + +export function resolveProjectName(config: NormalizedFederationConfig): string { + const normalizedProjectName = normalizePackageName(config.name); + if (normalizedProjectName.length < 1) { + logger.warn( + "Project name in 'federation.config.js' is empty, defaulting to 'shell' cache folder (could collide with other projects in the workspace).", + ); + } + + return normalizedProjectName.length < 1 ? 'shell' : normalizedProjectName; +}