diff --git a/changelog.md b/changelog.md index 7274a74..09c1023 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,13 @@ # Changelog +## 2026-03-05 - 4.1.11 - fix(mod_compiler) +flush directory entries before unpack to avoid XFS delayed-log causing partial readdir results + +- Add fs import and call child_process.execSync('sync') before unpacking compiled output to force kernel-level sync +- Add syncDirectoryTree(dir) to recursively open and fsync directory file descriptors and traverse subdirectories +- Call syncDirectoryTree on the destination directory before performing unpack to ensure TypeScript writeFileSync entries are committed (prevents partial readdir results on XFS) +- Errors during directory sync are ignored to avoid breaking normal flow + ## 2026-03-05 - 4.1.10 - fix(unpack) use atomic renames to flatten nested output and make unpacking more reliable diff --git a/ts/00_commitinfo_data.ts b/ts/00_commitinfo_data.ts index 24dfe9d..8982b73 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.10', + version: '4.1.11', 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 8fe5d5a..6f6ed80 100644 --- a/ts/mod_compiler/classes.tscompiler.ts +++ b/ts/mod_compiler/classes.tscompiler.ts @@ -1,5 +1,6 @@ import type { CompilerOptions, Diagnostic, Program } from 'typescript'; import typescript from 'typescript'; +import * as fs from 'fs'; import * as smartdelay from '@push.rocks/smartdelay'; import * as smartpromise from '@push.rocks/smartpromise'; import * as smartpath from '@push.rocks/smartpath'; @@ -367,6 +368,12 @@ export class TsCompiler { // Perform unpack if compilation succeeded if (result.errorSummary.totalErrors === 0) { + // Force XFS to commit all pending directory entries before unpacking. + // TypeScript's writeFileSync creates entries that may reside in XFS's + // delayed log. Without sync, readdir can return partial results. + require('child_process').execSync('sync'); + this.syncDirectoryTree(destDir); + try { await performUnpack(pattern, destDir, this.cwd); } catch (unpackErr: any) { @@ -495,6 +502,27 @@ export class TsCompiler { return success; } + /** + * Recursively fsync all directories in a tree. + * Forces XFS to commit pending directory entries from its log. + */ + private syncDirectoryTree(dirPath: string): void { + try { + const fd = fs.openSync(dirPath, 'r'); + fs.fsyncSync(fd); + fs.closeSync(fd); + + const entries = fs.readdirSync(dirPath, { withFileTypes: true }); + for (const entry of entries) { + if (entry.isDirectory()) { + this.syncDirectoryTree(require('path').join(dirPath, entry.name)); + } + } + } catch { + // Ignore errors (directory may not exist) + } + } + /** * Merge multiple error summaries into one */