Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 83e0e9b5dd | |||
| 094f9df55f | |||
| a6a006aaec | |||
| 9477875c1d | |||
| 2b73f3d582 | |||
| 0ffdcf852f | |||
| f8f20be4f4 |
19
changelog.md
19
changelog.md
@@ -1,5 +1,24 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2026-03-05 - 4.2.6 - fix(meta)
|
||||||
|
no changes
|
||||||
|
|
||||||
|
- Current package version: 4.2.5
|
||||||
|
- No code or file changes detected in this commit; no release required
|
||||||
|
|
||||||
|
## 2026-03-05 - 4.2.5 - fix(compiler)
|
||||||
|
yield to the event loop after TypeScript emit to allow pending microtasks and I/O to settle before reading or modifying the output directory
|
||||||
|
|
||||||
|
- Added await new Promise(resolve => process.nextTick(resolve)) immediately after program.emit()
|
||||||
|
- Prevents race conditions by allowing libuv write completions and other deferred callbacks to complete before accessing the output directory
|
||||||
|
- File changed: ts/mod_compiler/classes.tscompiler.ts
|
||||||
|
|
||||||
|
## 2026-03-05 - 4.2.4 - fix(fshelpers)
|
||||||
|
remove outdated comment about using synchronous rm to avoid XFS metadata corruption
|
||||||
|
|
||||||
|
- Comment-only change in ts/mod_fs/classes.fshelpers.ts; no runtime or API behavior changes
|
||||||
|
- Bump patch version from 4.2.3 to 4.2.4
|
||||||
|
|
||||||
## 2026-03-05 - 4.2.3 - fix(compiler)
|
## 2026-03-05 - 4.2.3 - fix(compiler)
|
||||||
defer unpacking until after all compilations and remove diagnostic filesystem syncs to avoid XFS metadata visibility issues
|
defer unpacking until after all compilations and remove diagnostic filesystem syncs to avoid XFS metadata visibility issues
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@git.zone/tsbuild",
|
"name": "@git.zone/tsbuild",
|
||||||
"version": "4.2.3",
|
"version": "4.2.6",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.",
|
"description": "A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tsbuild',
|
name: '@git.zone/tsbuild',
|
||||||
version: '4.2.3',
|
version: '4.2.6',
|
||||||
description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.'
|
description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,6 +244,12 @@ export class TsCompiler {
|
|||||||
|
|
||||||
// If no pre-emit errors, proceed with emit
|
// If no pre-emit errors, proceed with emit
|
||||||
const emitResult = program.emit();
|
const emitResult = program.emit();
|
||||||
|
|
||||||
|
// Yield to the event loop so any pending microtasks, nextTick callbacks,
|
||||||
|
// or deferred I/O from TypeScript's emit (e.g. libuv write completions)
|
||||||
|
// can settle before we read or modify the output directory.
|
||||||
|
await new Promise<void>((resolve) => process.nextTick(resolve));
|
||||||
|
|
||||||
const emitErrorSummary = this.processDiagnostics(emitResult.diagnostics);
|
const emitErrorSummary = this.processDiagnostics(emitResult.diagnostics);
|
||||||
|
|
||||||
// Combine error summaries
|
// Combine error summaries
|
||||||
@@ -324,26 +330,25 @@ export class TsCompiler {
|
|||||||
console.log('');
|
console.log('');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect unpack tasks to perform AFTER all compilations complete.
|
// Phase 1: Resolve glob patterns and clean ALL output directories upfront.
|
||||||
// This prevents filesystem metadata corruption on XFS where heavy write
|
// This ensures no rm/rmSync activity overlaps with TypeScript compilation,
|
||||||
// activity during subsequent compilations can make freshly-renamed entries
|
// preventing XFS metadata corruption from concurrent metadata operations.
|
||||||
// in previously-unpacked directories invisible or lost.
|
interface IResolvedTask {
|
||||||
const pendingUnpacks: Array<{ pattern: string; destDir: string }> = [];
|
pattern: string;
|
||||||
|
destPath: string;
|
||||||
|
destDir: string;
|
||||||
|
absoluteFiles: string[];
|
||||||
|
}
|
||||||
|
const resolvedTasks: IResolvedTask[] = [];
|
||||||
|
|
||||||
for (const pattern of Object.keys(globPatterns)) {
|
for (const pattern of Object.keys(globPatterns)) {
|
||||||
const destPath = globPatterns[pattern];
|
const destPath = globPatterns[pattern];
|
||||||
if (!pattern || !destPath) continue;
|
if (!pattern || !destPath) continue;
|
||||||
|
|
||||||
// Get files matching the glob pattern
|
|
||||||
const files = await FsHelpers.listFilesWithGlob(this.cwd, pattern);
|
const files = await FsHelpers.listFilesWithGlob(this.cwd, pattern);
|
||||||
|
|
||||||
// Transform to absolute paths
|
|
||||||
const absoluteFiles = smartpath.transform.toAbsolute(files, this.cwd) as string[];
|
const absoluteFiles = smartpath.transform.toAbsolute(files, this.cwd) as string[];
|
||||||
|
|
||||||
// Get destination directory as absolute path
|
|
||||||
const destDir = smartpath.transform.toAbsolute(destPath, this.cwd) as string;
|
const destDir = smartpath.transform.toAbsolute(destPath, this.cwd) as string;
|
||||||
|
|
||||||
// Clear the destination directory before compilation if it exists
|
|
||||||
if (await FsHelpers.directoryExists(destDir)) {
|
if (await FsHelpers.directoryExists(destDir)) {
|
||||||
if (!isQuiet && !isJson) {
|
if (!isQuiet && !isJson) {
|
||||||
console.log(`🧹 Clearing output directory: ${destPath}`);
|
console.log(`🧹 Clearing output directory: ${destPath}`);
|
||||||
@@ -351,10 +356,16 @@ export class TsCompiler {
|
|||||||
await FsHelpers.removeDirectory(destDir);
|
await FsHelpers.removeDirectory(destDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update compiler options with the output directory
|
resolvedTasks.push({ pattern, destPath, destDir, absoluteFiles });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Phase 2: Compile all tasks. No filesystem cleanup happens during this phase.
|
||||||
|
const pendingUnpacks: Array<{ pattern: string; destDir: string }> = [];
|
||||||
|
|
||||||
|
for (const task of resolvedTasks) {
|
||||||
const options: CompilerOptions = {
|
const options: CompilerOptions = {
|
||||||
...customOptions,
|
...customOptions,
|
||||||
outDir: destDir,
|
outDir: task.destDir,
|
||||||
listEmittedFiles: true,
|
listEmittedFiles: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -362,23 +373,22 @@ export class TsCompiler {
|
|||||||
const taskInfo: ITaskInfo = {
|
const taskInfo: ITaskInfo = {
|
||||||
taskNumber: currentTask,
|
taskNumber: currentTask,
|
||||||
totalTasks,
|
totalTasks,
|
||||||
sourcePattern: pattern,
|
sourcePattern: task.pattern,
|
||||||
destDir: destPath,
|
destDir: task.destPath,
|
||||||
fileCount: absoluteFiles.length,
|
fileCount: task.absoluteFiles.length,
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await this.compileFiles(absoluteFiles, options, taskInfo);
|
const result = await this.compileFiles(task.absoluteFiles, options, taskInfo);
|
||||||
emittedFiles.push(...result.emittedFiles);
|
emittedFiles.push(...result.emittedFiles);
|
||||||
errorSummaries.push(result.errorSummary);
|
errorSummaries.push(result.errorSummary);
|
||||||
|
|
||||||
// Queue unpack for after all compilations (don't modify output dirs between compilations)
|
|
||||||
if (result.errorSummary.totalErrors === 0) {
|
if (result.errorSummary.totalErrors === 0) {
|
||||||
pendingUnpacks.push({ pattern, destDir });
|
pendingUnpacks.push({ pattern: task.pattern, destDir: task.destDir });
|
||||||
successfulOutputDirs.push(destDir);
|
successfulOutputDirs.push(task.destDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform all unpacks after all compilations are done.
|
// Phase 3: Perform all unpacks after all compilations are done.
|
||||||
// This ensures no output directory is modified while subsequent compilations
|
// This ensures no output directory is modified while subsequent compilations
|
||||||
// are performing heavy filesystem writes to sibling directories.
|
// are performing heavy filesystem writes to sibling directories.
|
||||||
for (const { pattern, destDir } of pendingUnpacks) {
|
for (const { pattern, destDir } of pendingUnpacks) {
|
||||||
|
|||||||
@@ -123,9 +123,6 @@ export class FsHelpers {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a directory recursively.
|
* Remove a directory recursively.
|
||||||
* Uses synchronous rm to avoid XFS metadata corruption observed with
|
|
||||||
* async fs.promises.rm affecting sibling directory entries on the
|
|
||||||
* libuv thread pool under signal pressure.
|
|
||||||
*/
|
*/
|
||||||
public static async removeDirectory(dirPath: string): Promise<void> {
|
public static async removeDirectory(dirPath: string): Promise<void> {
|
||||||
fs.rmSync(dirPath, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
fs.rmSync(dirPath, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
||||||
|
|||||||
Reference in New Issue
Block a user