@d1g1tal/tsbuild 1.6.3 → 1.6.5

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.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,62 @@
1
+ ## [1.6.5](https://github.com/D1g1talEntr0py/tsbuild/compare/v1.6.4...v1.6.5) (2026-03-21)
2
+
3
+ ### Code Refactoring
4
+
5
+ * **bundler:** remove unused getModuleExports method (40be90ce03de5ada924ac6ac5d0966cceda7ade1)
6
+ - Remove dead private method getModuleExports from DeclarationBundler class
7
+
8
+
9
+ ### Tests
10
+
11
+ * add json, incremental-build-cache tests and shared declaration fixtures (dadea76ceb2fa487124031bbef2b2532195a60dd)
12
+ - Add comprehensive tests for Json.parse and Json.serialize with primitives, arrays, and objects
13
+ - Add tests for IncrementalBuildCache covering restore, save, invalidate, isBuildInfoFile, and isValid
14
+ - Add tests for corrupt cache file handling, cache invalidation skipping restore, and round-trip save/restore
15
+ - Add shared declaration fixture file with reusable type definitions for bundler and processor tests
16
+ - Remove old build-cache.test.ts in favor of new incremental-build-cache.test.ts
17
+
18
+ * rewrite test suite with parameterized patterns and expanded coverage (dd30d901a7bcffcf743ae7bb91d6309377cdb49d)
19
+ - Rewrite text-formatter tests with it.each matrix patterns for formatting, color, bright, background, and bright background categories
20
+ - Rewrite paths tests with it.each matrix for isPath, add parse and isFile coverage, remove TestHelper boilerplate
21
+ - Rewrite logger tests with it.each matrices for isWrittenFiles/colorize/prettyBytes, remove memfs dependency, add header/separator/step/subSteps/EntryType tests
22
+ - Rewrite decorator-metadata plugin tests removing TestHelper dependency, simplify mock setup
23
+ - Rewrite external-modules plugin tests with it.each for bare specifiers and local paths, add packageName extraction tests
24
+ - Rewrite output plugin tests, add rewriteRelativeSpecifiers unit tests for extension-less and bare specifier handling
25
+ - Simplify process-manager tests by condensing redundant assertions and removing duplicate spy verifications
26
+ - Rewrite tsbuild CLI tests with it.each for --help/-h and --version/-v flags, group into describe blocks
27
+ - Expand type-script-project tests with triggerRebuild (rename, unlink, empty changes), close, handleBuildError (watch mode), resolveConfiguration (browser platform, entry point inference, invalid tsconfig, malformed package.json), getEntryPoints, and transpile (env expansion, esbuild warnings/errors, SWC decorator metadata plugin)
28
+ - Streamline integration tests with condensed fixtures and consistent Logger mock formatting
29
+ - Rewrite constants, declaration-bundler, declaration-processor, decorator, entry-points, errors, file-manager, and files tests with simplified patterns
30
+
31
+ ## [1.6.4](https://github.com/D1g1talEntr0py/tsbuild/compare/v1.6.3...v1.6.4) (2026-03-21)
32
+
33
+ ### Bug Fixes
34
+
35
+ * **logging:** bypass formatting for empty error message arrays (7ced5ad268029ee763bafdfeda405c50faec8ad3)
36
+ - Check for messages length before invoking the format function
37
+ - Preclude unnecessary iteration over empty diagnostic outputs
38
+ - Prevent empty lines from being emitted to the console log stream
39
+
40
+
41
+ ### Performance Improvements
42
+
43
+ * **plugins:** optimize build plugins and file operations (d5669224bff868001baf43a99efb409ec33d521e)
44
+ - Cache SWC transformFile reference lazy-loaded for decorator metadata
45
+ - Build reusable O(1) matchers for external modules string and RegExp patterns
46
+ - Extract package names properly handling scoped and unscoped module paths
47
+ - Cache shared TextEncoder and TextDecoder instances for output generation
48
+ - Preserve shebangs and set correct execute permissions on output scripts
49
+ - Optimize extension rewriting to only trigger string replacements when modified
50
+
51
+
52
+ ### Miscellaneous Chores
53
+
54
+ * **deps:** update watchr and eslint dependencies (619d559159bb358cef75b36e99223999a0c6da57)
55
+ - Update @d1g1tal/watchr package to version 1.0.4
56
+ - Update eslint package to version 10.1.0
57
+ - Synchronize pnpm-lock.yaml with new dependency versions
58
+ - Update typescript-eslint plugin and parser dependencies in lockfile
59
+
1
60
  ## [1.6.3](https://github.com/D1g1talEntr0py/tsbuild/compare/v1.6.2...v1.6.3) (2026-03-18)
2
61
 
3
62
  ### Bug Fixes
@@ -18,6 +18,7 @@ var swcOptions = {
18
18
  configFile: false,
19
19
  swcrc: false
20
20
  };
21
+ var swcTransformFile;
21
22
  var swcDecoratorMetadataPlugin = {
22
23
  name: "esbuild:swc-decorator-metadata",
23
24
  /**
@@ -29,8 +30,8 @@ var swcDecoratorMetadataPlugin = {
29
30
  setup(build) {
30
31
  build.initialOptions.keepNames = true;
31
32
  build.onLoad({ filter: typeScriptExtensionExpression }, async ({ path }) => {
32
- const { transformFile } = await import("@swc/core");
33
- const result = await transformFile(path, swcOptions);
33
+ swcTransformFile ??= (await import("@swc/core")).transformFile;
34
+ const result = await swcTransformFile(path, swcOptions);
34
35
  if (result.map) {
35
36
  const sources = [];
36
37
  for (const source of Json.parse(result.map).sources) {
@@ -625,10 +625,10 @@ var DeclarationProcessor = class _DeclarationProcessor {
625
625
  }
626
626
  } else {
627
627
  inlineTypePattern.lastIndex = 0;
628
- let match2;
628
+ let match;
629
629
  const replacements = [];
630
- while ((match2 = inlineTypePattern.exec(code.slice(node.getStart(), node.getEnd()))) !== null) {
631
- const typeKeywordStart = node.getStart() + match2.index + match2[1].length;
630
+ while ((match = inlineTypePattern.exec(code.slice(node.getStart(), node.getEnd()))) !== null) {
631
+ const typeKeywordStart = node.getStart() + match.index + match[1].length;
632
632
  const typeKeywordEnd = typeKeywordStart + "type".length;
633
633
  replacements.push({ start: typeKeywordStart, end: typeKeywordEnd + getTrailingWhitespaceLength(typeKeywordEnd, node.getEnd()) });
634
634
  }
@@ -826,9 +826,9 @@ function mergeImports(imports) {
826
826
  const moduleImports = /* @__PURE__ */ new Map();
827
827
  const nonMergeableImports = [];
828
828
  for (const importStatement of imports) {
829
- const match2 = importPattern.exec(importStatement);
830
- if (match2) {
831
- const [, namesString, moduleSpecifier] = match2;
829
+ const match = importPattern.exec(importStatement);
830
+ if (match) {
831
+ const [, namesString, moduleSpecifier] = match;
832
832
  const isType = importStatement.includes("import type");
833
833
  const key = `${isType ? "type:" : ""}${moduleSpecifier}`;
834
834
  if (!moduleImports.has(key)) {
@@ -1323,22 +1323,6 @@ var DeclarationBundler = class {
1323
1323
  }
1324
1324
  return { code: outputParts.join(newLine), exports: [...finalTypeExports, ...finalValueExports], allDeclarations };
1325
1325
  }
1326
- /**
1327
- * Extract exported names from a processed source file
1328
- * @param processedSourceFile - The processed source file
1329
- * @returns Array of exported names
1330
- */
1331
- getModuleExports(processedSourceFile) {
1332
- const exports = [];
1333
- for (const statement of processedSourceFile.statements) {
1334
- if (isExportDeclaration2(statement) && statement.exportClause && isNamedExports2(statement.exportClause)) {
1335
- for (const element of statement.exportClause.elements) {
1336
- exports.push(element.name.text);
1337
- }
1338
- }
1339
- }
1340
- return exports;
1341
- }
1342
1326
  /**
1343
1327
  * Main bundling orchestration method
1344
1328
  * @param entryPoint - The entry point file path
@@ -1390,19 +1374,27 @@ async function bundleDeclarations(options) {
1390
1374
 
1391
1375
  // src/plugins/output.ts
1392
1376
  import { extname } from "node:path";
1393
- var FileMode = { READ_WRITE: 438, READ_WRITE_EXECUTE: 493 };
1377
+ var textEncoder = new TextEncoder();
1378
+ var textDecoder = new TextDecoder();
1379
+ var localFileIdentifier = /\.[a-z]+$/i;
1394
1380
  var relativeSpecifierPattern = /(from\s*['"])(\.\.?\/[^'"]*?)(['"])/g;
1381
+ var FileMode = { READ_WRITE: 438, READ_WRITE_EXECUTE: 493 };
1395
1382
  function rewriteRelativeSpecifiers(code) {
1396
- return code.replace(relativeSpecifierPattern, (_, before, path, after) => {
1397
- if (/\.[a-z]+$/i.test(path)) return before + path + after;
1398
- return `${before}${path}.js${after}`;
1399
- });
1383
+ return code.replace(relativeSpecifierPattern, (_, before, path, after) => localFileIdentifier.test(path) ? before + path + after : `${before}${path}.js${after}`);
1400
1384
  }
1401
1385
  async function fileMapper({ path, contents }) {
1402
- const isJs = extname(path) === FileExtension.JS;
1403
- const mode = isJs && contents[0] === 35 && contents[1] === 33 ? FileMode.READ_WRITE_EXECUTE : FileMode.READ_WRITE;
1404
- const finalContents = isJs ? new TextEncoder().encode(rewriteRelativeSpecifiers(new TextDecoder().decode(contents))) : contents;
1405
- return Files.write(path, finalContents, { mode });
1386
+ if (extname(path) !== FileExtension.JS) {
1387
+ return Files.write(path, contents, { mode: FileMode.READ_WRITE });
1388
+ }
1389
+ let rewritten = false;
1390
+ const result = textDecoder.decode(contents).replace(relativeSpecifierPattern, (_, before, specPath, after) => {
1391
+ if (localFileIdentifier.test(specPath)) {
1392
+ return before + specPath + after;
1393
+ }
1394
+ rewritten = true;
1395
+ return `${before}${specPath}.js${after}`;
1396
+ });
1397
+ return Files.write(path, rewritten ? textEncoder.encode(result) : contents, { mode: contents[0] === 35 && contents[1] === 33 ? FileMode.READ_WRITE_EXECUTE : FileMode.READ_WRITE });
1406
1398
  }
1407
1399
  var outputPlugin = () => {
1408
1400
  return {
@@ -1418,9 +1410,42 @@ var outputPlugin = () => {
1418
1410
  };
1419
1411
 
1420
1412
  // src/plugins/external-modules.ts
1421
- var match = (id, patterns) => {
1422
- return patterns.some((pattern) => pattern instanceof RegExp ? pattern.test(id) : id === pattern || id.startsWith(`${pattern}/`));
1423
- };
1413
+ function packageName(id) {
1414
+ if (id.charCodeAt(0) === 64) {
1415
+ const first = id.indexOf("/");
1416
+ if (first === -1) {
1417
+ return id;
1418
+ }
1419
+ const second = id.indexOf("/", first + 1);
1420
+ return second === -1 ? id : id.slice(0, second);
1421
+ }
1422
+ const slash = id.indexOf("/");
1423
+ return slash === -1 ? id : id.slice(0, slash);
1424
+ }
1425
+ function buildMatcher(patterns) {
1426
+ const exact = /* @__PURE__ */ new Set();
1427
+ const regexps = [];
1428
+ for (const p of patterns) {
1429
+ if (typeof p === "string") {
1430
+ exact.add(p);
1431
+ } else {
1432
+ regexps.push(p);
1433
+ }
1434
+ }
1435
+ if (exact.size === 0 && regexps.length === 0) {
1436
+ return () => false;
1437
+ }
1438
+ return (id) => {
1439
+ if (exact.has(id)) {
1440
+ return true;
1441
+ }
1442
+ const pkg = packageName(id);
1443
+ if (pkg !== id && exact.has(pkg)) {
1444
+ return true;
1445
+ }
1446
+ return regexps.length > 0 && regexps.some((r) => r.test(id));
1447
+ };
1448
+ }
1424
1449
  var externalModulesPlugin = ({ dependencies = [], noExternal = [] }) => {
1425
1450
  return {
1426
1451
  name: "esbuild:external-modules",
@@ -1430,11 +1455,13 @@ var externalModulesPlugin = ({ dependencies = [], noExternal = [] }) => {
1430
1455
  */
1431
1456
  setup(build) {
1432
1457
  const external = true;
1458
+ const matchNoExternal = buildMatcher(noExternal);
1459
+ const matchDependencies = buildMatcher(dependencies);
1433
1460
  build.onResolve({ filter: /.*/ }, ({ path }) => {
1434
1461
  switch (true) {
1435
- case match(path, noExternal):
1462
+ case matchNoExternal(path):
1436
1463
  return;
1437
- case match(path, dependencies):
1464
+ case matchDependencies(path):
1438
1465
  return { external };
1439
1466
  case !Paths.isPath(path):
1440
1467
  return { path, external };
@@ -2012,9 +2039,9 @@ function resolveConditionalExport(exportValue) {
2012
2039
  }
2013
2040
  return void 0;
2014
2041
  }
2015
- function subpathToEntryName(subpath, packageName) {
2042
+ function subpathToEntryName(subpath, packageName2) {
2016
2043
  if (subpath === ".") {
2017
- return packageName !== void 0 ? unscope(packageName) : "index";
2044
+ return packageName2 !== void 0 ? unscope(packageName2) : "index";
2018
2045
  }
2019
2046
  const withoutPrefix = subpath.replace(/^\.\//, "");
2020
2047
  const lastSegment = withoutPrefix.lastIndexOf("/");
@@ -2115,7 +2142,7 @@ var _TypeScriptProject = class _TypeScriptProject {
2115
2142
  }
2116
2143
  async build() {
2117
2144
  const tsLogo = TextFormat.bgBlue(TextFormat.bold(TextFormat.whiteBright(" TS ")));
2118
- Logger.header(`${tsLogo} tsbuild v${"1.6.3"}${this.configuration.compilerOptions.incremental && this.configuration.buildCache?.isValid() ? " [incremental]" : ""}`);
2145
+ Logger.header(`${tsLogo} tsbuild v${"1.6.5"}${this.configuration.compilerOptions.incremental && this.configuration.buildCache?.isValid() ? " [incremental]" : ""}`);
2119
2146
  try {
2120
2147
  const processes = [];
2121
2148
  const filesWereEmitted = await this.typeCheck();
@@ -2174,7 +2201,7 @@ var _TypeScriptProject = class _TypeScriptProject {
2174
2201
  }
2175
2202
  if (this.configuration.compilerOptions.emitDecoratorMetadata) {
2176
2203
  try {
2177
- const { swcDecoratorMetadataPlugin } = await import("./LEZQQWX3.js");
2204
+ const { swcDecoratorMetadataPlugin } = await import("./GQLOAEUW.js");
2178
2205
  plugins.push(swcDecoratorMetadataPlugin);
2179
2206
  } catch {
2180
2207
  throw new ConfigurationError("emitDecoratorMetadata is enabled but @swc/core is not installed. Install it with: pnpm add -D @swc/core");
@@ -2230,8 +2257,10 @@ var _TypeScriptProject = class _TypeScriptProject {
2230
2257
  supported: { decorators: false }
2231
2258
  });
2232
2259
  for (const [kind, logEntryType, messages] of [[BuildMessageType.WARNING, Logger.EntryType.Warn, warnings], [BuildMessageType.ERROR, Logger.EntryType.Error, errors]]) {
2233
- for (const message of await formatMessages(messages, { kind, color: true })) {
2234
- Logger.log(message, logEntryType);
2260
+ if (messages.length > 0) {
2261
+ for (const message of await formatMessages(messages, { kind, color: true })) {
2262
+ Logger.log(message, logEntryType);
2263
+ }
2235
2264
  }
2236
2265
  if (kind === BuildMessageType.ERROR && errors.length > 0) {
2237
2266
  return [];
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  TypeScriptProject
3
- } from "./XQW6Y6DJ.js";
3
+ } from "./SFYLP6W7.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 "./XQW6Y6DJ.js";
5
+ } from "./SFYLP6W7.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.6.3");
33
+ console.log("1.6.5");
34
34
  process.exit(0);
35
35
  }
36
36
  var typeScriptOptions = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@d1g1tal/tsbuild",
3
3
  "author": "D1g1talEntr0py",
4
- "version": "1.6.3",
4
+ "version": "1.6.5",
5
5
  "license": "MIT",
6
6
  "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.",
7
7
  "homepage": "https://github.com/D1g1talEntr0py/tsbuild#readme",
@@ -43,7 +43,7 @@
43
43
  "tsbuild": "./dist/tsbuild.js"
44
44
  },
45
45
  "dependencies": {
46
- "@d1g1tal/watchr": "^1.0.3",
46
+ "@d1g1tal/watchr": "^1.0.4",
47
47
  "esbuild": "^0.27.4",
48
48
  "magic-string": "^0.30.21"
49
49
  },
@@ -55,7 +55,7 @@
55
55
  "@typescript-eslint/eslint-plugin": "^8.57.1",
56
56
  "@typescript-eslint/parser": "^8.57.1",
57
57
  "@vitest/coverage-v8": "^4.1.0",
58
- "eslint": "^10.0.3",
58
+ "eslint": "^10.1.0",
59
59
  "eslint-plugin-jsdoc": "^62.8.0",
60
60
  "fs-monkey": "^1.1.0",
61
61
  "memfs": "^4.56.11",