Skip to content

Commit 65815d7

Browse files
committed
feat(nf): Added hash to metadata files to avoid collision
1 parent fcd305e commit 65815d7

4 files changed

Lines changed: 134 additions & 166 deletions

File tree

libs/native-federation-core/src/lib/core/build-for-federation.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { FederationOptions } from './federation-options';
1414
import { writeFederationInfo } from './write-federation-info';
1515
import { writeImportMap } from './write-import-map';
1616
import { logger } from '../utils/logger';
17-
import { getCachePath } from './get-cache';
17+
import { getCachePath } from './bundle-caching';
1818
import { normalizeFilename } from '../utils/normalize';
1919

2020
export interface BuildParams {
@@ -82,7 +82,7 @@ export async function buildForFederation(
8282
fedOptions,
8383
externals,
8484
'browser',
85-
{ pathToCache, metaDataFile: 'meta-browser-shared.json' }
85+
{ pathToCache, bundleName: 'browser-shared' }
8686
);
8787

8888
logger.measure(
@@ -101,7 +101,7 @@ export async function buildForFederation(
101101
fedOptions,
102102
externals,
103103
'node',
104-
{ pathToCache, metaDataFile: 'meta-node-shared.json' }
104+
{ pathToCache, bundleName: 'node-shared' }
105105
);
106106
logger.measure(
107107
start,
@@ -204,7 +204,7 @@ async function bundleSeparate(
204204
platform,
205205
{
206206
pathToCache,
207-
metaDataFile: `meta-${platform}-${normalizeFilename(key)}.json`,
207+
bundleName: `${platform}-${normalizeFilename(key)}`,
208208
}
209209
);
210210
}
@@ -223,7 +223,6 @@ function splitShared(
223223
const separateServer: Record<string, NormalizedSharedConfig> = {};
224224

225225
for (const key in shared) {
226-
// if (cachedSharedPackages.has(key)) continue;
227226
const obj = shared[key];
228227
if (obj.platform === 'node' && obj.build === 'default') {
229228
sharedServer[key] = obj;
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import path from 'path';
2+
import fs from 'fs';
3+
import crypto from 'crypto';
4+
import { NormalizedSharedConfig } from '../config/federation-config';
5+
import { SharedInfo } from '@softarc/native-federation-runtime';
6+
import { logger } from '../utils/logger';
7+
8+
export const getCachePath = (workspaceRoot: string, project: string) =>
9+
path.join(workspaceRoot, 'node_modules/.cache/native-federation', project);
10+
11+
export const getFilename = (checksum: string, title: string) => {
12+
return `${title}-meta-${checksum.substring(0, 10)}.json`;
13+
};
14+
15+
export const getChecksum = (
16+
shared: Record<string, NormalizedSharedConfig>
17+
): string => {
18+
const denseExternals = Object.keys(shared)
19+
.sort()
20+
.reduce((clean, external) => {
21+
return (
22+
clean +
23+
':' +
24+
external +
25+
(shared[external].version ? `@${shared[external].version}` : '')
26+
);
27+
}, 'deps');
28+
29+
return crypto.createHash('sha256').update(denseExternals).digest('hex');
30+
};
31+
32+
export const cacheEntry = (pathToCache: string, fileName: string) => ({
33+
getMetadata: (
34+
checksum: string
35+
):
36+
| {
37+
checksum: string;
38+
externals: SharedInfo[];
39+
files: string[];
40+
}
41+
| undefined => {
42+
const metadataFile = path.join(pathToCache, fileName);
43+
if (!fs.existsSync(pathToCache) || !fs.existsSync(metadataFile))
44+
return undefined;
45+
46+
const cachedResult: {
47+
checksum: string;
48+
externals: SharedInfo[];
49+
files: string[];
50+
} = JSON.parse(fs.readFileSync(metadataFile, 'utf-8'));
51+
if (cachedResult.checksum !== checksum) return undefined;
52+
return cachedResult;
53+
},
54+
persist: (payload: {
55+
checksum: string;
56+
externals: SharedInfo[];
57+
files: string[];
58+
}) => {
59+
fs.writeFileSync(
60+
path.join(pathToCache, fileName),
61+
JSON.stringify(payload),
62+
'utf-8'
63+
);
64+
},
65+
copyFiles: (fullOutputPath: string) => {
66+
const metadataFile = path.join(pathToCache, fileName);
67+
if (!fs.existsSync(metadataFile))
68+
throw new Error(
69+
'Error copying artifacts to dist, metadata file could not be found.'
70+
);
71+
72+
const cachedResult: {
73+
externals: SharedInfo[];
74+
files: string[];
75+
} = JSON.parse(fs.readFileSync(metadataFile, 'utf-8'));
76+
77+
fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true });
78+
79+
cachedResult.files.forEach((file) => {
80+
const cachedFile = path.join(pathToCache, file);
81+
const distFileName = path.join(fullOutputPath, file);
82+
83+
if (fs.existsSync(cachedFile)) {
84+
fs.copyFileSync(cachedFile, distFileName);
85+
}
86+
});
87+
},
88+
clear: () => {
89+
const metadataFile = path.join(pathToCache, fileName);
90+
if (!fs.existsSync(pathToCache)) {
91+
fs.mkdirSync(pathToCache, { recursive: true });
92+
return;
93+
}
94+
if (!fs.existsSync(metadataFile)) {
95+
logger.warn(
96+
`Could not purge cache, metadata file '${fileName}' could not be found.`
97+
);
98+
return;
99+
}
100+
101+
const cachedResult: {
102+
checksum: string;
103+
externals: SharedInfo[];
104+
files: string[];
105+
} = JSON.parse(fs.readFileSync(metadataFile, 'utf-8'));
106+
107+
cachedResult.files.forEach((file) => {
108+
const cachedFile = path.join(pathToCache, file);
109+
if (fs.existsSync(cachedFile)) fs.unlinkSync(cachedFile);
110+
});
111+
},
112+
});

libs/native-federation-core/src/lib/core/bundle-shared.ts

Lines changed: 18 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -17,47 +17,38 @@ import {
1717
isSourceFile,
1818
rewriteChunkImports,
1919
} from '../utils/rewrite-chunk-imports';
20-
import {
21-
copyCacheToDist,
22-
getCachedMetadata,
23-
getChecksum,
24-
purgeCacheFolder,
25-
storeCachedMetadata,
26-
} from './get-cache';
20+
import { cacheEntry, getChecksum, getFilename } from './bundle-caching';
2721

2822
export async function bundleShared(
2923
sharedBundles: Record<string, NormalizedSharedConfig>,
3024
config: NormalizedFederationConfig,
3125
fedOptions: FederationOptions,
3226
externals: string[],
3327
platform: 'browser' | 'node' = 'browser',
34-
cache: { pathToCache: string; metaDataFile: string }
28+
cacheOptions: { pathToCache: string; bundleName: string }
3529
): Promise<Array<SharedInfo>> {
3630
const checksum = getChecksum(sharedBundles);
3731
const folder = fedOptions.packageJson
3832
? path.dirname(fedOptions.packageJson)
3933
: fedOptions.workspaceRoot;
4034

41-
const sharedPackageInfoCache = getCachedMetadata(
42-
cache.pathToCache,
43-
cache.metaDataFile,
44-
checksum
35+
const bundleCache = cacheEntry(
36+
cacheOptions.pathToCache,
37+
getFilename(checksum, cacheOptions.bundleName)
4538
);
4639

47-
if (sharedPackageInfoCache) {
40+
const cacheMetadata = bundleCache.getMetadata(checksum);
41+
if (cacheMetadata) {
4842
logger.info(
49-
`Checksum of ${cache.metaDataFile} matched, Skipped artifact bundling`
43+
`Checksum of ${cacheOptions.bundleName} matched, Skipped artifact bundling`
5044
);
51-
copyCacheToDist(
52-
cache.pathToCache,
53-
cache.metaDataFile,
45+
bundleCache.copyFiles(
5446
path.join(fedOptions.workspaceRoot, fedOptions.outputPath)
5547
);
56-
return sharedPackageInfoCache;
48+
return cacheMetadata.externals;
5749
}
5850

59-
purgeCacheFolder(cache.pathToCache, cache.metaDataFile);
60-
fs.mkdirSync(cache.pathToCache, { recursive: true });
51+
bundleCache.clear();
6152

6253
const inferredPackageInfos = Object.keys(sharedBundles)
6354
.filter((packageName) => !sharedBundles[packageName].packageInfo)
@@ -93,7 +84,7 @@ export async function bundleShared(
9384
path.join(fullOutputPath, ep.outName)
9485
);
9586
const entryPoints = allEntryPoints.filter(
96-
(ep) => !fs.existsSync(path.join(cache.pathToCache, ep.outName))
87+
(ep) => !fs.existsSync(path.join(cacheOptions.pathToCache, ep.outName))
9788
);
9889

9990
if (entryPoints.length > 0) {
@@ -120,7 +111,7 @@ export async function bundleShared(
120111
entryPoints,
121112
tsConfigPath: fedOptions.tsConfig,
122113
external: [...additionalExternals, ...externals],
123-
outdir: cache.pathToCache,
114+
outdir: cacheOptions.pathToCache,
124115
mappedPaths: config.sharedMappings,
125116
dev: fedOptions.dev,
126117
kind: 'shared-package',
@@ -130,7 +121,7 @@ export async function bundleShared(
130121
});
131122

132123
const cachedFiles = bundleResult.map((br) => path.basename(br.fileName));
133-
rewriteImports(cachedFiles, cache.pathToCache);
124+
rewriteImports(cachedFiles, cacheOptions.pathToCache);
134125
} catch (e) {
135126
logger.error('Error bundling shared npm package ');
136127
if (e instanceof Error) {
@@ -165,12 +156,7 @@ export async function bundleShared(
165156

166157
const outFileNames = [...expectedResults];
167158

168-
const result = buildResult(
169-
packageInfos,
170-
sharedBundles,
171-
outFileNames,
172-
fedOptions
173-
);
159+
const result = buildResult(packageInfos, sharedBundles, outFileNames);
174160

175161
// TODO: Decide whether/when to add .map files
176162
const chunks = bundleResult.filter(
@@ -181,15 +167,13 @@ export async function bundleShared(
181167

182168
addChunksToResult(chunks, result, fedOptions.dev);
183169

184-
storeCachedMetadata(cache.pathToCache, cache.metaDataFile, {
170+
bundleCache.persist({
185171
checksum,
186172
externals: result,
187173
files: bundleResult.map((r) => r.fileName.split('/').pop() ?? r.fileName),
188174
});
189175

190-
copyCacheToDist(
191-
cache.pathToCache,
192-
cache.metaDataFile,
176+
bundleCache.copyFiles(
193177
path.join(fedOptions.workspaceRoot, fedOptions.outputPath)
194178
);
195179

@@ -220,28 +204,10 @@ function createOutName(
220204
return outName;
221205
}
222206

223-
function createCacheFileName(
224-
configState: string,
225-
sharedBundles: Record<string, NormalizedSharedConfig>,
226-
fedOptions: FederationOptions,
227-
cachePath: string,
228-
platform: string
229-
) {
230-
const resultCacheState = configState + JSON.stringify(sharedBundles);
231-
const resultHash = calcHash(resultCacheState);
232-
const dev = fedOptions.dev ? '-dev' : '';
233-
const resultCacheFile = path.join(
234-
cachePath,
235-
'result-' + resultHash + '-' + platform + dev + '.json'
236-
);
237-
return resultCacheFile;
238-
}
239-
240207
function buildResult(
241208
packageInfos: PackageInfo[],
242209
sharedBundles: Record<string, NormalizedSharedConfig>,
243-
outFileNames: string[],
244-
fedOptions: FederationOptions
210+
outFileNames: string[]
245211
) {
246212
return packageInfos.map((pi) => {
247213
const shared = sharedBundles[pi.packageName];

0 commit comments

Comments
 (0)