fix(js): ensure the tsconfig files cache is correctly busted when implementation changes (#30689)

## Current Behavior

The `@nx/js/typescript` plugin sometimes throws an error due to a
mismatch between the cached information for the tsconfig files and a
newer implementation.

## Expected Behavior

The `@nx/js/typescript` plugin should correctly invalidate the cached
tsconfig files when the implementation changes.

## Related Issue(s)

Fixes #
This commit is contained in:
Leosvel Pérez Espinosa 2025-04-12 00:12:22 +02:00 committed by GitHub
parent f3013ccafe
commit ce41dedf01
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -82,12 +82,20 @@ type TsconfigCacheData = {
hash: string; hash: string;
extendedFilesHash: string; extendedFilesHash: string;
}; };
type TsconfigCache = {
version: number;
data: Record<string, TsconfigCacheData>;
};
let ts: typeof import('typescript'); let ts: typeof import('typescript');
const pmc = getPackageManagerCommand(); const pmc = getPackageManagerCommand();
let tsConfigCache: Record<string, TsconfigCacheData>; const TSCONFIG_CACHE_VERSION = 1;
const tsConfigCachePath = join(workspaceDataDirectory, 'tsconfig-files.hash'); const TS_CONFIG_CACHE_PATH = join(
workspaceDataDirectory,
'tsconfig-files.hash'
);
let tsConfigCacheData: Record<string, TsconfigCacheData>;
let cache: { let cache: {
fileHashes: Record<string, string>; fileHashes: Record<string, string>;
rawFiles: Record<string, string>; rawFiles: Record<string, string>;
@ -103,10 +111,25 @@ function readFromCache<T extends object>(cachePath: string): T {
return {} as T; return {} as T;
} }
} }
function readTsConfigCacheData(): Record<string, TsconfigCacheData> {
const cache = readFromCache<TsconfigCache>(TS_CONFIG_CACHE_PATH);
if (cache.version !== TSCONFIG_CACHE_VERSION) {
return {};
}
return cache.data;
}
function writeToCache<T extends object>(cachePath: string, data: T) { function writeToCache<T extends object>(cachePath: string, data: T) {
writeJsonFile(cachePath, data, { spaces: 0 }); writeJsonFile(cachePath, data, { spaces: 0 });
} }
function writeTsConfigCache(data: Record<string, TsconfigCacheData>) {
writeToCache(TS_CONFIG_CACHE_PATH, {
version: TSCONFIG_CACHE_VERSION,
data,
});
}
/** /**
* @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'. * @deprecated The 'createDependencies' function is now a no-op. This functionality is included in 'createNodesV2'.
@ -175,9 +198,8 @@ export const createNodesV2: CreateNodesV2<TscPluginOptions> = [
); );
} finally { } finally {
writeToCache(targetsCachePath, targetsCache); writeToCache(targetsCachePath, targetsCache);
writeToCache( writeTsConfigCache(
tsConfigCachePath, toRelativePaths(tsConfigCacheData, context.workspaceRoot)
toRelativePaths(tsConfigCache, context.workspaceRoot)
); );
} }
}, },
@ -208,9 +230,8 @@ export const createNodes: CreateNodes<TscPluginOptions> = [
context context
); );
writeToCache( writeTsConfigCache(
tsConfigCachePath, toRelativePaths(tsConfigCacheData, context.workspaceRoot)
toRelativePaths(tsConfigCache, context.workspaceRoot)
); );
return { return {
@ -1097,8 +1118,8 @@ function retrieveTsConfigFromCache(
// we don't need to check the hash if it's in the cache, because we've already // we don't need to check the hash if it's in the cache, because we've already
// checked it when we initially populated the cache // checked it when we initially populated the cache
return tsConfigCache[relativePath] return tsConfigCacheData[relativePath]
? tsConfigCache[relativePath].data ? tsConfigCacheData[relativePath].data
: readTsConfigAndCache(tsConfigPath, workspaceRoot); : readTsConfigAndCache(tsConfigPath, workspaceRoot);
} }
@ -1106,10 +1127,7 @@ function initializeTsConfigCache(
configFilePaths: readonly string[], configFilePaths: readonly string[],
workspaceRoot: string workspaceRoot: string
): void { ): void {
tsConfigCache = toAbsolutePaths( tsConfigCacheData = toAbsolutePaths(readTsConfigCacheData(), workspaceRoot);
readFromCache<Record<string, TsconfigCacheData>>(tsConfigCachePath),
workspaceRoot
);
// ensure hashes are checked and the cache is invalidated and populated as needed // ensure hashes are checked and the cache is invalidated and populated as needed
for (const configFilePath of configFilePaths) { for (const configFilePath of configFilePaths) {
@ -1127,15 +1145,17 @@ function readTsConfigAndCache(
let extendedFilesHash: string; let extendedFilesHash: string;
if ( if (
tsConfigCache[relativePath] && tsConfigCacheData[relativePath] &&
tsConfigCache[relativePath].hash === hash tsConfigCacheData[relativePath].hash === hash
) { ) {
extendedFilesHash = getExtendedFilesHash( extendedFilesHash = getExtendedFilesHash(
tsConfigCache[relativePath].data.extendedConfigFiles, tsConfigCacheData[relativePath].data.extendedConfigFiles,
workspaceRoot workspaceRoot
); );
if (tsConfigCache[relativePath].extendedFilesHash === extendedFilesHash) { if (
return tsConfigCache[relativePath].data; tsConfigCacheData[relativePath].extendedFilesHash === extendedFilesHash
) {
return tsConfigCacheData[relativePath].data;
} }
} }
@ -1161,7 +1181,7 @@ function readTsConfigAndCache(
workspaceRoot workspaceRoot
); );
tsConfigCache[relativePath] = { tsConfigCacheData[relativePath] = {
data: { data: {
options: tsConfig.options, options: tsConfig.options,
projectReferences: tsConfig.projectReferences, projectReferences: tsConfig.projectReferences,
@ -1172,7 +1192,7 @@ function readTsConfigAndCache(
extendedFilesHash, extendedFilesHash,
}; };
return tsConfigCache[relativePath].data; return tsConfigCacheData[relativePath].data;
} }
function getExtendedFilesHash( function getExtendedFilesHash(