@d1g1tal/tsbuild 1.2.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -340,6 +340,19 @@ var Logger = class _Logger {
340
340
  const prefix = indent ? " \u2514\u2500" : "\u2713";
341
341
  console.log(TextFormat.green(`${prefix} ${message}`));
342
342
  }
343
+ /**
344
+ * Logs sub-step timing entries in a tree format below a parent step.
345
+ * @param steps The sub-steps to log.
346
+ */
347
+ static subSteps(steps) {
348
+ const maxNameLength = steps.reduce((max, { name }) => Math.max(max, name.length), 0);
349
+ const maxDurationLength = steps.reduce((max, { duration }) => Math.max(max, duration.length), 0);
350
+ for (let i = 0, length = steps.length; i < length; i++) {
351
+ const { name, duration } = steps[i];
352
+ const prefix = i === length - 1 ? " \u2514\u2500" : " \u251C\u2500";
353
+ console.log(`${TextFormat.dim(prefix)} ${TextFormat.bold(name.padEnd(maxNameLength))} ${TextFormat.cyan(duration.padStart(maxDurationLength))}`);
354
+ }
355
+ }
343
356
  /**
344
357
  * Logs a success message.
345
358
  * @param message The message to log.
@@ -1495,13 +1508,14 @@ function closeOnExit(value, _context) {
1495
1508
  // src/decorators/performance-logger.ts
1496
1509
  import { PerformanceObserver, performance } from "perf_hooks";
1497
1510
  var type = "measure";
1511
+ var pendingSteps = [];
1498
1512
  var _PerformanceLogger_decorators, _init;
1499
1513
  _PerformanceLogger_decorators = [closeOnExit];
1500
1514
  var _PerformanceLogger = class _PerformanceLogger {
1501
1515
  performanceObserver;
1502
1516
  constructor() {
1503
1517
  this.performanceObserver = new PerformanceObserver((list) => {
1504
- for (const { name, duration, detail: { message, result = [] } } of list.getEntriesByType(type).reverse()) {
1518
+ for (const { name, duration, detail: { message, result = [], steps } } of list.getEntriesByType(type).reverse()) {
1505
1519
  if (message === "Build") {
1506
1520
  Logger.separator();
1507
1521
  if (process.exitCode) {
@@ -1513,6 +1527,9 @@ var _PerformanceLogger = class _PerformanceLogger {
1513
1527
  }
1514
1528
  } else {
1515
1529
  Logger.step(`${message} ${TextFormat.dim(`(${_PerformanceLogger.formatDuration(duration)})`)}`);
1530
+ if (steps?.length) {
1531
+ Logger.subSteps(steps);
1532
+ }
1516
1533
  if (result.length > 0) {
1517
1534
  Logger.success("", ...result);
1518
1535
  }
@@ -1533,6 +1550,9 @@ var _PerformanceLogger = class _PerformanceLogger {
1533
1550
  if (logResult) {
1534
1551
  options.detail.result = result;
1535
1552
  }
1553
+ if (pendingSteps.length > 0) {
1554
+ options.detail.steps = pendingSteps.splice(0);
1555
+ }
1536
1556
  ({ startTime: options.end } = performance.mark(propertyKey));
1537
1557
  performance.measure(propertyKey, options);
1538
1558
  return result;
@@ -1575,6 +1595,9 @@ _PerformanceLogger = __decorateElement(_init, 0, "PerformanceLogger", _Performan
1575
1595
  __runInitializers(_init, 1, _PerformanceLogger);
1576
1596
  var PerformanceLogger = _PerformanceLogger;
1577
1597
  var measure = new PerformanceLogger().measure;
1598
+ function addPerformanceStep(name, duration) {
1599
+ pendingSteps.push({ name, duration });
1600
+ }
1578
1601
 
1579
1602
  // src/decorators/debounce.ts
1580
1603
  var _DebounceManager_decorators, _init2;
@@ -1643,11 +1666,17 @@ function debounce(wait) {
1643
1666
  }
1644
1667
 
1645
1668
  // src/file-manager.ts
1646
- import { sys as sys2, createSourceFile as createSourceFile2, ScriptTarget as ScriptTarget2 } from "typescript";
1669
+ import { createSourceFile as createSourceFile2, ScriptTarget as ScriptTarget2 } from "typescript";
1647
1670
  var FileManager = class {
1648
1671
  hasEmittedFiles = false;
1649
1672
  declarationFiles = /* @__PURE__ */ new Map();
1650
1673
  cache;
1674
+ /** Raw declaration text captured during emit, pending pre-processing */
1675
+ pendingFiles = [];
1676
+ /** Buffered .tsbuildinfo content for async write (avoids sync I/O during emit) */
1677
+ pendingBuildInfo;
1678
+ /** Background cache save promise — awaited in initialize() and close() */
1679
+ pendingSave;
1651
1680
  /**
1652
1681
  * Creates a new file manager.
1653
1682
  * @param buildCache - Optional build cache for incremental builds
@@ -1668,7 +1697,13 @@ var FileManager = class {
1668
1697
  * ```
1669
1698
  */
1670
1699
  async initialize() {
1700
+ if (this.pendingSave) {
1701
+ await this.pendingSave;
1702
+ this.pendingSave = void 0;
1703
+ }
1671
1704
  this.hasEmittedFiles = false;
1705
+ this.pendingFiles.length = 0;
1706
+ this.pendingBuildInfo = void 0;
1672
1707
  if (this.cache) {
1673
1708
  await this.cache.restore(this.declarationFiles);
1674
1709
  } else {
@@ -1684,14 +1719,22 @@ var FileManager = class {
1684
1719
  * ```typescript
1685
1720
  * await manager.initialize();
1686
1721
  * program.emit(undefined, manager.fileWriter, undefined, true);
1687
- * const hasEmitted = await manager.finalize();
1722
+ * const hasEmitted = manager.finalize();
1688
1723
  * if (hasEmitted) { // Continue with build }
1689
1724
  * ```
1690
1725
  */
1691
- async finalize() {
1726
+ finalize() {
1727
+ this.processEmittedFiles();
1728
+ const buildInfoWrite = this.pendingBuildInfo ? Files.write(this.pendingBuildInfo.path, this.pendingBuildInfo.text) : void 0;
1729
+ this.pendingBuildInfo = void 0;
1692
1730
  if (this.cache && this.hasEmittedFiles) {
1693
- await this.cache.save(this.declarationFiles);
1731
+ this.pendingSave = Promise.all([buildInfoWrite, this.cache.save(this.declarationFiles)]).then(() => {
1732
+ });
1733
+ } else if (buildInfoWrite) {
1734
+ this.pendingSave = buildInfoWrite;
1694
1735
  }
1736
+ this.pendingSave?.catch(() => {
1737
+ });
1695
1738
  return this.cache === void 0 || this.hasEmittedFiles;
1696
1739
  }
1697
1740
  /**
@@ -1746,8 +1789,25 @@ var FileManager = class {
1746
1789
  * Clears all stored declaration files.
1747
1790
  */
1748
1791
  close() {
1792
+ this.pendingSave?.then(() => {
1793
+ }, () => {
1794
+ });
1795
+ this.pendingSave = void 0;
1796
+ this.pendingFiles.length = 0;
1797
+ this.pendingBuildInfo = void 0;
1749
1798
  this.declarationFiles.clear();
1750
1799
  }
1800
+ /**
1801
+ * Awaits any in-flight background I/O (cache save, .tsbuildinfo write).
1802
+ * Call this when you need to guarantee all pending writes have completed,
1803
+ * e.g., before reading the cache file from a different instance.
1804
+ */
1805
+ async flush() {
1806
+ if (this.pendingSave) {
1807
+ await this.pendingSave;
1808
+ this.pendingSave = void 0;
1809
+ }
1810
+ }
1751
1811
  /**
1752
1812
  * Writes a single declaration file to disk.
1753
1813
  * @param projectDirectory - Project root for calculating relative paths
@@ -1761,21 +1821,32 @@ var FileManager = class {
1761
1821
  }
1762
1822
  /**
1763
1823
  * Function that intercepts file writes during TypeScript emit.
1764
- * Declaration files are pre-processed and stored in memory, while .tsbuildinfo is written to disk.
1765
- * Pre-processing happens immediately so the cache stores ready-to-use declarations.
1824
+ * Captures raw text for deferred pre-processing and buffers .tsbuildinfo for async I/O.
1825
+ * Designed to be as fast as possible since it runs inside TypeScript's synchronous emit() call.
1766
1826
  * @param filePath - The path of the file being written
1767
1827
  * @param text - The content of the file being written
1768
1828
  */
1769
1829
  fileWriter = (filePath, text) => {
1770
1830
  if (this.cache?.isBuildInfoFile(filePath)) {
1771
- sys2.writeFile(filePath, text);
1831
+ this.pendingBuildInfo = { path: filePath, text };
1772
1832
  } else {
1773
- this.declarationFiles.set(filePath, DeclarationProcessor.preProcess(createSourceFile2(filePath, text, ScriptTarget2.Latest, true)));
1833
+ this.pendingFiles.push({ path: filePath, text });
1774
1834
  }
1775
1835
  if (!this.hasEmittedFiles) {
1776
1836
  this.hasEmittedFiles = true;
1777
1837
  }
1778
1838
  };
1839
+ /**
1840
+ * Pre-processes all declaration files captured during emit.
1841
+ * Runs createSourceFile + DeclarationProcessor.preProcess for each pending file,
1842
+ * then clears the pending queue.
1843
+ */
1844
+ processEmittedFiles() {
1845
+ for (const { path, text } of this.pendingFiles) {
1846
+ this.declarationFiles.set(path, DeclarationProcessor.preProcess(createSourceFile2(path, text, ScriptTarget2.Latest, true)));
1847
+ }
1848
+ this.pendingFiles.length = 0;
1849
+ }
1779
1850
  /**
1780
1851
  * Custom inspection method for better type representation.
1781
1852
  * @returns The string 'FileManager'
@@ -1971,10 +2042,11 @@ function inferEntryPoints(packageJson, outDir, sourceDir = "src") {
1971
2042
 
1972
2043
  // src/type-script-project.ts
1973
2044
  import { build as esbuild, formatMessages } from "esbuild";
1974
- import { sys as sys3, createIncrementalProgram, formatDiagnostics, formatDiagnosticsWithColorAndContext, parseJsonConfigFileContent, readConfigFile, findConfigFile } from "typescript";
2045
+ import { performance as performance2 } from "node:perf_hooks";
2046
+ import { sys as sys2, createIncrementalProgram, formatDiagnostics, formatDiagnosticsWithColorAndContext, parseJsonConfigFileContent, readConfigFile, findConfigFile } from "typescript";
1975
2047
  var globCharacters = /[*?\\[\]!].*$/;
1976
2048
  var domPredicate = (lib) => lib.toUpperCase() === "DOM";
1977
- var diagnosticsHost = { getNewLine: () => sys3.newLine, getCurrentDirectory: sys3.getCurrentDirectory, getCanonicalFileName: (fileName) => fileName };
2049
+ var diagnosticsHost = { getNewLine: () => sys2.newLine, getCurrentDirectory: sys2.getCurrentDirectory, getCanonicalFileName: (fileName) => fileName };
1978
2050
  var _triggerRebuild_dec, _processDeclarations_dec, _transpile_dec, _typeCheck_dec, _build_dec, _TypeScriptProject_decorators, _init3;
1979
2051
  _TypeScriptProject_decorators = [closeOnExit], _build_dec = [measure("Build")], _typeCheck_dec = [measure("Type-checking")], _transpile_dec = [measure("Transpile", true)], _processDeclarations_dec = [measure("Process Declarations", true)], _triggerRebuild_dec = [debounce(100)];
1980
2052
  var _TypeScriptProject = class _TypeScriptProject {
@@ -1983,7 +2055,7 @@ var _TypeScriptProject = class _TypeScriptProject {
1983
2055
  * @param directory - Project root directory (defaults to current working directory)
1984
2056
  * @param options - Project options to merge with tsconfig.json
1985
2057
  */
1986
- constructor(directory = sys3.getCurrentDirectory(), options = {}) {
2058
+ constructor(directory = sys2.getCurrentDirectory(), options = {}) {
1987
2059
  __runInitializers(_init3, 5, this);
1988
2060
  __publicField(this, "fileWatcher");
1989
2061
  __publicField(this, "builderProgram");
@@ -2015,7 +2087,7 @@ var _TypeScriptProject = class _TypeScriptProject {
2015
2087
  return Files.empty(this.buildConfiguration.outDir);
2016
2088
  }
2017
2089
  async build() {
2018
- Logger.header(`\u{1F680} tsbuild v${"1.2.5"}${this.configuration.compilerOptions.incremental ? " [incremental]" : ""}`);
2090
+ Logger.header(`\u{1F680} tsbuild v${"1.3.0"}${this.configuration.compilerOptions.incremental ? " [incremental]" : ""}`);
2019
2091
  try {
2020
2092
  const processes = [];
2021
2093
  const filesWereEmitted = await this.typeCheck();
@@ -2053,12 +2125,19 @@ var _TypeScriptProject = class _TypeScriptProject {
2053
2125
  }
2054
2126
  async typeCheck() {
2055
2127
  await this.fileManager.initialize();
2128
+ performance2.mark("emit:start");
2056
2129
  const { diagnostics: emitDiagnostics } = this.builderProgram.emit(void 0, this.fileManager.fileWriter, void 0, true);
2130
+ addPerformanceStep("Emit", _TypeScriptProject.elapsed("emit:start"));
2131
+ performance2.mark("diagnostics:start");
2057
2132
  const allDiagnostics = [...this.builderProgram.getSemanticDiagnostics(), ...emitDiagnostics];
2133
+ addPerformanceStep("Diagnostics", _TypeScriptProject.elapsed("diagnostics:start"));
2058
2134
  if (allDiagnostics.length > 0) {
2059
2135
  _TypeScriptProject.handleTypeErrors("Type-checking failed", allDiagnostics, this.directory);
2060
2136
  }
2061
- return this.fileManager.finalize();
2137
+ performance2.mark("finalize:start");
2138
+ const result = this.fileManager.finalize();
2139
+ addPerformanceStep("Finalize", _TypeScriptProject.elapsed("finalize:start"));
2140
+ return result;
2062
2141
  }
2063
2142
  async transpile() {
2064
2143
  const plugins = [outputPlugin()];
@@ -2224,7 +2303,7 @@ var _TypeScriptProject = class _TypeScriptProject {
2224
2303
  * @returns Resolved configuration and TypeScript parser results
2225
2304
  */
2226
2305
  static resolveConfiguration(directory, typeScriptOptions) {
2227
- const configResult = readConfigFile(findConfigFile(directory, sys3.fileExists) ?? "./tsconfig.json", sys3.readFile);
2306
+ const configResult = readConfigFile(findConfigFile(directory, sys2.fileExists) ?? "./tsconfig.json", sys2.readFile);
2228
2307
  if (configResult.error !== void 0) {
2229
2308
  throw new ConfigurationError(formatDiagnostics([configResult.error], diagnosticsHost));
2230
2309
  }
@@ -2234,7 +2313,7 @@ var _TypeScriptProject = class _TypeScriptProject {
2234
2313
  const hasExplicitEntryPoints = typeScriptOptions.tsbuild?.entryPoints !== void 0 || configResult.config.tsbuild?.entryPoints !== void 0;
2235
2314
  let inferredEntryPoints;
2236
2315
  if (!hasExplicitEntryPoints && bundle) {
2237
- const packageJsonContent = sys3.readFile(Paths.join(directory, "package.json"));
2316
+ const packageJsonContent = sys2.readFile(Paths.join(directory, "package.json"));
2238
2317
  if (packageJsonContent) {
2239
2318
  try {
2240
2319
  const pkgJson = JSON.parse(packageJsonContent);
@@ -2273,7 +2352,7 @@ var _TypeScriptProject = class _TypeScriptProject {
2273
2352
  ...typeScriptOptions.compilerOptions
2274
2353
  }
2275
2354
  };
2276
- const { options, fileNames, errors } = parseJsonConfigFileContent(baseConfig, sys3, directory);
2355
+ const { options, fileNames, errors } = parseJsonConfigFileContent(baseConfig, sys2, directory);
2277
2356
  return {
2278
2357
  ...baseConfig,
2279
2358
  compilerOptions: {
@@ -2373,11 +2452,11 @@ var _TypeScriptProject = class _TypeScriptProject {
2373
2452
  const [[firstFileName, { line: firstLine }] = ["", { line: 0 }]] = filesWithErrors;
2374
2453
  const relativeFirstFileName = Paths.relative(projectDirectory, firstFileName);
2375
2454
  if (errorCount === 1) {
2376
- Logger.error(`Found 1 error in ${relativeFirstFileName}:${firstLine + 1}${sys3.newLine}`);
2455
+ Logger.error(`Found 1 error in ${relativeFirstFileName}:${firstLine + 1}${sys2.newLine}`);
2377
2456
  } else if (fileCount === 1) {
2378
- Logger.error(`Found ${errorCount} errors in the same file, starting at: ${relativeFirstFileName}:${firstLine + 1}${sys3.newLine}`);
2457
+ Logger.error(`Found ${errorCount} errors in the same file, starting at: ${relativeFirstFileName}:${firstLine + 1}${sys2.newLine}`);
2379
2458
  } else {
2380
- Logger.error(`Found ${errorCount} errors in ${fileCount} files.${sys3.newLine}`);
2459
+ Logger.error(`Found ${errorCount} errors in ${fileCount} files.${sys2.newLine}`);
2381
2460
  Logger.error("Errors Files");
2382
2461
  for (const [fileName, { count, line }] of filesWithErrors) {
2383
2462
  Logger.error(` ${count} ${fileName}:${line + 1}`);
@@ -2385,6 +2464,18 @@ var _TypeScriptProject = class _TypeScriptProject {
2385
2464
  }
2386
2465
  throw new TypeCheckError(message, formatDiagnostics(diagnostics, diagnosticsHost));
2387
2466
  }
2467
+ /**
2468
+ * Calculates elapsed time since a performance mark and clears the mark.
2469
+ * @param markName - The name of the performance mark to measure from
2470
+ * @returns Formatted elapsed time string (e.g., '42ms')
2471
+ */
2472
+ static elapsed(markName) {
2473
+ const endMark = performance2.mark(`${markName}:end`);
2474
+ const duration = endMark.startTime - performance2.getEntriesByName(markName, "mark")[0].startTime;
2475
+ performance2.clearMarks(markName);
2476
+ performance2.clearMarks(endMark.name);
2477
+ return `${~~duration}ms`;
2478
+ }
2388
2479
  };
2389
2480
  _init3 = __decoratorStart(null);
2390
2481
  __decorateElement(_init3, 1, "build", _build_dec, _TypeScriptProject);
package/dist/index.d.ts CHANGED
@@ -41,9 +41,14 @@ interface Closable {
41
41
  close: Callable;
42
42
  }
43
43
  type ClosableConstructor = Constructor<any[], Closable>;
44
+ type PerformanceSubStep = {
45
+ name: string;
46
+ duration: string;
47
+ };
44
48
  type PerformanceEntryDetail<T = unknown[]> = {
45
49
  message: string;
46
50
  result?: T;
51
+ steps?: PerformanceSubStep[];
47
52
  };
48
53
  type DetailedPerformanceMeasureOptions<R> = PrettyModify<PerformanceMeasureOptions, {
49
54
  detail: PerformanceEntryDetail<R>;
@@ -337,7 +342,13 @@ declare class TypeScriptProject implements Closable {
337
342
  * @param projectDirectory - The project directory.
338
343
  */
339
344
  private static handleTypeErrors;
345
+ /**
346
+ * Calculates elapsed time since a performance mark and clears the mark.
347
+ * @param markName - The name of the performance mark to measure from
348
+ * @returns Formatted elapsed time string (e.g., '42ms')
349
+ */
350
+ private static elapsed;
340
351
  }
341
352
 
342
353
  export { TypeScriptProject };
343
- export type { AbsolutePath, AsyncEntryPoints, Brand, BuildCache, BuildConfiguration, CachedDeclaration, Closable, ClosableConstructor, CompilerOptionOverrides, ConditionalPath, DetailedPerformanceEntry, DetailedPerformanceMeasureOptions, EntryPoints, EsTarget, FormatSupplier, Function, InferredFunction, JsonString, JsxRenderingMode, LogEntryType, MethodFunction, OptionalReturn, Path, Pattern, PendingFileChange, ProjectBuildConfiguration, ProjectDependencies, ReadConfigResult, RelativePath, SourceMap, TypeScriptConfiguration, TypeScriptOptions, TypedFunction, WrittenFile };
354
+ export type { AbsolutePath, AsyncEntryPoints, Brand, BuildCache, BuildConfiguration, CachedDeclaration, Closable, ClosableConstructor, CompilerOptionOverrides, ConditionalPath, DetailedPerformanceEntry, DetailedPerformanceMeasureOptions, EntryPoints, EsTarget, FormatSupplier, Function, InferredFunction, JsonString, JsxRenderingMode, LogEntryType, MethodFunction, OptionalReturn, Path, Pattern, PendingFileChange, PerformanceSubStep, ProjectBuildConfiguration, ProjectDependencies, ReadConfigResult, RelativePath, SourceMap, TypeScriptConfiguration, TypeScriptOptions, TypedFunction, WrittenFile };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TypeScriptProject
3
- } from "./O7CHVFME.js";
3
+ } from "./3HKDLNVH.js";
4
4
  import "./7FPDHUPW.js";
5
5
  export {
6
6
  TypeScriptProject
package/dist/tsbuild.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  BuildError,
4
4
  TypeScriptProject
5
- } from "./O7CHVFME.js";
5
+ } from "./3HKDLNVH.js";
6
6
  import "./7FPDHUPW.js";
7
7
 
8
8
  // src/tsbuild.ts
@@ -30,7 +30,7 @@ if (help) {
30
30
  process.exit(0);
31
31
  }
32
32
  if (version) {
33
- console.log("1.2.5");
33
+ console.log("1.3.0");
34
34
  process.exit(0);
35
35
  }
36
36
  var typeScriptOptions = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d1g1tal/tsbuild",
3
- "version": "1.2.5",
3
+ "version": "1.3.0",
4
4
  "packageManager": "pnpm@10.30.3",
5
5
  "description": "A fast, ESM-only TypeScript build tool combining the TypeScript API for type checking and declaration generation, esbuild for bundling, and SWC for decorator metadata.",
6
6
  "type": "module",