From ad4e75961cdc899d3cb5186006c7c0dfea1cbef8 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Tue, 4 Nov 2025 16:58:04 +0100 Subject: [PATCH 01/12] feat: Added cache file with checksum --- .../src/lib/core/build-for-federation.ts | 66 +++++++++++------ .../src/lib/core/bundle-shared.ts | 74 ++++++------------- .../src/lib/core/get-cache.ts | 67 +++++++++++++++++ .../src/lib/utils/normalize.ts | 5 ++ 4 files changed, 138 insertions(+), 74 deletions(-) create mode 100644 libs/native-federation-core/src/lib/core/get-cache.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 b624c638..3796c9b2 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 @@ -14,6 +14,15 @@ import { FederationOptions } from './federation-options'; import { writeFederationInfo } from './write-federation-info'; import { writeImportMap } from './write-import-map'; import { logger } from '../utils/logger'; +import { + copyCacheToDist, + getCachedMetadata, + getCachePath, + getChecksum, + storeCachedMetadata, +} from './get-cache'; +import path from 'path'; +import { normalizeFilename } from '../utils/normalize'; export interface BuildParams { skipMappingsAndExposed: boolean; @@ -27,7 +36,7 @@ export const defaultBuildParams: BuildParams = { // Externals cache const sharedPackageInfoCache: SharedInfo[] = []; -const cachedSharedPackages = new Set(); +// const cachedSharedPackages = new Set(); export async function buildForFederation( config: NormalizedFederationConfig, @@ -54,7 +63,22 @@ export async function buildForFederation( ? describeExposed(config, fedOptions) : artefactInfo.exposes; - if (!buildParams.skipShared) { + const cacheFolder = getCachePath( + fedOptions.workspaceRoot, + normalizeFilename(config.name) + ); + const cacheChecksum = getChecksum(config.shared); + + const sharedPackageInfoCache: SharedInfo[] = getCachedMetadata( + cacheFolder, + cacheChecksum + ); + + if (!buildParams.skipShared && sharedPackageInfoCache.length === 0) { + logger.debug('Checksum matched, re-using cached externals.'); + } + + if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { const { sharedBrowser, sharedServer, separateBrowser, separateServer } = splitShared(config.shared); @@ -65,7 +89,8 @@ export async function buildForFederation( config, fedOptions, externals, - 'browser' + 'browser', + cacheFolder ); logger.measure( @@ -74,9 +99,6 @@ export async function buildForFederation( ); sharedPackageInfoCache.push(...sharedPackageInfoBrowser); - Object.keys(sharedBrowser).forEach((packageName) => - cachedSharedPackages.add(packageName) - ); } if (Object.keys(sharedServer).length > 0) { @@ -86,16 +108,14 @@ export async function buildForFederation( config, fedOptions, externals, - 'node' + 'node', + cacheFolder ); logger.measure( start, '[build artifacts] - To bundle all shared node externals' ); sharedPackageInfoCache.push(...sharedPackageInfoServer); - Object.keys(sharedServer).forEach((packageName) => - cachedSharedPackages.add(packageName) - ); } if (Object.keys(separateBrowser).length > 0) { @@ -105,16 +125,14 @@ export async function buildForFederation( externals, config, fedOptions, - 'browser' + 'browser', + cacheFolder ); logger.measure( start, '[build artifacts] - To bundle all separate browser externals' ); sharedPackageInfoCache.push(...separatePackageInfoBrowser); - Object.keys(separateBrowser).forEach((packageName) => - cachedSharedPackages.add(packageName) - ); } if (Object.keys(separateServer).length > 0) { @@ -124,17 +142,21 @@ export async function buildForFederation( externals, config, fedOptions, - 'node' + 'node', + cacheFolder ); logger.measure( start, '[build artifacts] - To bundle all separate node externals' ); sharedPackageInfoCache.push(...separatePackageInfoServer); - Object.keys(separateServer).forEach((packageName) => - cachedSharedPackages.add(packageName) - ); } + + copyCacheToDist( + cacheFolder, + path.join(fedOptions.workspaceRoot, fedOptions.outputPath) + ); + storeCachedMetadata(cacheFolder, cacheChecksum, sharedPackageInfoCache); } const sharedMappingInfo = !artefactInfo @@ -179,7 +201,8 @@ async function bundleSeparate( externals: string[], config: NormalizedFederationConfig, fedOptions: FederationOptions, - platform: 'node' | 'browser' + platform: 'node' | 'browser', + dest: string ) { const bundlePromises = Object.entries(separateBrowser).map( async ([key, shared]) => { @@ -192,7 +215,8 @@ async function bundleSeparate( config, fedOptions, filteredExternals, - platform + platform, + dest ); } ); @@ -210,7 +234,7 @@ function splitShared( const separateServer: Record = {}; for (const key in shared) { - if (cachedSharedPackages.has(key)) continue; + // if (cachedSharedPackages.has(key)) continue; const obj = shared[key]; if (obj.platform === 'node' && obj.build === 'default') { sharedServer[key] = obj; diff --git a/libs/native-federation-core/src/lib/core/bundle-shared.ts b/libs/native-federation-core/src/lib/core/bundle-shared.ts index 0ee1f467..0cfd8a06 100644 --- a/libs/native-federation-core/src/lib/core/bundle-shared.ts +++ b/libs/native-federation-core/src/lib/core/bundle-shared.ts @@ -8,7 +8,6 @@ import { bundle } from '../utils/build-utils'; import { getPackageInfo, PackageInfo } from '../utils/package-info'; import { SharedInfo } from '@softarc/native-federation-runtime'; import { FederationOptions } from './federation-options'; -import { copySrcMapIfExists } from '../utils/copy-src-map-if-exists'; import { logger } from '../utils/logger'; import crypto from 'crypto'; import { DEFAULT_EXTERNAL_LIST } from './default-external-list'; @@ -24,18 +23,14 @@ export async function bundleShared( config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], - platform: 'browser' | 'node' = 'browser' + platform: 'browser' | 'node' = 'browser', + dest: string ): Promise> { const folder = fedOptions.packageJson ? path.dirname(fedOptions.packageJson) : fedOptions.workspaceRoot; - const cachePath = path.join( - fedOptions.workspaceRoot, - 'node_modules/.cache/native-federation' - ); - - fs.mkdirSync(cachePath, { recursive: true }); + fs.mkdirSync(dest, { recursive: true }); const inferredPackageInfos = Object.keys(sharedBundles) .filter((packageName) => !sharedBundles[packageName].packageInfo) @@ -67,11 +62,11 @@ export async function bundleShared( fedOptions.outputPath ); - const exptedResults = allEntryPoints.map((ep) => + const expectedResults = allEntryPoints.map((ep) => path.join(fullOutputPath, ep.outName) ); const entryPoints = allEntryPoints.filter( - (ep) => !fs.existsSync(path.join(cachePath, ep.outName)) + (ep) => !fs.existsSync(path.join(dest, ep.outName)) ); if (entryPoints.length > 0) { @@ -98,7 +93,7 @@ export async function bundleShared( entryPoints, tsConfigPath: fedOptions.tsConfig, external: [...additionalExternals, ...externals], - outdir: cachePath, + outdir: dest, mappedPaths: config.sharedMappings, dev: fedOptions.dev, kind: 'shared-package', @@ -108,9 +103,9 @@ export async function bundleShared( }); const cachedFiles = bundleResult.map((br) => path.basename(br.fileName)); - rewriteImports(cachedFiles, cachePath); + rewriteImports(cachedFiles, dest); - copyCacheToOutput(cachedFiles, cachePath, fullOutputPath); + // copyCacheToOutput(cachedFiles, cachePath, fullOutputPath); } catch (e) { logger.error('Error bundling shared npm package '); if (e instanceof Error) { @@ -143,28 +138,7 @@ export async function bundleShared( throw e; } - const resultCacheFile = createCacheFileName( - configState, - sharedBundles, - fedOptions, - cachePath, - platform - ); - - if (fs.existsSync(resultCacheFile)) { - const cachedResult: SharedInfo[] = JSON.parse( - fs.readFileSync(resultCacheFile, 'utf-8') - ); - const cachedFiles = cachedResult.map((cr) => cr.outFileName); - - // Chunks are overwritten by the bundler, so we need to reprocess them - rewriteImports(cachedFiles, cachePath); - - copyCacheToOutput(cachedFiles, cachePath, fullOutputPath); - return cachedResult; - } - - const outFileNames = [...exptedResults]; + const outFileNames = [...expectedResults]; const result = buildResult( packageInfos, @@ -182,12 +156,6 @@ export async function bundleShared( addChunksToResult(chunks, result, fedOptions.dev); - fs.writeFileSync( - resultCacheFile, - JSON.stringify(result, undefined, 2), - 'utf-8' - ); - return result; } @@ -200,18 +168,18 @@ function rewriteImports(cachedFiles: string[], cachePath: string) { } } -function copyCacheToOutput( - cachedFiles: string[], - cachePath: string, - fullOutputPath: string -) { - for (const fileName of cachedFiles) { - const cachedFile = path.join(cachePath, fileName); - const distFileName = path.join(fullOutputPath, fileName); - copyFileIfExists(cachedFile, distFileName); - copySrcMapIfExists(cachedFile, distFileName); - } -} +// function copyCacheToOutput( +// cachedFiles: string[], +// cachePath: string, +// fullOutputPath: string +// ) { +// for (const fileName of cachedFiles) { +// const cachedFile = path.join(cachePath, fileName); +// const distFileName = path.join(fullOutputPath, fileName); +// copyFileIfExists(cachedFile, distFileName); +// copySrcMapIfExists(cachedFile, distFileName); +// } +// } function createOutName( pi: PackageInfo, diff --git a/libs/native-federation-core/src/lib/core/get-cache.ts b/libs/native-federation-core/src/lib/core/get-cache.ts new file mode 100644 index 00000000..7dc5dde5 --- /dev/null +++ b/libs/native-federation-core/src/lib/core/get-cache.ts @@ -0,0 +1,67 @@ +import path from 'path'; +import fs from 'fs'; +import crypto from 'crypto'; +import { NormalizedSharedConfig } from '../config/federation-config'; +import { SharedInfo } from '@softarc/native-federation-runtime'; + +export const getCachePath = (workspaceRoot: string, project: string) => + path.join(workspaceRoot, 'node_modules/.cache/native-federation', project); + +export const getChecksum = ( + shared: Record +): string => { + const denseExternals = Object.keys(shared) + .sort() + .reduce((clean, external) => { + return ( + clean + + ':' + + external + + (shared[external].version ? `@${shared[external].version}` : '') + ); + }, 'deps'); + + return crypto.createHash('sha256').update(denseExternals).digest('hex'); +}; + +export const getCachedMetadata = ( + pathToCache: string, + checksum: string +): SharedInfo[] => { + const metadataFile = path.join(pathToCache, 'metadata.json'); + if (!fs.existsSync(metadataFile)) return []; + + const cachedResult: { checksum: string; externals: SharedInfo[] } = + JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); + if (cachedResult.checksum !== checksum) return []; + return cachedResult.externals; +}; + +export const storeCachedMetadata = ( + pathToCache: string, + checksum: string, + externals: SharedInfo[] +) => { + fs.writeFileSync( + path.join(pathToCache, 'metadata.json'), + JSON.stringify({ checksum, externals }, undefined, 2), + 'utf-8' + ); +}; + +export const copyCacheToDist = ( + pathToCache: string, + fullOutputPath: string +) => { + fs.readdirSync(pathToCache).forEach((file) => { + if (file === '.checksum') return; + const cachedFile = path.join(pathToCache, file); + //const distFileName = path.join(fullOutputPath, file); + + if (fs.existsSync(cachedFile)) { + fs.copyFileSync(cachedFile, fullOutputPath); + } + console.log(file); + }); + fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true }); +}; diff --git a/libs/native-federation-core/src/lib/utils/normalize.ts b/libs/native-federation-core/src/lib/utils/normalize.ts index 4b0814b9..738c5fd3 100644 --- a/libs/native-federation-core/src/lib/utils/normalize.ts +++ b/libs/native-federation-core/src/lib/utils/normalize.ts @@ -15,3 +15,8 @@ export function normalize(path: string, trailingSlash?: boolean): string { return cand; } + +export function normalizeFilename(path: string) { + let sanitized = path.replace(/[^A-Za-z0-9]/g, '_'); + return sanitized.startsWith('_') ? sanitized.slice(1) : sanitized; +} From 9ec02ccc6ab39fa64c19b73cb06bd90e041572f1 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Tue, 4 Nov 2025 17:27:46 +0100 Subject: [PATCH 02/12] fix(nf): Correct output folder --- .../src/lib/core/build-for-federation.ts | 16 ++++++++++------ .../src/lib/core/get-cache.ts | 6 +++--- 2 files changed, 13 insertions(+), 9 deletions(-) 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 3796c9b2..1c0abee0 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 @@ -34,10 +34,6 @@ export const defaultBuildParams: BuildParams = { skipShared: false, }; -// Externals cache -const sharedPackageInfoCache: SharedInfo[] = []; -// const cachedSharedPackages = new Set(); - export async function buildForFederation( config: NormalizedFederationConfig, fedOptions: FederationOptions, @@ -63,6 +59,14 @@ export async function buildForFederation( ? describeExposed(config, fedOptions) : artefactInfo.exposes; + let cacheProjectFolder = normalizeFilename(config.name); + if (cacheProjectFolder.length < 1) { + logger.warn( + "Project name in 'federation.config.js' is empty, defaulting to 'NF_PROJECT'" + ); + cacheProjectFolder = 'NF_PROJECT'; + } + const cacheFolder = getCachePath( fedOptions.workspaceRoot, normalizeFilename(config.name) @@ -74,11 +78,11 @@ export async function buildForFederation( cacheChecksum ); - if (!buildParams.skipShared && sharedPackageInfoCache.length === 0) { + if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { logger.debug('Checksum matched, re-using cached externals.'); } - if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { + if (!buildParams.skipShared && sharedPackageInfoCache.length === 0) { const { sharedBrowser, sharedServer, separateBrowser, separateServer } = splitShared(config.shared); diff --git a/libs/native-federation-core/src/lib/core/get-cache.ts b/libs/native-federation-core/src/lib/core/get-cache.ts index 7dc5dde5..44372934 100644 --- a/libs/native-federation-core/src/lib/core/get-cache.ts +++ b/libs/native-federation-core/src/lib/core/get-cache.ts @@ -54,12 +54,12 @@ export const copyCacheToDist = ( fullOutputPath: string ) => { fs.readdirSync(pathToCache).forEach((file) => { - if (file === '.checksum') return; + if (file === 'metadata.json') return; const cachedFile = path.join(pathToCache, file); - //const distFileName = path.join(fullOutputPath, file); + const distFileName = path.join(fullOutputPath, file); if (fs.existsSync(cachedFile)) { - fs.copyFileSync(cachedFile, fullOutputPath); + fs.copyFileSync(cachedFile, distFileName); } console.log(file); }); From a713a31194e9574a45b4c2759c043f4d6a4dd5bd Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Tue, 4 Nov 2025 17:42:31 +0100 Subject: [PATCH 03/12] feat(nf): Clear cache folder if rebuild necessary --- .../src/lib/core/build-for-federation.ts | 8 ++++--- .../src/lib/core/bundle-shared.ts | 21 ------------------- .../src/lib/core/get-cache.ts | 18 ++++++++++++++++ .../src/lib/utils/copy-src-map-if-exists.ts | 10 --------- 4 files changed, 23 insertions(+), 34 deletions(-) delete mode 100644 libs/native-federation-core/src/lib/utils/copy-src-map-if-exists.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 1c0abee0..a370e4cb 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 @@ -19,6 +19,7 @@ import { getCachedMetadata, getCachePath, getChecksum, + purgeCacheFolder, storeCachedMetadata, } from './get-cache'; import path from 'path'; @@ -62,9 +63,8 @@ export async function buildForFederation( let cacheProjectFolder = normalizeFilename(config.name); if (cacheProjectFolder.length < 1) { logger.warn( - "Project name in 'federation.config.js' is empty, defaulting to 'NF_PROJECT'" + "Project name in 'federation.config.js' is empty, defaulting to root cache folder." ); - cacheProjectFolder = 'NF_PROJECT'; } const cacheFolder = getCachePath( @@ -79,10 +79,12 @@ export async function buildForFederation( ); if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { - logger.debug('Checksum matched, re-using cached externals.'); + logger.info('Checksum matched, re-using cached externals.'); } if (!buildParams.skipShared && sharedPackageInfoCache.length === 0) { + purgeCacheFolder(cacheFolder); + const { sharedBrowser, sharedServer, separateBrowser, separateServer } = splitShared(config.shared); diff --git a/libs/native-federation-core/src/lib/core/bundle-shared.ts b/libs/native-federation-core/src/lib/core/bundle-shared.ts index 0cfd8a06..97143f03 100644 --- a/libs/native-federation-core/src/lib/core/bundle-shared.ts +++ b/libs/native-federation-core/src/lib/core/bundle-shared.ts @@ -168,19 +168,6 @@ function rewriteImports(cachedFiles: string[], cachePath: string) { } } -// function copyCacheToOutput( -// cachedFiles: string[], -// cachePath: string, -// fullOutputPath: string -// ) { -// for (const fileName of cachedFiles) { -// const cachedFile = path.join(cachePath, fileName); -// const distFileName = path.join(fullOutputPath, fileName); -// copyFileIfExists(cachedFile, distFileName); -// copySrcMapIfExists(cachedFile, distFileName); -// } -// } - function createOutName( pi: PackageInfo, configState: string, @@ -280,11 +267,3 @@ function calcHash(hashBase: string) { .substring(0, 10); return hash; } - -function copyFileIfExists(cachedFile: string, fullOutputPath: string) { - fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true }); - - if (fs.existsSync(cachedFile)) { - fs.copyFileSync(cachedFile, fullOutputPath); - } -} diff --git a/libs/native-federation-core/src/lib/core/get-cache.ts b/libs/native-federation-core/src/lib/core/get-cache.ts index 44372934..6a9b8944 100644 --- a/libs/native-federation-core/src/lib/core/get-cache.ts +++ b/libs/native-federation-core/src/lib/core/get-cache.ts @@ -65,3 +65,21 @@ export const copyCacheToDist = ( }); fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true }); }; + +export const purgeCacheFolder = (pathToCache: string) => { + if (!fs.existsSync(pathToCache)) return; + + try { + fs.rmSync(pathToCache, { recursive: true, force: true }); + } catch (error) { + // Fallback for older Node.js versions or if rmSync fails + try { + fs.rmdirSync(pathToCache, { recursive: true }); + } catch (fallbackError) { + console.warn( + `Failed to purge cache folder: ${pathToCache}`, + fallbackError + ); + } + } +}; diff --git a/libs/native-federation-core/src/lib/utils/copy-src-map-if-exists.ts b/libs/native-federation-core/src/lib/utils/copy-src-map-if-exists.ts deleted file mode 100644 index 9905e033..00000000 --- a/libs/native-federation-core/src/lib/utils/copy-src-map-if-exists.ts +++ /dev/null @@ -1,10 +0,0 @@ -import * as fs from 'fs'; - -export function copySrcMapIfExists(cachedFile: string, fullOutputPath: string) { - const mapSrc = cachedFile + '.map'; - const mapDest = fullOutputPath + '.map'; - - if (fs.existsSync(mapSrc)) { - fs.copyFileSync(mapSrc, mapDest); - } -} From f978cce4bf3bdfcae2b8e0bee3c39acc2f630976 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Wed, 5 Nov 2025 08:29:07 +0100 Subject: [PATCH 04/12] feat(nf): Updated to cache per bundle --- .../src/lib/core/build-for-federation.ts | 43 ++++------ .../src/lib/core/bundle-shared.ts | 52 +++++++++++-- .../src/lib/core/get-cache.ts | 78 ++++++++++++------- 3 files changed, 107 insertions(+), 66 deletions(-) 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 a370e4cb..c9eafb53 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 @@ -14,15 +14,7 @@ import { FederationOptions } from './federation-options'; import { writeFederationInfo } from './write-federation-info'; import { writeImportMap } from './write-import-map'; import { logger } from '../utils/logger'; -import { - copyCacheToDist, - getCachedMetadata, - getCachePath, - getChecksum, - purgeCacheFolder, - storeCachedMetadata, -} from './get-cache'; -import path from 'path'; +import { getCachePath } from './get-cache'; import { normalizeFilename } from '../utils/normalize'; export interface BuildParams { @@ -35,6 +27,8 @@ export const defaultBuildParams: BuildParams = { skipShared: false, }; +const sharedPackageInfoCache: SharedInfo[] = []; + export async function buildForFederation( config: NormalizedFederationConfig, fedOptions: FederationOptions, @@ -67,24 +61,16 @@ export async function buildForFederation( ); } - const cacheFolder = getCachePath( + const pathToCache = getCachePath( fedOptions.workspaceRoot, normalizeFilename(config.name) ); - const cacheChecksum = getChecksum(config.shared); - - const sharedPackageInfoCache: SharedInfo[] = getCachedMetadata( - cacheFolder, - cacheChecksum - ); if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { logger.info('Checksum matched, re-using cached externals.'); } if (!buildParams.skipShared && sharedPackageInfoCache.length === 0) { - purgeCacheFolder(cacheFolder); - const { sharedBrowser, sharedServer, separateBrowser, separateServer } = splitShared(config.shared); @@ -96,7 +82,7 @@ export async function buildForFederation( fedOptions, externals, 'browser', - cacheFolder + { pathToCache, metaDataFile: 'meta-browser-shared.json' } ); logger.measure( @@ -115,7 +101,7 @@ export async function buildForFederation( fedOptions, externals, 'node', - cacheFolder + { pathToCache, metaDataFile: 'meta-node-shared.json' } ); logger.measure( start, @@ -132,7 +118,7 @@ export async function buildForFederation( config, fedOptions, 'browser', - cacheFolder + pathToCache ); logger.measure( start, @@ -149,7 +135,7 @@ export async function buildForFederation( config, fedOptions, 'node', - cacheFolder + pathToCache ); logger.measure( start, @@ -157,12 +143,6 @@ export async function buildForFederation( ); sharedPackageInfoCache.push(...separatePackageInfoServer); } - - copyCacheToDist( - cacheFolder, - path.join(fedOptions.workspaceRoot, fedOptions.outputPath) - ); - storeCachedMetadata(cacheFolder, cacheChecksum, sharedPackageInfoCache); } const sharedMappingInfo = !artefactInfo @@ -208,7 +188,7 @@ async function bundleSeparate( config: NormalizedFederationConfig, fedOptions: FederationOptions, platform: 'node' | 'browser', - dest: string + pathToCache: string ) { const bundlePromises = Object.entries(separateBrowser).map( async ([key, shared]) => { @@ -222,7 +202,10 @@ async function bundleSeparate( fedOptions, filteredExternals, platform, - dest + { + pathToCache, + metaDataFile: `meta-${platform}-${normalizeFilename(key)}.json`, + } ); } ); diff --git a/libs/native-federation-core/src/lib/core/bundle-shared.ts b/libs/native-federation-core/src/lib/core/bundle-shared.ts index 97143f03..e328cdcf 100644 --- a/libs/native-federation-core/src/lib/core/bundle-shared.ts +++ b/libs/native-federation-core/src/lib/core/bundle-shared.ts @@ -17,6 +17,13 @@ import { isSourceFile, rewriteChunkImports, } from '../utils/rewrite-chunk-imports'; +import { + copyCacheToDist, + getCachedMetadata, + getChecksum, + purgeCacheFolder, + storeCachedMetadata, +} from './get-cache'; export async function bundleShared( sharedBundles: Record, @@ -24,13 +31,34 @@ export async function bundleShared( fedOptions: FederationOptions, externals: string[], platform: 'browser' | 'node' = 'browser', - dest: string + cache: { pathToCache: string; metaDataFile: string } ): Promise> { + const checksum = getChecksum(sharedBundles); const folder = fedOptions.packageJson ? path.dirname(fedOptions.packageJson) : fedOptions.workspaceRoot; - fs.mkdirSync(dest, { recursive: true }); + fs.mkdirSync(cache.pathToCache, { recursive: true }); + + const sharedPackageInfoCache = getCachedMetadata( + cache.pathToCache, + cache.metaDataFile, + checksum + ); + + if (sharedPackageInfoCache) { + logger.info( + `Checksum of ${cache.metaDataFile} matched, Skipped artifact bundling` + ); + copyCacheToDist( + cache.pathToCache, + cache.metaDataFile, + path.join(fedOptions.workspaceRoot, fedOptions.outputPath) + ); + return sharedPackageInfoCache; + } + + purgeCacheFolder(cache.pathToCache, cache.metaDataFile); const inferredPackageInfos = Object.keys(sharedBundles) .filter((packageName) => !sharedBundles[packageName].packageInfo) @@ -66,7 +94,7 @@ export async function bundleShared( path.join(fullOutputPath, ep.outName) ); const entryPoints = allEntryPoints.filter( - (ep) => !fs.existsSync(path.join(dest, ep.outName)) + (ep) => !fs.existsSync(path.join(cache.pathToCache, ep.outName)) ); if (entryPoints.length > 0) { @@ -93,7 +121,7 @@ export async function bundleShared( entryPoints, tsConfigPath: fedOptions.tsConfig, external: [...additionalExternals, ...externals], - outdir: dest, + outdir: cache.pathToCache, mappedPaths: config.sharedMappings, dev: fedOptions.dev, kind: 'shared-package', @@ -103,9 +131,7 @@ export async function bundleShared( }); const cachedFiles = bundleResult.map((br) => path.basename(br.fileName)); - rewriteImports(cachedFiles, dest); - - // copyCacheToOutput(cachedFiles, cachePath, fullOutputPath); + rewriteImports(cachedFiles, cache.pathToCache); } catch (e) { logger.error('Error bundling shared npm package '); if (e instanceof Error) { @@ -156,6 +182,18 @@ export async function bundleShared( addChunksToResult(chunks, result, fedOptions.dev); + storeCachedMetadata(cache.pathToCache, cache.metaDataFile, { + checksum, + externals: result, + files: bundleResult.map((r) => r.fileName.split('/').pop() ?? r.fileName), + }); + + copyCacheToDist( + cache.pathToCache, + cache.metaDataFile, + path.join(fedOptions.workspaceRoot, fedOptions.outputPath) + ); + return result; } diff --git a/libs/native-federation-core/src/lib/core/get-cache.ts b/libs/native-federation-core/src/lib/core/get-cache.ts index 6a9b8944..6b2cfaf3 100644 --- a/libs/native-federation-core/src/lib/core/get-cache.ts +++ b/libs/native-federation-core/src/lib/core/get-cache.ts @@ -3,6 +3,7 @@ import fs from 'fs'; import crypto from 'crypto'; import { NormalizedSharedConfig } from '../config/federation-config'; import { SharedInfo } from '@softarc/native-federation-runtime'; +import { logger } from '../utils/logger'; export const getCachePath = (workspaceRoot: string, project: string) => path.join(workspaceRoot, 'node_modules/.cache/native-federation', project); @@ -26,60 +27,79 @@ export const getChecksum = ( export const getCachedMetadata = ( pathToCache: string, + file: string, checksum: string -): SharedInfo[] => { - const metadataFile = path.join(pathToCache, 'metadata.json'); - if (!fs.existsSync(metadataFile)) return []; +): SharedInfo[] | false => { + const metadataFile = path.join(pathToCache, file); + if (!fs.existsSync(metadataFile)) return false; - const cachedResult: { checksum: string; externals: SharedInfo[] } = - JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); - if (cachedResult.checksum !== checksum) return []; + const cachedResult: { + checksum: string; + externals: SharedInfo[]; + files: string[]; + } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); + if (cachedResult.checksum !== checksum) return false; return cachedResult.externals; }; export const storeCachedMetadata = ( pathToCache: string, - checksum: string, - externals: SharedInfo[] + file: string, + payload: { checksum: string; externals: SharedInfo[]; files: string[] } ) => { fs.writeFileSync( - path.join(pathToCache, 'metadata.json'), - JSON.stringify({ checksum, externals }, undefined, 2), + path.join(pathToCache, file), + JSON.stringify(payload, undefined, 2), 'utf-8' ); }; export const copyCacheToDist = ( pathToCache: string, + file: string, fullOutputPath: string ) => { - fs.readdirSync(pathToCache).forEach((file) => { - if (file === 'metadata.json') return; + const metadataFile = path.join(pathToCache, file); + if (!fs.existsSync(metadataFile)) + throw new Error( + 'Error copying artifacts to dist, metadata file could not be found.' + ); + + const cachedResult: { + checksum: string; + externals: SharedInfo[]; + files: string[]; + } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); + + fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true }); + + cachedResult.files.forEach((file) => { const cachedFile = path.join(pathToCache, file); const distFileName = path.join(fullOutputPath, file); if (fs.existsSync(cachedFile)) { fs.copyFileSync(cachedFile, distFileName); } - console.log(file); }); - fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true }); }; -export const purgeCacheFolder = (pathToCache: string) => { - if (!fs.existsSync(pathToCache)) return; - - try { - fs.rmSync(pathToCache, { recursive: true, force: true }); - } catch (error) { - // Fallback for older Node.js versions or if rmSync fails - try { - fs.rmdirSync(pathToCache, { recursive: true }); - } catch (fallbackError) { - console.warn( - `Failed to purge cache folder: ${pathToCache}`, - fallbackError - ); - } +export const purgeCacheFolder = (pathToCache: string, file: string) => { + const metadataFile = path.join(pathToCache, file); + if (!fs.existsSync(metadataFile)) { + logger.warn( + `Could not purge cache, metadata file '${file}' could not be found.` + ); } + + const cachedResult: { + checksum: string; + externals: SharedInfo[]; + files: string[]; + } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); + + cachedResult.files.forEach((file) => { + const cachedFile = path.join(pathToCache, file); + + if (fs.existsSync(cachedFile)) fs.unlinkSync(cachedFile); + }); }; From ff8aa6c2982118bbc5efaccfb70662539d223456 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Wed, 5 Nov 2025 08:42:18 +0100 Subject: [PATCH 05/12] fix(nf): Linting errors --- .../native-federation-core/src/lib/core/build-for-federation.ts | 2 +- libs/native-federation-core/src/lib/utils/normalize.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 c9eafb53..9fbf5c42 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 @@ -54,7 +54,7 @@ export async function buildForFederation( ? describeExposed(config, fedOptions) : artefactInfo.exposes; - let cacheProjectFolder = normalizeFilename(config.name); + const cacheProjectFolder = normalizeFilename(config.name); if (cacheProjectFolder.length < 1) { logger.warn( "Project name in 'federation.config.js' is empty, defaulting to root cache folder." diff --git a/libs/native-federation-core/src/lib/utils/normalize.ts b/libs/native-federation-core/src/lib/utils/normalize.ts index 738c5fd3..68f3acda 100644 --- a/libs/native-federation-core/src/lib/utils/normalize.ts +++ b/libs/native-federation-core/src/lib/utils/normalize.ts @@ -17,6 +17,6 @@ export function normalize(path: string, trailingSlash?: boolean): string { } export function normalizeFilename(path: string) { - let sanitized = path.replace(/[^A-Za-z0-9]/g, '_'); + const sanitized = path.replace(/[^A-Za-z0-9]/g, '_'); return sanitized.startsWith('_') ? sanitized.slice(1) : sanitized; } From fcd305e0ae419101fdb2cebac67001f4a74c0162 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Thu, 6 Nov 2025 08:33:58 +0100 Subject: [PATCH 06/12] fix(nf): Cache issue where metadata could not be found --- libs/native-federation-core/src/lib/core/bundle-shared.ts | 3 +-- libs/native-federation-core/src/lib/core/get-cache.ts | 6 +++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/libs/native-federation-core/src/lib/core/bundle-shared.ts b/libs/native-federation-core/src/lib/core/bundle-shared.ts index e328cdcf..a861dcb7 100644 --- a/libs/native-federation-core/src/lib/core/bundle-shared.ts +++ b/libs/native-federation-core/src/lib/core/bundle-shared.ts @@ -38,8 +38,6 @@ export async function bundleShared( ? path.dirname(fedOptions.packageJson) : fedOptions.workspaceRoot; - fs.mkdirSync(cache.pathToCache, { recursive: true }); - const sharedPackageInfoCache = getCachedMetadata( cache.pathToCache, cache.metaDataFile, @@ -59,6 +57,7 @@ export async function bundleShared( } purgeCacheFolder(cache.pathToCache, cache.metaDataFile); + fs.mkdirSync(cache.pathToCache, { recursive: true }); const inferredPackageInfos = Object.keys(sharedBundles) .filter((packageName) => !sharedBundles[packageName].packageInfo) diff --git a/libs/native-federation-core/src/lib/core/get-cache.ts b/libs/native-federation-core/src/lib/core/get-cache.ts index 6b2cfaf3..a512a44b 100644 --- a/libs/native-federation-core/src/lib/core/get-cache.ts +++ b/libs/native-federation-core/src/lib/core/get-cache.ts @@ -31,7 +31,7 @@ export const getCachedMetadata = ( checksum: string ): SharedInfo[] | false => { const metadataFile = path.join(pathToCache, file); - if (!fs.existsSync(metadataFile)) return false; + if (!fs.existsSync(pathToCache) || !fs.existsSync(metadataFile)) return false; const cachedResult: { checksum: string; @@ -85,10 +85,14 @@ export const copyCacheToDist = ( export const purgeCacheFolder = (pathToCache: string, file: string) => { const metadataFile = path.join(pathToCache, file); + if (!fs.existsSync(pathToCache)) { + return; + } if (!fs.existsSync(metadataFile)) { logger.warn( `Could not purge cache, metadata file '${file}' could not be found.` ); + return; } const cachedResult: { From 65815d76e633c45be7cecf379c76316c73052aa4 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Fri, 7 Nov 2025 10:06:45 +0100 Subject: [PATCH 07/12] feat(nf): Added hash to metadata files to avoid collision --- .../src/lib/core/build-for-federation.ts | 9 +- .../src/lib/core/bundle-caching.ts | 112 ++++++++++++++++++ .../src/lib/core/bundle-shared.ts | 70 +++-------- .../src/lib/core/get-cache.ts | 109 ----------------- 4 files changed, 134 insertions(+), 166 deletions(-) create mode 100644 libs/native-federation-core/src/lib/core/bundle-caching.ts delete mode 100644 libs/native-federation-core/src/lib/core/get-cache.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 9fbf5c42..4c7886e8 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 @@ -14,7 +14,7 @@ import { FederationOptions } from './federation-options'; import { writeFederationInfo } from './write-federation-info'; import { writeImportMap } from './write-import-map'; import { logger } from '../utils/logger'; -import { getCachePath } from './get-cache'; +import { getCachePath } from './bundle-caching'; import { normalizeFilename } from '../utils/normalize'; export interface BuildParams { @@ -82,7 +82,7 @@ export async function buildForFederation( fedOptions, externals, 'browser', - { pathToCache, metaDataFile: 'meta-browser-shared.json' } + { pathToCache, bundleName: 'browser-shared' } ); logger.measure( @@ -101,7 +101,7 @@ export async function buildForFederation( fedOptions, externals, 'node', - { pathToCache, metaDataFile: 'meta-node-shared.json' } + { pathToCache, bundleName: 'node-shared' } ); logger.measure( start, @@ -204,7 +204,7 @@ async function bundleSeparate( platform, { pathToCache, - metaDataFile: `meta-${platform}-${normalizeFilename(key)}.json`, + bundleName: `${platform}-${normalizeFilename(key)}`, } ); } @@ -223,7 +223,6 @@ function splitShared( const separateServer: Record = {}; for (const key in shared) { - // if (cachedSharedPackages.has(key)) continue; const obj = shared[key]; if (obj.platform === 'node' && obj.build === 'default') { sharedServer[key] = obj; diff --git a/libs/native-federation-core/src/lib/core/bundle-caching.ts b/libs/native-federation-core/src/lib/core/bundle-caching.ts new file mode 100644 index 00000000..08a661ed --- /dev/null +++ b/libs/native-federation-core/src/lib/core/bundle-caching.ts @@ -0,0 +1,112 @@ +import path from 'path'; +import fs from 'fs'; +import crypto from 'crypto'; +import { NormalizedSharedConfig } from '../config/federation-config'; +import { SharedInfo } from '@softarc/native-federation-runtime'; +import { logger } from '../utils/logger'; + +export const getCachePath = (workspaceRoot: string, project: string) => + path.join(workspaceRoot, 'node_modules/.cache/native-federation', project); + +export const getFilename = (checksum: string, title: string) => { + return `${title}-meta-${checksum.substring(0, 10)}.json`; +}; + +export const getChecksum = ( + shared: Record +): string => { + const denseExternals = Object.keys(shared) + .sort() + .reduce((clean, external) => { + return ( + clean + + ':' + + external + + (shared[external].version ? `@${shared[external].version}` : '') + ); + }, 'deps'); + + return crypto.createHash('sha256').update(denseExternals).digest('hex'); +}; + +export const cacheEntry = (pathToCache: string, fileName: string) => ({ + getMetadata: ( + checksum: string + ): + | { + checksum: string; + externals: SharedInfo[]; + files: string[]; + } + | undefined => { + const metadataFile = path.join(pathToCache, fileName); + if (!fs.existsSync(pathToCache) || !fs.existsSync(metadataFile)) + return undefined; + + const cachedResult: { + checksum: string; + externals: SharedInfo[]; + files: string[]; + } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); + if (cachedResult.checksum !== checksum) return undefined; + return cachedResult; + }, + persist: (payload: { + checksum: string; + externals: SharedInfo[]; + files: string[]; + }) => { + fs.writeFileSync( + path.join(pathToCache, fileName), + JSON.stringify(payload), + 'utf-8' + ); + }, + copyFiles: (fullOutputPath: string) => { + const metadataFile = path.join(pathToCache, fileName); + if (!fs.existsSync(metadataFile)) + throw new Error( + 'Error copying artifacts to dist, metadata file could not be found.' + ); + + const cachedResult: { + externals: SharedInfo[]; + files: string[]; + } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); + + fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true }); + + cachedResult.files.forEach((file) => { + const cachedFile = path.join(pathToCache, file); + const distFileName = path.join(fullOutputPath, file); + + if (fs.existsSync(cachedFile)) { + fs.copyFileSync(cachedFile, distFileName); + } + }); + }, + clear: () => { + const metadataFile = path.join(pathToCache, fileName); + if (!fs.existsSync(pathToCache)) { + fs.mkdirSync(pathToCache, { recursive: true }); + return; + } + if (!fs.existsSync(metadataFile)) { + logger.warn( + `Could not purge cache, metadata file '${fileName}' could not be found.` + ); + return; + } + + const cachedResult: { + checksum: string; + externals: SharedInfo[]; + files: string[]; + } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); + + cachedResult.files.forEach((file) => { + const cachedFile = path.join(pathToCache, file); + if (fs.existsSync(cachedFile)) fs.unlinkSync(cachedFile); + }); + }, +}); diff --git a/libs/native-federation-core/src/lib/core/bundle-shared.ts b/libs/native-federation-core/src/lib/core/bundle-shared.ts index a861dcb7..b04ff740 100644 --- a/libs/native-federation-core/src/lib/core/bundle-shared.ts +++ b/libs/native-federation-core/src/lib/core/bundle-shared.ts @@ -17,13 +17,7 @@ import { isSourceFile, rewriteChunkImports, } from '../utils/rewrite-chunk-imports'; -import { - copyCacheToDist, - getCachedMetadata, - getChecksum, - purgeCacheFolder, - storeCachedMetadata, -} from './get-cache'; +import { cacheEntry, getChecksum, getFilename } from './bundle-caching'; export async function bundleShared( sharedBundles: Record, @@ -31,33 +25,30 @@ export async function bundleShared( fedOptions: FederationOptions, externals: string[], platform: 'browser' | 'node' = 'browser', - cache: { pathToCache: string; metaDataFile: string } + cacheOptions: { pathToCache: string; bundleName: string } ): Promise> { const checksum = getChecksum(sharedBundles); const folder = fedOptions.packageJson ? path.dirname(fedOptions.packageJson) : fedOptions.workspaceRoot; - const sharedPackageInfoCache = getCachedMetadata( - cache.pathToCache, - cache.metaDataFile, - checksum + const bundleCache = cacheEntry( + cacheOptions.pathToCache, + getFilename(checksum, cacheOptions.bundleName) ); - if (sharedPackageInfoCache) { + const cacheMetadata = bundleCache.getMetadata(checksum); + if (cacheMetadata) { logger.info( - `Checksum of ${cache.metaDataFile} matched, Skipped artifact bundling` + `Checksum of ${cacheOptions.bundleName} matched, Skipped artifact bundling` ); - copyCacheToDist( - cache.pathToCache, - cache.metaDataFile, + bundleCache.copyFiles( path.join(fedOptions.workspaceRoot, fedOptions.outputPath) ); - return sharedPackageInfoCache; + return cacheMetadata.externals; } - purgeCacheFolder(cache.pathToCache, cache.metaDataFile); - fs.mkdirSync(cache.pathToCache, { recursive: true }); + bundleCache.clear(); const inferredPackageInfos = Object.keys(sharedBundles) .filter((packageName) => !sharedBundles[packageName].packageInfo) @@ -93,7 +84,7 @@ export async function bundleShared( path.join(fullOutputPath, ep.outName) ); const entryPoints = allEntryPoints.filter( - (ep) => !fs.existsSync(path.join(cache.pathToCache, ep.outName)) + (ep) => !fs.existsSync(path.join(cacheOptions.pathToCache, ep.outName)) ); if (entryPoints.length > 0) { @@ -120,7 +111,7 @@ export async function bundleShared( entryPoints, tsConfigPath: fedOptions.tsConfig, external: [...additionalExternals, ...externals], - outdir: cache.pathToCache, + outdir: cacheOptions.pathToCache, mappedPaths: config.sharedMappings, dev: fedOptions.dev, kind: 'shared-package', @@ -130,7 +121,7 @@ export async function bundleShared( }); const cachedFiles = bundleResult.map((br) => path.basename(br.fileName)); - rewriteImports(cachedFiles, cache.pathToCache); + rewriteImports(cachedFiles, cacheOptions.pathToCache); } catch (e) { logger.error('Error bundling shared npm package '); if (e instanceof Error) { @@ -165,12 +156,7 @@ export async function bundleShared( const outFileNames = [...expectedResults]; - const result = buildResult( - packageInfos, - sharedBundles, - outFileNames, - fedOptions - ); + const result = buildResult(packageInfos, sharedBundles, outFileNames); // TODO: Decide whether/when to add .map files const chunks = bundleResult.filter( @@ -181,15 +167,13 @@ export async function bundleShared( addChunksToResult(chunks, result, fedOptions.dev); - storeCachedMetadata(cache.pathToCache, cache.metaDataFile, { + bundleCache.persist({ checksum, externals: result, files: bundleResult.map((r) => r.fileName.split('/').pop() ?? r.fileName), }); - copyCacheToDist( - cache.pathToCache, - cache.metaDataFile, + bundleCache.copyFiles( path.join(fedOptions.workspaceRoot, fedOptions.outputPath) ); @@ -220,28 +204,10 @@ function createOutName( return outName; } -function createCacheFileName( - configState: string, - sharedBundles: Record, - fedOptions: FederationOptions, - cachePath: string, - platform: string -) { - const resultCacheState = configState + JSON.stringify(sharedBundles); - const resultHash = calcHash(resultCacheState); - const dev = fedOptions.dev ? '-dev' : ''; - const resultCacheFile = path.join( - cachePath, - 'result-' + resultHash + '-' + platform + dev + '.json' - ); - return resultCacheFile; -} - function buildResult( packageInfos: PackageInfo[], sharedBundles: Record, - outFileNames: string[], - fedOptions: FederationOptions + outFileNames: string[] ) { return packageInfos.map((pi) => { const shared = sharedBundles[pi.packageName]; diff --git a/libs/native-federation-core/src/lib/core/get-cache.ts b/libs/native-federation-core/src/lib/core/get-cache.ts deleted file mode 100644 index a512a44b..00000000 --- a/libs/native-federation-core/src/lib/core/get-cache.ts +++ /dev/null @@ -1,109 +0,0 @@ -import path from 'path'; -import fs from 'fs'; -import crypto from 'crypto'; -import { NormalizedSharedConfig } from '../config/federation-config'; -import { SharedInfo } from '@softarc/native-federation-runtime'; -import { logger } from '../utils/logger'; - -export const getCachePath = (workspaceRoot: string, project: string) => - path.join(workspaceRoot, 'node_modules/.cache/native-federation', project); - -export const getChecksum = ( - shared: Record -): string => { - const denseExternals = Object.keys(shared) - .sort() - .reduce((clean, external) => { - return ( - clean + - ':' + - external + - (shared[external].version ? `@${shared[external].version}` : '') - ); - }, 'deps'); - - return crypto.createHash('sha256').update(denseExternals).digest('hex'); -}; - -export const getCachedMetadata = ( - pathToCache: string, - file: string, - checksum: string -): SharedInfo[] | false => { - const metadataFile = path.join(pathToCache, file); - if (!fs.existsSync(pathToCache) || !fs.existsSync(metadataFile)) return false; - - const cachedResult: { - checksum: string; - externals: SharedInfo[]; - files: string[]; - } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); - if (cachedResult.checksum !== checksum) return false; - return cachedResult.externals; -}; - -export const storeCachedMetadata = ( - pathToCache: string, - file: string, - payload: { checksum: string; externals: SharedInfo[]; files: string[] } -) => { - fs.writeFileSync( - path.join(pathToCache, file), - JSON.stringify(payload, undefined, 2), - 'utf-8' - ); -}; - -export const copyCacheToDist = ( - pathToCache: string, - file: string, - fullOutputPath: string -) => { - const metadataFile = path.join(pathToCache, file); - if (!fs.existsSync(metadataFile)) - throw new Error( - 'Error copying artifacts to dist, metadata file could not be found.' - ); - - const cachedResult: { - checksum: string; - externals: SharedInfo[]; - files: string[]; - } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); - - fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true }); - - cachedResult.files.forEach((file) => { - const cachedFile = path.join(pathToCache, file); - const distFileName = path.join(fullOutputPath, file); - - if (fs.existsSync(cachedFile)) { - fs.copyFileSync(cachedFile, distFileName); - } - }); -}; - -export const purgeCacheFolder = (pathToCache: string, file: string) => { - const metadataFile = path.join(pathToCache, file); - if (!fs.existsSync(pathToCache)) { - return; - } - if (!fs.existsSync(metadataFile)) { - logger.warn( - `Could not purge cache, metadata file '${file}' could not be found.` - ); - return; - } - - const cachedResult: { - checksum: string; - externals: SharedInfo[]; - files: string[]; - } = JSON.parse(fs.readFileSync(metadataFile, 'utf-8')); - - cachedResult.files.forEach((file) => { - const cachedFile = path.join(pathToCache, file); - - if (fs.existsSync(cachedFile)) fs.unlinkSync(cachedFile); - }); -}; From e3fcb55d8ae22bea44a8a3102e36c7f532e0d1cd Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Wed, 26 Nov 2025 12:55:34 +0100 Subject: [PATCH 08/12] fix: Moved shared externals to separate folder in cache --- libs/native-federation-core/src/lib/core/remove-unused-deps.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 6770d58a..cba98ca2 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 @@ -120,7 +120,7 @@ function getExternalImports(pInfo: PackageInfo, workspaceRoot: string) { const cacheFileName = `${encodedPackageName}-${pInfo.version}.deps.json`; const cachePath = path.join( workspaceRoot, - 'node_modules/.cache/native-federation', + 'node_modules/.cache/native-federation/_externals-metadata', ); const cacheFilePath = path.join(cachePath, cacheFileName); From 4834c6b6123bbe9f491df3a233a96376e65b76a8 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Wed, 26 Nov 2025 13:52:58 +0100 Subject: [PATCH 09/12] fix: consistent package names --- .../src/lib/core/build-for-federation.ts | 48 +++++++++---------- .../src/lib/core/remove-unused-deps.ts | 3 +- .../src/lib/utils/normalize.ts | 4 +- 3 files changed, 28 insertions(+), 27 deletions(-) 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 13d0fc82..8def1ef0 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 @@ -15,7 +15,7 @@ import { writeFederationInfo } from './write-federation-info'; import { writeImportMap } from './write-import-map'; import { logger } from '../utils/logger'; import { getCachePath } from './bundle-caching'; -import { normalizeFilename } from '../utils/normalize'; +import { normalizePackageName } from '../utils/normalize'; import { AbortedError } from '../utils/errors'; export interface BuildParams { @@ -35,7 +35,7 @@ export async function buildForFederation( config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], - buildParams = defaultBuildParams, + buildParams = defaultBuildParams ): Promise { const signal = buildParams.signal; @@ -47,16 +47,16 @@ export async function buildForFederation( config, fedOptions, externals, - signal, + signal ); logger.measure( start, - '[build artifacts] - To bundle all mappings and exposed.', + '[build artifacts] - To bundle all mappings and exposed.' ); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After exposed-and-mappings bundle', + '[buildForFederation] After exposed-and-mappings bundle' ); } @@ -64,16 +64,16 @@ export async function buildForFederation( ? describeExposed(config, fedOptions) : artefactInfo.exposes; - const cacheProjectFolder = normalizeFilename(config.name); + const cacheProjectFolder = normalizePackageName(config.name); if (cacheProjectFolder.length < 1) { logger.warn( - "Project name in 'federation.config.js' is empty, defaulting to root cache folder.", + "Project name in 'federation.config.js' is empty, defaulting to root cache folder." ); } const pathToCache = getCachePath( fedOptions.workspaceRoot, - normalizeFilename(config.name), + normalizePackageName(config.name) ); if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { @@ -92,19 +92,19 @@ export async function buildForFederation( fedOptions, externals, 'browser', - { pathToCache, bundleName: 'browser-shared' }, + { pathToCache, bundleName: 'browser-shared' } ); logger.measure( start, - '[build artifacts] - To bundle all shared browser externals', + '[build artifacts] - To bundle all shared browser externals' ); sharedPackageInfoCache.push(...sharedPackageInfoBrowser); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After shared-browser bundle', + '[buildForFederation] After shared-browser bundle' ); } @@ -116,11 +116,11 @@ export async function buildForFederation( fedOptions, externals, 'node', - { pathToCache, bundleName: 'node-shared' }, + { pathToCache, bundleName: 'node-shared' } ); logger.measure( start, - '[build artifacts] - To bundle all shared node externals', + '[build artifacts] - To bundle all shared node externals' ); sharedPackageInfoCache.push(...sharedPackageInfoServer); @@ -136,17 +136,17 @@ export async function buildForFederation( config, fedOptions, 'browser', - pathToCache, + pathToCache ); logger.measure( start, - '[build artifacts] - To bundle all separate browser externals', + '[build artifacts] - To bundle all separate browser externals' ); sharedPackageInfoCache.push(...separatePackageInfoBrowser); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After separate-browser bundle', + '[buildForFederation] After separate-browser bundle' ); } @@ -158,11 +158,11 @@ export async function buildForFederation( config, fedOptions, 'node', - pathToCache, + pathToCache ); logger.measure( start, - '[build artifacts] - To bundle all separate node externals', + '[build artifacts] - To bundle all separate node externals' ); sharedPackageInfoCache.push(...separatePackageInfoServer); } @@ -214,13 +214,13 @@ async function bundleSeparate( config: NormalizedFederationConfig, fedOptions: FederationOptions, platform: 'node' | 'browser', - pathToCache: string, + pathToCache: string ) { const bundlePromises = Object.entries(separateBrowser).map( async ([key, shared]) => { const packageName = inferPackageFromSecondary(key); const filteredExternals = externals.filter( - (e) => !e.startsWith(packageName), + (e) => !e.startsWith(packageName) ); return bundleShared( { [key]: shared }, @@ -230,10 +230,10 @@ async function bundleSeparate( platform, { pathToCache, - bundleName: `${platform}-${normalizeFilename(key)}`, - }, + bundleName: `${platform}-${normalizePackageName(key)}`, + } ); - }, + } ); const buildResults = await Promise.all(bundlePromises); @@ -241,7 +241,7 @@ async function bundleSeparate( } function splitShared( - shared: Record, + shared: Record ): SplitSharedResult { const sharedServer: Record = {}; const sharedBrowser: Record = {}; 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 cba98ca2..ec819acb 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 @@ -6,6 +6,7 @@ import { NormalizedFederationConfig } from '../config/federation-config'; 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'; export function removeUnusedDeps( config: NormalizedFederationConfig, @@ -116,7 +117,7 @@ function addTransientDeps(packages: Set, workspaceRoot: string) { } function getExternalImports(pInfo: PackageInfo, workspaceRoot: string) { - const encodedPackageName = pInfo.packageName.replace(/[^A-Za-z0-9]/g, '_'); + const encodedPackageName = normalizePackageName(pInfo.packageName); const cacheFileName = `${encodedPackageName}-${pInfo.version}.deps.json`; const cachePath = path.join( workspaceRoot, diff --git a/libs/native-federation-core/src/lib/utils/normalize.ts b/libs/native-federation-core/src/lib/utils/normalize.ts index 68f3acda..4c81b515 100644 --- a/libs/native-federation-core/src/lib/utils/normalize.ts +++ b/libs/native-federation-core/src/lib/utils/normalize.ts @@ -16,7 +16,7 @@ export function normalize(path: string, trailingSlash?: boolean): string { return cand; } -export function normalizeFilename(path: string) { - const sanitized = path.replace(/[^A-Za-z0-9]/g, '_'); +export function normalizePackageName(fileName: string) { + const sanitized = fileName.replace(/[^A-Za-z0-9]/g, '_'); return sanitized.startsWith('_') ? sanitized.slice(1) : sanitized; } From 9a64d7e1cc6e4c26861759c7dceb890be53094b2 Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Wed, 26 Nov 2025 13:53:33 +0100 Subject: [PATCH 10/12] fix: consistent package names --- .../src/lib/core/build-for-federation.ts | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) 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 8def1ef0..47591ad3 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 @@ -35,7 +35,7 @@ export async function buildForFederation( config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], - buildParams = defaultBuildParams + buildParams = defaultBuildParams, ): Promise { const signal = buildParams.signal; @@ -47,16 +47,16 @@ export async function buildForFederation( config, fedOptions, externals, - signal + signal, ); logger.measure( start, - '[build artifacts] - To bundle all mappings and exposed.' + '[build artifacts] - To bundle all mappings and exposed.', ); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After exposed-and-mappings bundle' + '[buildForFederation] After exposed-and-mappings bundle', ); } @@ -67,13 +67,13 @@ export async function buildForFederation( const cacheProjectFolder = normalizePackageName(config.name); if (cacheProjectFolder.length < 1) { logger.warn( - "Project name in 'federation.config.js' is empty, defaulting to root cache folder." + "Project name in 'federation.config.js' is empty, defaulting to root cache folder.", ); } const pathToCache = getCachePath( fedOptions.workspaceRoot, - normalizePackageName(config.name) + normalizePackageName(config.name), ); if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { @@ -92,19 +92,19 @@ export async function buildForFederation( fedOptions, externals, 'browser', - { pathToCache, bundleName: 'browser-shared' } + { pathToCache, bundleName: 'browser-shared' }, ); logger.measure( start, - '[build artifacts] - To bundle all shared browser externals' + '[build artifacts] - To bundle all shared browser externals', ); sharedPackageInfoCache.push(...sharedPackageInfoBrowser); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After shared-browser bundle' + '[buildForFederation] After shared-browser bundle', ); } @@ -116,11 +116,11 @@ export async function buildForFederation( fedOptions, externals, 'node', - { pathToCache, bundleName: 'node-shared' } + { pathToCache, bundleName: 'node-shared' }, ); logger.measure( start, - '[build artifacts] - To bundle all shared node externals' + '[build artifacts] - To bundle all shared node externals', ); sharedPackageInfoCache.push(...sharedPackageInfoServer); @@ -136,17 +136,17 @@ export async function buildForFederation( config, fedOptions, 'browser', - pathToCache + pathToCache, ); logger.measure( start, - '[build artifacts] - To bundle all separate browser externals' + '[build artifacts] - To bundle all separate browser externals', ); sharedPackageInfoCache.push(...separatePackageInfoBrowser); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After separate-browser bundle' + '[buildForFederation] After separate-browser bundle', ); } @@ -158,11 +158,11 @@ export async function buildForFederation( config, fedOptions, 'node', - pathToCache + pathToCache, ); logger.measure( start, - '[build artifacts] - To bundle all separate node externals' + '[build artifacts] - To bundle all separate node externals', ); sharedPackageInfoCache.push(...separatePackageInfoServer); } @@ -214,13 +214,13 @@ async function bundleSeparate( config: NormalizedFederationConfig, fedOptions: FederationOptions, platform: 'node' | 'browser', - pathToCache: string + pathToCache: string, ) { const bundlePromises = Object.entries(separateBrowser).map( async ([key, shared]) => { const packageName = inferPackageFromSecondary(key); const filteredExternals = externals.filter( - (e) => !e.startsWith(packageName) + (e) => !e.startsWith(packageName), ); return bundleShared( { [key]: shared }, @@ -231,9 +231,9 @@ async function bundleSeparate( { pathToCache, bundleName: `${platform}-${normalizePackageName(key)}`, - } + }, ); - } + }, ); const buildResults = await Promise.all(bundlePromises); @@ -241,7 +241,7 @@ async function bundleSeparate( } function splitShared( - shared: Record + shared: Record, ): SplitSharedResult { const sharedServer: Record = {}; const sharedBrowser: Record = {}; From 8b32d0cfab927be01a6db3a23da46e51abde7a0a Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Wed, 26 Nov 2025 17:23:18 +0100 Subject: [PATCH 11/12] fix(nf): Removed checksum from cache to allow consistent removal if checksum doesnt match --- .../src/lib/core/build-for-federation.ts | 42 +++++++++---------- .../src/lib/core/bundle-caching.ts | 11 +++-- .../src/lib/core/bundle-shared.ts | 5 ++- 3 files changed, 31 insertions(+), 27 deletions(-) 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 47591ad3..bcf599c9 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 @@ -35,7 +35,7 @@ export async function buildForFederation( config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], - buildParams = defaultBuildParams, + buildParams = defaultBuildParams ): Promise { const signal = buildParams.signal; @@ -47,16 +47,16 @@ export async function buildForFederation( config, fedOptions, externals, - signal, + signal ); logger.measure( start, - '[build artifacts] - To bundle all mappings and exposed.', + '[build artifacts] - To bundle all mappings and exposed.' ); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After exposed-and-mappings bundle', + '[buildForFederation] After exposed-and-mappings bundle' ); } @@ -67,13 +67,13 @@ export async function buildForFederation( const cacheProjectFolder = normalizePackageName(config.name); if (cacheProjectFolder.length < 1) { logger.warn( - "Project name in 'federation.config.js' is empty, defaulting to root cache folder.", + "Project name in 'federation.config.js' is empty, defaulting to root cache folder (could collide with other projects in the workspace)." ); } const pathToCache = getCachePath( fedOptions.workspaceRoot, - normalizePackageName(config.name), + cacheProjectFolder ); if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { @@ -92,19 +92,19 @@ export async function buildForFederation( fedOptions, externals, 'browser', - { pathToCache, bundleName: 'browser-shared' }, + { pathToCache, bundleName: 'browser-shared' } ); logger.measure( start, - '[build artifacts] - To bundle all shared browser externals', + '[build artifacts] - To bundle all shared browser externals' ); sharedPackageInfoCache.push(...sharedPackageInfoBrowser); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After shared-browser bundle', + '[buildForFederation] After shared-browser bundle' ); } @@ -116,11 +116,11 @@ export async function buildForFederation( fedOptions, externals, 'node', - { pathToCache, bundleName: 'node-shared' }, + { pathToCache, bundleName: 'node-shared' } ); logger.measure( start, - '[build artifacts] - To bundle all shared node externals', + '[build artifacts] - To bundle all shared node externals' ); sharedPackageInfoCache.push(...sharedPackageInfoServer); @@ -136,17 +136,17 @@ export async function buildForFederation( config, fedOptions, 'browser', - pathToCache, + pathToCache ); logger.measure( start, - '[build artifacts] - To bundle all separate browser externals', + '[build artifacts] - To bundle all separate browser externals' ); sharedPackageInfoCache.push(...separatePackageInfoBrowser); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After separate-browser bundle', + '[buildForFederation] After separate-browser bundle' ); } @@ -158,11 +158,11 @@ export async function buildForFederation( config, fedOptions, 'node', - pathToCache, + pathToCache ); logger.measure( start, - '[build artifacts] - To bundle all separate node externals', + '[build artifacts] - To bundle all separate node externals' ); sharedPackageInfoCache.push(...separatePackageInfoServer); } @@ -214,13 +214,13 @@ async function bundleSeparate( config: NormalizedFederationConfig, fedOptions: FederationOptions, platform: 'node' | 'browser', - pathToCache: string, + pathToCache: string ) { const bundlePromises = Object.entries(separateBrowser).map( async ([key, shared]) => { const packageName = inferPackageFromSecondary(key); const filteredExternals = externals.filter( - (e) => !e.startsWith(packageName), + (e) => !e.startsWith(packageName) ); return bundleShared( { [key]: shared }, @@ -231,9 +231,9 @@ async function bundleSeparate( { pathToCache, bundleName: `${platform}-${normalizePackageName(key)}`, - }, + } ); - }, + } ); const buildResults = await Promise.all(bundlePromises); @@ -241,7 +241,7 @@ async function bundleSeparate( } function splitShared( - shared: Record, + shared: Record ): SplitSharedResult { const sharedServer: Record = {}; const sharedBrowser: Record = {}; diff --git a/libs/native-federation-core/src/lib/core/bundle-caching.ts b/libs/native-federation-core/src/lib/core/bundle-caching.ts index f0a727b1..f063567f 100644 --- a/libs/native-federation-core/src/lib/core/bundle-caching.ts +++ b/libs/native-federation-core/src/lib/core/bundle-caching.ts @@ -8,8 +8,8 @@ import { logger } from '../utils/logger'; export const getCachePath = (workspaceRoot: string, project: string) => path.join(workspaceRoot, 'node_modules/.cache/native-federation', project); -export const getFilename = (checksum: string, title: string) => { - return `${title}-meta-${checksum.substring(0, 10)}.json`; +export const getFilename = (title: string) => { + return `${title}.meta.json`; }; export const getChecksum = ( @@ -89,11 +89,12 @@ export const cacheEntry = (pathToCache: string, fileName: string) => ({ const metadataFile = path.join(pathToCache, fileName); if (!fs.existsSync(pathToCache)) { fs.mkdirSync(pathToCache, { recursive: true }); + logger.debug(`Creating cache folder '${pathToCache}' for '${fileName}'.`); return; } if (!fs.existsSync(metadataFile)) { - logger.warn( - `Could not purge cache, metadata file '${fileName}' could not be found.`, + logger.debug( + `Could not purge cached bundle, metadata file '${metadataFile}' does not exist.`, ); return; } @@ -108,5 +109,7 @@ export const cacheEntry = (pathToCache: string, fileName: string) => ({ const cachedFile = path.join(pathToCache, file); if (fs.existsSync(cachedFile)) fs.unlinkSync(cachedFile); }); + + fs.unlinkSync(metadataFile); }, }); diff --git a/libs/native-federation-core/src/lib/core/bundle-shared.ts b/libs/native-federation-core/src/lib/core/bundle-shared.ts index df4e3e1d..37fed5ef 100644 --- a/libs/native-federation-core/src/lib/core/bundle-shared.ts +++ b/libs/native-federation-core/src/lib/core/bundle-shared.ts @@ -34,12 +34,12 @@ export async function bundleShared( const bundleCache = cacheEntry( cacheOptions.pathToCache, - getFilename(checksum, cacheOptions.bundleName), + getFilename(cacheOptions.bundleName), ); const cacheMetadata = bundleCache.getMetadata(checksum); if (cacheMetadata) { - logger.info( + logger.debug( `Checksum of ${cacheOptions.bundleName} matched, Skipped artifact bundling`, ); bundleCache.copyFiles( @@ -48,6 +48,7 @@ export async function bundleShared( return cacheMetadata.externals; } + // Delete older packages if checksum didnt match bundleCache.clear(); const inferredPackageInfos = Object.keys(sharedBundles) From c4c5d643e8096c0afa8898fe3316531628a83f8e Mon Sep 17 00:00:00 2001 From: Aukevanoost Date: Wed, 26 Nov 2025 17:23:51 +0100 Subject: [PATCH 12/12] format --- .../src/lib/core/build-for-federation.ts | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) 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 bcf599c9..5e1d2e4c 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 @@ -35,7 +35,7 @@ export async function buildForFederation( config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], - buildParams = defaultBuildParams + buildParams = defaultBuildParams, ): Promise { const signal = buildParams.signal; @@ -47,16 +47,16 @@ export async function buildForFederation( config, fedOptions, externals, - signal + signal, ); logger.measure( start, - '[build artifacts] - To bundle all mappings and exposed.' + '[build artifacts] - To bundle all mappings and exposed.', ); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After exposed-and-mappings bundle' + '[buildForFederation] After exposed-and-mappings bundle', ); } @@ -67,13 +67,13 @@ export async function buildForFederation( const cacheProjectFolder = normalizePackageName(config.name); if (cacheProjectFolder.length < 1) { logger.warn( - "Project name in 'federation.config.js' is empty, defaulting to root cache folder (could collide with other projects in the workspace)." + "Project name in 'federation.config.js' is empty, defaulting to root cache folder (could collide with other projects in the workspace).", ); } const pathToCache = getCachePath( fedOptions.workspaceRoot, - cacheProjectFolder + cacheProjectFolder, ); if (!buildParams.skipShared && sharedPackageInfoCache.length > 0) { @@ -92,19 +92,19 @@ export async function buildForFederation( fedOptions, externals, 'browser', - { pathToCache, bundleName: 'browser-shared' } + { pathToCache, bundleName: 'browser-shared' }, ); logger.measure( start, - '[build artifacts] - To bundle all shared browser externals' + '[build artifacts] - To bundle all shared browser externals', ); sharedPackageInfoCache.push(...sharedPackageInfoBrowser); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After shared-browser bundle' + '[buildForFederation] After shared-browser bundle', ); } @@ -116,11 +116,11 @@ export async function buildForFederation( fedOptions, externals, 'node', - { pathToCache, bundleName: 'node-shared' } + { pathToCache, bundleName: 'node-shared' }, ); logger.measure( start, - '[build artifacts] - To bundle all shared node externals' + '[build artifacts] - To bundle all shared node externals', ); sharedPackageInfoCache.push(...sharedPackageInfoServer); @@ -136,17 +136,17 @@ export async function buildForFederation( config, fedOptions, 'browser', - pathToCache + pathToCache, ); logger.measure( start, - '[build artifacts] - To bundle all separate browser externals' + '[build artifacts] - To bundle all separate browser externals', ); sharedPackageInfoCache.push(...separatePackageInfoBrowser); if (signal?.aborted) throw new AbortedError( - '[buildForFederation] After separate-browser bundle' + '[buildForFederation] After separate-browser bundle', ); } @@ -158,11 +158,11 @@ export async function buildForFederation( config, fedOptions, 'node', - pathToCache + pathToCache, ); logger.measure( start, - '[build artifacts] - To bundle all separate node externals' + '[build artifacts] - To bundle all separate node externals', ); sharedPackageInfoCache.push(...separatePackageInfoServer); } @@ -214,13 +214,13 @@ async function bundleSeparate( config: NormalizedFederationConfig, fedOptions: FederationOptions, platform: 'node' | 'browser', - pathToCache: string + pathToCache: string, ) { const bundlePromises = Object.entries(separateBrowser).map( async ([key, shared]) => { const packageName = inferPackageFromSecondary(key); const filteredExternals = externals.filter( - (e) => !e.startsWith(packageName) + (e) => !e.startsWith(packageName), ); return bundleShared( { [key]: shared }, @@ -231,9 +231,9 @@ async function bundleSeparate( { pathToCache, bundleName: `${platform}-${normalizePackageName(key)}`, - } + }, ); - } + }, ); const buildResults = await Promise.all(bundlePromises); @@ -241,7 +241,7 @@ async function bundleSeparate( } function splitShared( - shared: Record + shared: Record, ): SplitSharedResult { const sharedServer: Record = {}; const sharedBrowser: Record = {};