fix(js): write typescript plugin cache files atomically (#31390)

## Current Behavior

The `@nx/js/typescript` plugin writes cache files in a non-atomic
fashion, which can result in corrupted or empty files being written.

## Expected Behavior

The `@nx/js/typescript` plugin should write cache files atomically, and
they should not be corrupt. It should also retry a limited amount
attempts to account for temporary file locks.

## Related Issue(s)

Fixes #30239 
Fixes #31187
This commit is contained in:
Leosvel Pérez Espinosa 2025-05-30 22:41:45 +02:00 committed by GitHub
parent efc9c8d12a
commit 7595d8a5e1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -17,7 +17,14 @@ import {
type TargetConfiguration,
} from '@nx/devkit';
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
import {
existsSync,
readdirSync,
readFileSync,
renameSync,
statSync,
unlinkSync,
} from 'node:fs';
import {
basename,
dirname,
@ -122,7 +129,20 @@ function readTsConfigCacheData(): Record<string, TsconfigCacheData> {
}
function writeToCache<T extends object>(cachePath: string, data: T) {
writeJsonFile(cachePath, data, { spaces: 0 });
const maxAttempts = 5;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const unique = (Math.random().toString(16) + '00000000').slice(2, 10);
const tempPath = `${cachePath}.${process.pid}.${unique}.tmp`;
try {
writeJsonFile(tempPath, data, { spaces: 0 });
renameSync(tempPath, cachePath);
return;
} catch {
try {
unlinkSync(tempPath);
} catch {}
}
}
}
function writeTsConfigCache(data: Record<string, TsconfigCacheData>) {
writeToCache(TS_CONFIG_CACHE_PATH, {