diff --git a/changelog.md b/changelog.md index ff16a22..03ffb3a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2026-03-05 - 4.2.0 - feat(mod_compiler) +add diagnostic interception of fs operations to detect and report unexpected file system changes in previously compiled output directories during compilation + +- Wraps fs.unlinkSync, fs.rmSync, fs.rmdirSync, fs.renameSync and fs.writeFileSync to record operations targeting other successful output directories during a compile. +- Enabled only when there are previously compiled output dirs and when not running in quiet or JSON mode; original fs methods are restored after compilation. +- Logs up to 30 intercepted operations and prints a summary count if any ops were observed; intercepted calls still perform the original fs action (diagnostic-only). +- No functional change to compilation behavior beyond additional diagnostic reporting. + ## 2026-03-05 - 4.1.26 - fix(compiler) fsync output directories after unpack to avoid XFS delayed logging causing corrupt or invisible directory entries during subsequent TypeScript emits diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 25fae25..621ee5f 100644 --- a/ts/00_commitinfo_data.ts +++ b/ts/00_commitinfo_data.ts @@ -3,6 +3,6 @@ */ export const commitinfo = { name: '@git.zone/tsbuild', - version: '4.1.26', + version: '4.2.0', description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.' } diff --git a/ts/mod_compiler/classes.tscompiler.ts b/ts/mod_compiler/classes.tscompiler.ts index dafb279..ff35196 100644 --- a/ts/mod_compiler/classes.tscompiler.ts +++ b/ts/mod_compiler/classes.tscompiler.ts @@ -380,9 +380,69 @@ export class TsCompiler { fileCount: absoluteFiles.length, }; + // Diagnostic: intercept fs operations during compilation to detect + // any unexpected deletions in previously compiled output directories + const watchedDirs = successfulOutputDirs.filter(d => d !== destDir); + const origUnlink = fs.unlinkSync; + const origRm = fs.rmSync; + const origRmdir = fs.rmdirSync; + const origRename = fs.renameSync; + const origWriteFile = fs.writeFileSync; + let interceptedOps: string[] = []; + if (watchedDirs.length > 0 && !isQuiet && !isJson) { + (fs as any).unlinkSync = (p: string, ...args: any[]) => { + if (watchedDirs.some(d => String(p).startsWith(d + '/'))) { + interceptedOps.push(`unlink: ${p}`); + } + return origUnlink.call(fs, p, ...args); + }; + (fs as any).rmSync = (p: string, ...args: any[]) => { + if (watchedDirs.some(d => String(p).startsWith(d + '/'))) { + interceptedOps.push(`rm: ${p}`); + } + return origRm.call(fs, p, ...args); + }; + (fs as any).rmdirSync = (p: string, ...args: any[]) => { + if (watchedDirs.some(d => String(p).startsWith(d + '/'))) { + interceptedOps.push(`rmdir: ${p}`); + } + return origRmdir.call(fs, p, ...args); + }; + (fs as any).renameSync = (src: string, dest: string, ...args: any[]) => { + if (watchedDirs.some(d => String(src).startsWith(d + '/') || String(dest).startsWith(d + '/'))) { + interceptedOps.push(`rename: ${src} → ${dest}`); + } + return origRename.call(fs, src, dest, ...args); + }; + (fs as any).writeFileSync = (p: string, ...args: any[]) => { + if (watchedDirs.some(d => String(p).startsWith(d + '/'))) { + interceptedOps.push(`write: ${p}`); + } + return origWriteFile.call(fs, p, ...args); + }; + } + const result = await this.compileFiles(absoluteFiles, options, taskInfo); emittedFiles.push(...result.emittedFiles); errorSummaries.push(result.errorSummary); + + // Restore original fs methods and report any intercepted operations + if (watchedDirs.length > 0 && !isQuiet && !isJson) { + (fs as any).unlinkSync = origUnlink; + (fs as any).rmSync = origRm; + (fs as any).rmdirSync = origRmdir; + (fs as any).renameSync = origRename; + (fs as any).writeFileSync = origWriteFile; + if (interceptedOps.length > 0) { + console.log(` ⚠️ [diag] ${interceptedOps.length} ops on previous output dirs during compilation:`); + for (const op of interceptedOps.slice(0, 30)) { + console.log(` ${op.replace(this.cwd + '/', '')}`); + } + if (interceptedOps.length > 30) { + console.log(` ... and ${interceptedOps.length - 30} more`); + } + } + } diagSnap('post-compile'); // Diagnostic: log emitted files that went to unexpected directories