Compare commits

..

191 Commits

Author SHA1 Message Date
0eaca1f3d1 4.0.1 2025-12-13 23:41:01 +00:00
aeaa957efa 4.0.0 2025-12-13 23:00:52 +00:00
e5fcbb9a09 BREAKING(structure): modernize internal structure and support unpacking 2025-12-13 22:59:58 +00:00
4ebc37fa5a 3.1.4 2025-12-13 11:44:20 +00:00
5bfe60927e fix(npmextra.json): update to new format 2025-12-13 11:44:03 +00:00
9e6b91b891 v3.1.3 2025-12-13 09:44:15 +00:00
e40e8f6a77 fix(npmextra): Align npmextra.json package name with package.json (@git.zone/tsbuild) 2025-12-13 09:44:15 +00:00
ebe7afce82 v3.1.2 2025-11-28 08:44:42 +00:00
27aa318054 fix(TsBuild): Set default TypeScript target to ESNext 2025-11-28 08:44:42 +00:00
9a071ce82f v3.1.1 2025-11-27 23:04:13 +00:00
ad227ded73 fix(compiler): Update default TypeScript target to ES2024 2025-11-27 23:04:13 +00:00
c66a941aaf v3.1.0 2025-11-17 12:25:31 +00:00
a3b58dda39 feat(tsbuild.classes): Update default TypeScript lib to lib.esnext.d.ts 2025-11-17 12:25:31 +00:00
4b29107130 v3.0.0 2025-11-17 11:57:32 +00:00
09adbc1965 BREAKING CHANGE(TsBuild): Stop forcing emitDecoratorMetadata in protected compiler defaults 2025-11-17 11:57:32 +00:00
7fb571d4f6 v2.7.3 2025-11-17 11:51:56 +00:00
6b32dced5a fix(tsbuild.classes): Remove duplicate emitDecoratorMetadata from default compiler options and centralize it in protected defaults 2025-11-17 11:51:56 +00:00
df39aa48d0 v2.7.2 2025-11-17 08:08:21 +00:00
a4863bc761 fix(compilerOptions): Remove experimentalDecorators and useDefineForClassFields from default TypeScript compiler options 2025-11-17 08:08:21 +00:00
b38ef6cf82 2.7.1 2025-11-02 06:13:34 +00:00
8b50cd3090 fix(readme): Update documentation: expand README with usage, CLI and API examples; add readme.hints.md project memory 2025-11-02 06:13:34 +00:00
c6ab493efc 2.7.0 2025-11-02 05:31:55 +00:00
82ae8a0e4a feat(tsbuild): Add tsconfig.json support and safer compiler option merging; protect critical options; apply path and enum transforms; bump dependencies. 2025-11-02 05:31:55 +00:00
787becc4d3 2.6.8 2025-08-29 17:14:58 +00:00
746ca8767a fix(tsbuild): Avoid process.exit in library, add confirmskiplibcheck flag, improve CLI exit handling and JSON/quiet modes, update test script 2025-08-29 17:14:58 +00:00
55c1a2953a 2.6.7 2025-08-18 02:19:15 +00:00
8e9fcb8135 fix(tspublish): Bump @git.zone/tspublish dependency to ^1.10.3 2025-08-18 02:19:15 +00:00
18573c777d 2.6.6 2025-08-18 00:32:26 +00:00
fa654b83e3 fix(dependencies): Update dependency @git.zone/tspublish to ^1.10.2 2025-08-18 00:32:26 +00:00
aa3c83cd95 2.6.5 2025-08-18 00:29:51 +00:00
20ed41df42 fix(dependencies): Bump dependencies and add pnpm-workspace configuration 2025-08-18 00:29:51 +00:00
7162476f7f 2.6.4 2025-05-24 00:30:16 +00:00
1b012628eb fix(dependencies): Add .npmrc and update dependency versions for smartfile and tstest 2025-05-24 00:30:16 +00:00
4081086621 2.6.3 2025-05-21 18:06:46 +00:00
b9cf09ccba fix(tsbuild): minor maintenance updates and documentation improvements 2025-05-21 18:06:46 +00:00
8463fbc78a 2.6.2 2025-05-21 17:59:28 +00:00
0164eb51a1 fix(npm configuration): Remove .npmrc file to default npm registry behavior 2025-05-21 17:59:28 +00:00
a305dd89dd 2.6.1 2025-05-21 17:58:22 +00:00
fa6b053ee0 fix(tsbuild.classes): Improve error diagnostics handling by removing legacy helper and integrating more robust error summaries in the compilation process 2025-05-21 17:58:22 +00:00
adfba21c67 2.6.0 2025-05-21 13:38:21 +00:00
050e41cdf9 feat(tsbuild): Improve task logging and update dependencies 2025-05-21 13:38:21 +00:00
f220a11caa 2.5.2 2025-05-21 00:20:45 +00:00
0909fa306a fix(tsbuild): Improve diagnostic error handling and summary reporting for TypeScript compilation by refactoring diagnostic processing and adding pre-emit error checks. 2025-05-21 00:20:45 +00:00
88c0601c03 2.5.1 2025-05-15 14:07:26 +00:00
9645f27942 fix(commitinfo): Update commit information and metadata to synchronize release data 2025-05-15 14:07:26 +00:00
b73aa4f21f 2.5.0 2025-05-15 13:53:08 +00:00
d9d6878a9f feat(cli): Enhance type checking in CLI by adding default file pattern handling 2025-05-15 13:53:08 +00:00
30506da84c 2.4.1 2025-05-15 09:55:29 +00:00
52b4d8f944 fix(cli): Improve TS folder compilation order display in CLI 2025-05-15 09:55:29 +00:00
57d2726f6b 2.4.0 2025-05-15 09:31:57 +00:00
960fc5f213 feat(cli): Add new check command for type checking and update compiler options handling 2025-05-15 09:31:57 +00:00
6d68f35a9a 2.3.2 2025-03-20 15:30:52 +00:00
0378b9feca fix(compileGlobStringObject): Fix duplicate file outputs in glob pattern processing 2025-03-20 15:30:52 +00:00
5ecf4b7125 2.3.1 2025-03-20 15:20:27 +00:00
2aa6348cdd fix(compiler): Refactor compiler implementation with consolidated TsBuild class and improved diagnostics handling 2025-03-20 15:20:27 +00:00
9f42670865 2.3.0 2025-03-20 15:16:02 +00:00
6cc9f41bd2 feat(cli): Add emitcheck command to validate TS file emission without generating output 2025-03-20 15:16:02 +00:00
5d32ac85e0 2.2.7 2025-03-17 10:27:36 +00:00
4f2ac6922a fix(compiler): Improve diagnostic checking and error handling in the TypeScript compiler integration 2025-03-17 10:27:36 +00:00
31834e0b3e 2.2.6 2025-03-04 23:13:33 +00:00
c6c94866bb fix(package): Fix repository URL in package.json 2025-03-04 23:13:33 +00:00
09a648b435 2.2.5 2025-03-04 23:00:51 +00:00
065e0baaf7 fix(package.json): Update repository URLs in package metadata. 2025-03-04 23:00:51 +00:00
89d32617ce 2.2.4 2025-03-04 19:41:37 +00:00
094702b917 fix(core): Fix compiler logic to remove duplicate compiled files and improve glob pattern handling. 2025-03-04 19:41:37 +00:00
c2aec98da3 2.2.3 2025-03-04 19:41:05 +00:00
289421206c fix(exports): Fixed duplicate file compilation in compileGlobStringObject, fixes #1 2025-03-04 19:41:05 +00:00
1f9870ffbb 2.2.2 2025-01-28 12:06:06 +01:00
8f7f34d61e fix(ci): Remove GitLab CI configuration 2025-01-28 12:06:06 +01:00
2e23919503 2.2.1 2025-01-28 12:04:59 +01:00
f601cf8eb8 fix(core): Update dependencies to improve stability and performance. 2025-01-28 12:04:58 +01:00
15af2e4e2d 2.2.0 2024-11-05 01:48:49 +01:00
59c6e72187 feat(cli): Enhance CLI for TypeScript folder compilation ordering based on rank and predefined rules. 2024-11-05 01:48:49 +01:00
5369e8d931 2.1.85 2024-10-27 13:35:55 +01:00
88444e835f fix(compiler): Improve path handling in compiler options 2024-10-27 13:35:54 +01:00
4892c8f7ae 2.1.84 2024-07-22 00:07:08 +02:00
687f01d1a3 fix(cli): Fixed transpilation order issue in tsfolders command 2024-07-22 00:07:07 +02:00
071b3e222f 2.1.83 2024-07-21 17:33:22 +02:00
fa41dbf332 fix(cli): Ensure ts_shared folder is compiled sedond to interfaces when present 2024-07-21 17:33:21 +02:00
c45a216379 2.1.82 2024-06-24 12:06:19 +02:00
00046837a7 fix(core): Minor improvements and optimizations in core TypeScript compiler integration. 2024-06-24 12:06:18 +02:00
ed22b539f2 2.1.81 2024-06-24 12:00:38 +02:00
43c7021aea fix(dependencies): Update dependencies to latest versions 2024-06-24 12:00:37 +02:00
b21009f815 2.1.80 2024-05-17 19:19:55 +02:00
deba95dfa6 fix(core): update 2024-05-17 19:19:55 +02:00
50e6fec9b0 2.1.79 2024-05-17 19:19:04 +02:00
f7c2a67d81 fix(core): update 2024-05-17 19:19:03 +02:00
91e3502965 2.1.78 2024-05-17 19:18:26 +02:00
0afddbefce fix(core): update 2024-05-17 19:18:26 +02:00
94e879d9fe 2.1.77 2024-05-17 19:17:52 +02:00
43e2a1d777 fix(core): update 2024-05-17 19:17:51 +02:00
9a1f57b92d 2.1.76 2024-05-14 01:20:50 +02:00
0064f63ddb fix(core): update 2024-05-14 01:20:49 +02:00
6e90bdda36 2.1.75 2024-05-10 16:54:46 +02:00
82cce58d69 fix(core): update 2024-05-10 16:54:45 +02:00
a316cc6725 2.1.74 2024-05-10 16:52:43 +02:00
05738a5c94 fix(core): update 2024-05-10 16:52:43 +02:00
651a06ff7b 2.1.73 2024-05-10 15:55:25 +02:00
2f04474dab fix(core): update 2024-05-10 15:55:25 +02:00
d88af368a4 2.1.72 2024-01-08 16:17:15 +01:00
e60816be4c fix(core): update 2024-01-08 16:17:15 +01:00
a9e383ba2d 2.1.71 2024-01-08 16:06:31 +01:00
a3e1cf9e3d fix(core): update 2024-01-08 16:06:30 +01:00
8790886815 2.1.70 2023-08-26 15:34:43 +02:00
611bd0d542 fix(core): update 2023-08-26 15:34:42 +02:00
b182fa5c0a 2.1.69 2023-08-26 14:40:49 +02:00
03d4996284 fix(core): update 2023-08-26 14:40:48 +02:00
6b2d2c9cc3 2.1.68 2023-08-26 13:30:07 +02:00
e9dc5ae444 fix(core): update 2023-08-26 13:30:06 +02:00
24574bdb4d 2.1.67 2023-08-26 13:27:51 +02:00
2fe98bec22 fix(core): update 2023-08-26 13:27:51 +02:00
87b6a4efb2 2.1.66 2023-06-03 16:45:40 +02:00
dc81d99ac3 fix(core): update 2023-06-03 16:45:40 +02:00
5d0c9b0326 2.1.65 2022-08-03 19:34:52 +02:00
f27c27bd31 fix(core): update 2022-08-03 19:34:51 +02:00
67e42cc0bd 2.1.64 2022-08-03 19:05:31 +02:00
4a71b92868 fix(core): update 2022-08-03 19:05:31 +02:00
c251bce006 2.1.63 2022-05-25 23:43:44 +02:00
f83b872f31 fix(core): update 2022-05-25 23:43:44 +02:00
4e93832683 2.1.62 2022-05-25 11:52:42 +02:00
4d37e880a3 fix(core): update 2022-05-25 11:52:42 +02:00
b7e6412b7a 2.1.61 2022-03-24 18:24:05 +01:00
113717886c fix(core): update 2022-03-24 18:24:05 +01:00
98b4d4bc5b 2.1.60 2022-03-18 22:12:28 +01:00
83f496f0ca fix(core): update 2022-03-18 22:12:27 +01:00
662b7d4e6c 2.1.59 2022-03-18 14:33:22 +01:00
ab43ea0a10 fix(core): update 2022-03-18 14:33:22 +01:00
0561f655cb 2.1.58 2022-03-18 14:32:35 +01:00
b0b1be70ab fix(core): update 2022-03-18 14:32:35 +01:00
dda03bad45 2.1.57 2022-03-18 14:31:48 +01:00
a0b9f8d8f3 fix(core): update 2022-03-18 14:31:48 +01:00
da823e51d5 2.1.56 2022-03-15 20:06:48 +01:00
b68aa06941 fix(core): update 2022-03-15 20:06:48 +01:00
f5ee2c2c70 2.1.55 2022-03-15 10:21:15 +01:00
0c018e6448 fix(core): update 2022-03-15 10:21:15 +01:00
565c66e4e6 2.1.54 2022-03-15 10:18:09 +01:00
72ad77446c fix(core): update 2022-03-15 10:18:08 +01:00
59ce28395f 2.1.53 2022-03-15 10:04:59 +01:00
cddd7ffd25 fix(core): update 2022-03-15 10:04:59 +01:00
48ef556e6b 2.1.52 2022-03-15 09:59:31 +01:00
0645beb199 fix(core): update 2022-03-15 09:59:30 +01:00
97f52d1016 2.1.51 2022-03-15 09:45:49 +01:00
8d725e2e11 fix(core): update 2022-03-15 09:45:49 +01:00
42b83b888e 2.1.50 2022-03-14 21:46:42 +01:00
832664b667 fix(core): update 2022-03-14 21:46:41 +01:00
4925809030 2.1.49 2022-03-14 16:04:07 +01:00
622a080b3c fix(core): update 2022-03-14 16:04:07 +01:00
18747dbc83 2.1.48 2022-03-12 23:15:28 +01:00
210a7a07f7 fix(core): update 2022-03-12 23:15:28 +01:00
211d42c36b 2.1.47 2022-03-12 21:09:33 +01:00
cfbe4bbcb9 fix(core): update 2022-03-12 21:09:33 +01:00
72350a49ec 2.1.46 2022-03-12 19:42:49 +01:00
d4a0d03301 fix(core): update 2022-03-12 19:42:49 +01:00
75f506ba0e 2.1.45 2022-03-12 19:32:15 +01:00
035207f4f9 fix(core): update 2022-03-12 19:32:15 +01:00
6d2d48af9d 2.1.44 2022-03-12 19:05:47 +01:00
9adbce12e1 fix(core): update 2022-03-12 19:05:46 +01:00
318189c7b2 2.1.43 2022-03-11 18:08:24 +01:00
167483b909 2.1.42 2022-03-11 18:08:19 +01:00
67db62e7d5 2.1.41 2022-03-11 18:07:07 +01:00
ddf76c31b9 2.1.40 2022-03-11 18:07:02 +01:00
6a0a53adda fix(core): update 2022-03-11 18:07:01 +01:00
eb1a70ea70 2.1.39 2022-03-11 18:05:10 +01:00
c10ddd3c1e 2.1.38 2022-03-11 18:05:03 +01:00
9c9f7ae1dc fix(core): update 2022-03-11 18:05:02 +01:00
70245584d7 2.1.37 2022-03-11 17:45:57 +01:00
f11d7c6cb0 2.1.36 2022-03-11 17:45:51 +01:00
dc7980e619 fix(core): update 2022-03-11 17:45:51 +01:00
686a428624 2.1.35 2022-03-11 17:41:00 +01:00
6772bb439c 2.1.34 2022-03-11 17:40:52 +01:00
3acab250db fix(core): update 2022-03-11 17:40:52 +01:00
ec90a006ce 2.1.33 2022-03-11 17:39:10 +01:00
30ba0c99fd 2.1.32 2022-03-11 17:38:54 +01:00
17a7749f23 fix(core): update 2022-03-11 17:38:54 +01:00
3926a66d1d 2.1.31 2022-03-11 17:36:32 +01:00
6eb5755aed 2.1.30 2022-03-11 17:36:27 +01:00
165113f132 fix(core): update 2022-03-11 17:36:27 +01:00
7ee9935a9a 2.1.29 2022-01-19 17:59:08 +01:00
5128d450bc fix(core): update 2022-01-19 17:59:07 +01:00
8708b1442d 2.1.28 2021-10-06 13:33:35 +02:00
0e39dfecba fix(core): update 2021-10-06 13:33:35 +02:00
b4e8accad6 2.1.27 2021-09-08 22:18:27 +02:00
1415d66ffb fix(core): update 2021-09-08 22:18:26 +02:00
38e3eed003 2.1.26 2021-08-17 12:13:52 +02:00
466095ed26 fix(core): update 2021-08-17 12:13:51 +02:00
3d8041fdde 2.1.25 2020-08-10 22:10:20 +00:00
f021525f9e fix(core): update 2020-08-10 22:10:20 +00:00
63fa2e84d4 2.1.24 2020-05-14 09:51:22 +00:00
af66bd8ff1 fix(core): update 2020-05-14 09:51:22 +00:00
53f4e402f5 2.1.23 2020-04-30 09:02:43 +00:00
2f57c26b87 fix(core): update 2020-04-30 09:02:43 +00:00
43 changed files with 11188 additions and 1909 deletions

1
.gitignore vendored
View File

@@ -18,3 +18,4 @@ dist/
dist_*/
# custom
.claude

View File

@@ -1,126 +0,0 @@
# gitzone ci_default
image: registry.gitlab.com/hosttoday/ht-docker-node:npmci
cache:
paths:
- .npmci_cache/
key: '$CI_BUILD_STAGE'
stages:
- security
- test
- release
- metadata
# ====================
# security stage
# ====================
mirror:
stage: security
script:
- npmci git mirror
tags:
- lossless
- docker
- notpriv
snyk:
image: registry.gitlab.com/hosttoday/ht-docker-node:snyk
stage: security
script:
- npmci npm prepare
- npmci command npm install --ignore-scripts
- npmci command snyk test
tags:
- lossless
- docker
- notpriv
# ====================
# test stage
# ====================
testStable:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci npm test
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- lossless
- docker
- priv
testBuild:
stage: test
script:
- npmci npm prepare
- npmci node install stable
- npmci npm install
- npmci command npm run build
coverage: /\d+.?\d+?\%\s*coverage/
tags:
- lossless
- docker
- notpriv
release:
stage: release
script:
- npmci node install stable
- npmci npm publish
only:
- tags
tags:
- lossless
- docker
- notpriv
# ====================
# metadata stage
# ====================
codequality:
stage: metadata
allow_failure: true
script:
- npmci command npm install -g tslint typescript
- npmci npm prepare
- npmci npm install
- npmci command "tslint -c tslint.json ./ts/**/*.ts"
tags:
- lossless
- docker
- priv
trigger:
stage: metadata
script:
- npmci trigger
only:
- tags
tags:
- lossless
- docker
- notpriv
pages:
stage: metadata
script:
- npmci node install lts
- npmci command npm install -g @gitzone/tsdoc
- npmci npm prepare
- npmci npm install
- npmci command tsdoc
tags:
- lossless
- docker
- notpriv
only:
- tags
artifacts:
expire_in: 1 week
paths:
- public
allow_failure: true

4
.snyk
View File

@@ -1,4 +0,0 @@
# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities.
version: v1.13.3
ignore: {}
patch: {}

24
.vscode/launch.json vendored
View File

@@ -2,28 +2,10 @@
"version": "0.2.0",
"configurations": [
{
"name": "current file",
"type": "node",
"command": "npm test",
"name": "Run npm test",
"request": "launch",
"args": [
"${relativeFile}"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
},
{
"name": "test.ts",
"type": "node",
"request": "launch",
"args": [
"test/test.ts"
],
"runtimeArgs": ["-r", "@gitzone/tsrun"],
"cwd": "${workspaceRoot}",
"protocol": "inspector",
"internalConsoleOptions": "openOnSessionStart"
"type": "node-terminal"
}
]
}

View File

@@ -15,7 +15,7 @@
"properties": {
"projectType": {
"type": "string",
"enum": ["website", "element", "service", "npm"]
"enum": ["website", "element", "service", "npm", "wcc"]
}
}
}

429
changelog.md Normal file
View File

@@ -0,0 +1,429 @@
# Changelog
## 2025-12-13 - 3.1.3 - fix(npmextra)
Align npmextra.json package name with package.json (@git.zone/tsbuild)
- Corrected npmPackagename in npmextra.json from "@gitzone/tsbuild" to "@git.zone/tsbuild" to match package.json and README
- Metadata-only change: no code or API behavior affected
## 2025-11-28 - 3.1.2 - fix(TsBuild)
Set default TypeScript target to ESNext
- Default compiler target changed from ScriptTarget.ES2024 to ScriptTarget.ESNext in ts/tsbuild.classes.tsbuild.ts
- Aligns the default compiler options with documentation and ensures use of the latest language features
## 2025-11-27 - 3.1.1 - fix(compiler)
Update default TypeScript target to ES2024
- Default compiler option 'target' changed from ScriptTarget.ESNext to ScriptTarget.ES2024 in ts/tsbuild.classes.tsbuild.ts
- Aligns emitted code to ES2024 language features by default
## 2025-11-17 - 3.1.0 - feat(tsbuild.classes)
Update default TypeScript lib to lib.esnext.d.ts
- Changed default compilerOptions.lib from ['lib.dom.d.ts', 'lib.es2022.d.ts'] to ['lib.dom.d.ts', 'lib.esnext.d.ts'] in compilerOptionsDefault.
- Allows newer ECMAScript/DOM features by default when compiling with tsbuild (affects emitted types and available globals).
- Behavioral default change only — no public API changes; callers can still override lib via tsconfig, programmatic options, or CLI.
## 2025-11-17 - 3.0.0 - BREAKING CHANGE(TsBuild)
Stop forcing emitDecoratorMetadata in protected compiler defaults
- Removed emitDecoratorMetadata from the set of protected/critical default compiler options.
- Projects that relied on tsbuild automatically enabling emitDecoratorMetadata (for DI frameworks like NestJS, TypeORM, Inversify) must now enable it explicitly in their tsconfig.json or via programmatic/CLI options.
- No other protected defaults were changed; outDir, noEmitOnError, declaration and inlineSourceMap remain enforced.
- Behavior is now more permissive: decorator metadata generation is controlled by user configuration rather than being forced by tsbuild.
## 2025-11-17 - 2.7.3 - fix(tsbuild.classes)
Remove duplicate emitDecoratorMetadata from default compiler options and centralize it in protected defaults
- Removed emitDecoratorMetadata from compilerOptionsDefault in ts/tsbuild.classes.tsbuild.ts to avoid duplicate configuration.
- emitDecoratorMetadata remains enforced via getCriticalDefaults(), ensuring decorator metadata support is protected from tsconfig.json overrides.
- Prevents inconsistencies during compiler option merging by centralizing the decorator-related setting.
## 2025-11-17 - 2.7.2 - fix(compilerOptions)
Remove experimentalDecorators and useDefineForClassFields from default TypeScript compiler options
- Removed experimentalDecorators from compilerOptionsDefault in ts/tsbuild.classes.tsbuild.ts
- Removed useDefineForClassFields from compilerOptionsDefault in ts/tsbuild.classes.tsbuild.ts
- Default compiler options now rely on TypeScript's upstream defaults for decorator and class field behavior
- If your project relies on these settings, re-enable them in your tsconfig.json or pass them via the programmatic API / CLI
## 2025-11-02 - 2.7.1 - fix(readme)
Update documentation: expand README with usage, CLI and API examples; add readme.hints.md project memory
- Add readme.hints.md: new project memory / quick reference with public API and CLI summaries
- Expand and restructure readme.md: more comprehensive Quick Start, CLI Commands, API Reference, configuration, examples and troubleshooting
- Clarify protected compiler options, default compiler options, path transformation behavior and error-handling patterns
- Docs-only change — no source code or behavioral changes
## 2025-11-02 - 2.7.0 - feat(tsbuild)
Add tsconfig.json support and safer compiler option merging; protect critical options; apply path and enum transforms; bump dependencies.
- Add robust tsconfig.json reading with graceful fallback when no tsconfig is present or it is invalid
- Merge compiler options in a clear priority order (defaults -> tsconfig -> protected defaults -> programmatic -> CLI flags)
- Introduce protected (critical) compiler options that cannot be overridden by tsconfig.json: outDir, noEmitOnError, declaration, emitDecoratorMetadata, inlineSourceMap
- Convert string values from tsconfig (target, module, moduleResolution) to TypeScript enum values where applicable; special-case NodeNext
- Transform tsconfig path mappings by replacing './ts_' with './dist_ts_' to keep runtime path resolution consistent with compiled output
- Expose getCriticalDefaults helper and adjust mergeCompilerOptions to apply protected defaults before programmatic and CLI overrides
- Update README with documentation for tsconfig support, merge order, protected compiler options, and example tsconfig
- Bump dependencies/devDependencies: @push.rocks/smartcli ^4.0.19, @push.rocks/smartlog ^3.1.10, typescript 5.9.3, @git.zone/tsrun ^1.6.2, @git.zone/tstest ^2.7.0
## 2025-08-29 - 2.6.8 - fix(tsbuild)
Avoid process.exit in library, add confirmskiplibcheck flag, improve CLI exit handling and JSON/quiet modes, update test script
- Changed package.json test script from "tsrun test/test.ts --verbose" to "tstest test/test.ts --verbose".
- Library no longer calls process.exit from compile and compileWithErrorTracking; errors are returned or thrown so callers can decide process termination.
- skipLibCheck behavior updated: delay/warning only happens when --confirmskiplibcheck is present; otherwise a short informational note is printed (suppressed in --quiet/--json).
- CLI now awaits compileGlobStringObject calls and inspects a final error summary attached to argv to decide process.exit(1) when errors occurred.
- compileGlobStringObject/exports now respect --quiet and --json modes, emit a JSON summary when --json is used, and attach the final error summary to argv so the CLI can determine exit behavior.
## 2025-08-18 - 2.6.7 - fix(tspublish)
Bump @git.zone/tspublish dependency to ^1.10.3
- Updated dependency @git.zone/tspublish from ^1.10.2 to ^1.10.3 in package.json
## 2025-08-18 - 2.6.6 - fix(dependencies)
Update dependency @git.zone/tspublish to ^1.10.2
- Bumped @git.zone/tspublish in package.json from ^1.10.1 to ^1.10.2
## 2025-08-18 - 2.6.5 - fix(dependencies)
Bump dependencies and add pnpm-workspace configuration
- Updated @git.zone/tspublish from ^1.9.1 to ^1.10.1
- Updated @push.rocks/smartfile from ^11.2.4 to ^11.2.7
- Updated @push.rocks/smartpath from ^5.0.18 to ^6.0.0
- Updated typescript from 5.8.3 to 5.9.2
- Updated devDependency @git.zone/tstest from ^1.10.1 to ^2.3.4
- Added pnpm-workspace.yaml with onlyBuiltDependencies list (esbuild, mongodb-memory-server, puppeteer)
## 2025-05-24 - 2.6.4 - fix(dependencies)
Add .npmrc and update dependency versions for smartfile and tstest
- Add .npmrc with registry configuration for npm
- Bump @push.rocks/smartfile version from ^11.2.3 to ^11.2.4
- Bump @git.zone/tstest version from ^1.9.0 to ^1.10.1
## 2025-05-21 - 2.6.3 - fix(tsbuild)
minor maintenance updates and documentation improvements
- Updated commit metadata to align with project version
- Refined CLI command parsing and diagnostics logging for better clarity
- Improved code readability in compiler options merging
## 2025-05-21 - 2.6.2 - fix(npm configuration)
Remove .npmrc file to default npm registry behavior
- Deleted .npmrc file that hard-coded the npm registry URL to https://registry.npmjs.org/
- This change leverages npm's default registry settings and reduces configuration clutter
## 2025-05-21 - 2.6.1 - fix(tsbuild.classes)
Improve error diagnostics handling by removing legacy helper and integrating more robust error summaries in the compilation process
- Removed the handleDiagnostics method and its legacy usage
- Replaced legacy calls with inline processDiagnostics checks for pre-emit and emit phases
- Combined error summaries for clearer reporting during emit checks and type validation
- Enhanced error output to guide users on resolving TypeScript errors before emission
## 2025-05-21 - 2.6.0 - feat(tsbuild)
Improve task logging and update dependencies
- Add .npmrc file with npm registry configuration
- Update test script to use '--verbose' flag
- Bump dependency versions for @push.rocks packages and TypeScript
- Enhance TsBuild logging by incorporating task info (e.g. task number, total tasks, output folder, and compile durations)
- Propagate task info in compileFileArrayWithErrorTracking for better task tracking
## 2025-05-21 - 2.5.2 - fix(tsbuild)
Improve diagnostic error handling and summary reporting for TypeScript compilation by refactoring diagnostic processing and adding pre-emit error checks.
- Introduce a dedicated processDiagnostics function that categorizes errors by file and computes error totals.
- Refactor displayErrorSummary to provide clearer, color-coded output of error details.
- Add pre-emit error checking in compileWithErrorTracking to prevent emission when errors exist.
- Consolidate error summary merging in compileFileArrayWithErrorTracking for improved reporting.
## 2025-05-15 - 2.5.1 - fix(commitinfo)
Update commit information and metadata to synchronize release data
- Regenerated the commitinfo file with current version details
- Maintained existing functionality with no functional code changes
## 2025-05-15 - 2.5.0 - feat(cli)
Enhance type checking in CLI by adding default file pattern handling
- When no TypeScript file or glob pattern is provided, the CLI now performs a default type checking sequence.
- First checks 'ts/**/*' files with standard options, then checks 'test/**/*' files with skiplibcheck enabled.
- Improved logging to indicate file discovery and check results, ensuring clear feedback for users.
## 2025-05-15 - 2.4.1 - fix(cli)
Improve TS folder compilation order display in CLI
- Refactor folder compilation output to use a bordered, tabular format with order numbering
- Enhance readability of TS folder compilation plan in the CLI output
## 2025-05-15 - 2.4.0 - feat(cli)
Add new 'check' command for type checking and update compiler options handling
- Introduced a new 'check' command to verify TypeScript files without emitting output
- Updated CLI error messages and logging for better clarity
- Replaced '--allowimplicitany' flag with '--disallowimplicitany' to reflect new default behavior
- Modified compiler options default settings (noImplicitAny now set to false) for more flexible type handling
- Refined diagnostic output in tsbuild class for improved error reporting
- Updated .gitignore to exclude the .claude file
- Enhanced documentation in readme and implementation plan files
## 2025-03-20 - 2.3.2 - fix(compileGlobStringObject)
Fix duplicate file outputs in glob pattern processing
- Removed duplicate concatenation of compiled files in compileGlobStringObject
- Ensured unique file paths are used during TypeScript compilation
## 2025-03-20 - 2.3.1 - fix(compiler)
Refactor compiler implementation with consolidated TsBuild class and improved diagnostics handling
- Removed legacy tsbuild.classes.compiler.ts and introduced tsbuild.classes.tsbuild.ts
- Unified compiler options merging, reading tsconfig.json, and diagnostics reporting within the TsBuild class
- Updated exports to reference the new compiler class implementation for backward compatibility
## 2025-03-20 - 2.3.0 - feat(cli)
Add emitcheck command to validate TS file emission without generating output
- Implemented the emitcheck CLI command to allow checking if TypeScript files can be emitted without producing files
- Updated tsbuild.classes.compiler.ts to include the emitCheck function
- Enhanced documentation in readme.md to describe the new emitcheck usage with examples
## 2025-03-17 - 2.2.7 - fix(compiler)
Improve diagnostic checking and error handling in the TypeScript compiler integration
- Added pre-emit diagnostics check to log errors before emitting
- Process exits on encountering pre-emit errors to ensure build correctness
- Enhanced logging for emit diagnostics to aid debugging
## 2025-03-04 - 2.2.6 - fix(package)
Fix repository URL in package.json
- Updated repository URL in package.json to point to the correct Git repository.
## 2025-03-04 - 2.2.5 - fix(package.json)
Update repository URLs in package metadata.
- Corrected the 'bugs.url' and 'homepage' fields with the accurate URLs.
## 2025-03-04 - 2.2.4 - fix(core)
Fix compiler logic to remove duplicate compiled files and improve glob pattern handling.
- Resolved an issue causing duplicate file compilations when using compileGlobStringObject.
- Improved handling of glob patterns to ensure accurate compilation paths.
## 2025-03-04 - 2.2.3 - fix(exports)
Fixed duplicate file compilation in compileGlobStringObject
- Enhanced type safety checks for glob pattern compilation keys.
- Ensured file list transformations include type checks.
- Fixed issue with duplicate compilation results in compileGlobStringObject.
## 2025-01-28 - 2.2.2 - fix(ci)
Remove GitLab CI configuration
## 2025-01-28 - 2.2.1 - fix(core)
Update dependencies to improve stability and performance.
- Updated @git.zone/tspublish from version ^1.7.5 to ^1.9.1.
- Updated @push.rocks/smartfile from version ^11.0.21 to ^11.1.5.
- Updated @push.rocks/smartpromise from version ^4.0.4 to ^4.2.2.
- Updated typescript from version 5.6.3 to 5.7.3.
- Updated @push.rocks/tapbundle from version ^5.0.23 to ^5.5.6.
- Updated @types/node from version ^22.8.7 to ^22.12.0.
## 2024-11-05 - 2.2.0 - feat(cli)
Enhance CLI for TypeScript folder compilation ordering based on rank and predefined rules.
- CLI now supports automatic ordering of TypeScript folders for compilation using tspublish.json based ranking.
- Ensures 'ts_interfaces' and 'ts_shared' are always transpiled first if certain conditions are met.
- Updated TypeScript compilerOptions to support additional path transformations.
- Updated dependencies versions and added '@git.zone/tspublish' in ts/plugins.ts.
## 2024-10-27 - 2.1.85 - fix(compiler)
Improve path handling in compiler options
- Refactored path import in tsbuild.classes.compiler.ts.
- Enhanced mergeCompilerOptions to read paths and baseUrl from tsconfig.json if present.
## 2024-07-22 - 2.1.84 - fix(cli)
Fixed transpilation order issue in tsfolders command
- Corrected the transpilation order so 'ts_shared' is processed before other folders in the 'tsfolders' CLI command.
## 2024-07-21 - 2.1.83 - fix(cli)
Ensure 'ts_shared' folder is compiled first if present
- Added logic to make sure the 'ts_shared' folder is compiled first when running 'tsfolders' command.
## 2024-06-24 - 2.1.82 - fix(core)
Minor improvements and optimizations in core TypeScript compiler integration.
- Updated TypeScript dependency to latest version 5.5.2.
- Enhanced logging in compiler process.
- Improved handling of CLI commands for better flexibility.
- Compiled output directories are now more structured.
- Refactored internal compiler options merge function.
## 2024-06-24 - 2.1.81 - fix(dependencies)
Update dependencies to latest versions
- Upgraded @push.rocks/smartcli from ^4.0.10 to ^4.0.11
- Upgraded @push.rocks/smartfile from ^11.0.14 to ^11.0.21
- Upgraded @push.rocks/smartlog from ^3.0.3 to ^3.0.7
- Upgraded @push.rocks/smartpromise from ^4.0.3 to ^4.0.4
- Upgraded typescript from 5.4.5 to 5.5.2
- Upgraded @git.zone/tsrun from ^1.2.46 to ^1.2.47
- Upgraded @types/node from ^20.12.11 to ^20.14.8
## 2024-05-17 - 2.1.80 - core
Fix multiple core issues.
- Various small fixes and updates to the core functionality.
## 2024-05-17 - 2.1.76 to 2.1.79 - core
Routine core updates and fixes.
- Several minor enhancements and bug fixes.
## 2024-05-14 - 2.1.74 to 2.1.75 - core
General core maintenance updates.
- Core updated to fix minor bugs.
## 2024-05-10 - 2.1.72 to 2.1.73 - core
Minor core updates.
- Improvements and fixes to core components.
## 2024-01-08 - 2.1.70 to 2.1.71 - core
Core functionality enhancements.
- Small fixes and updates in the core.
## 2023-08-26 - 2.1.66 to 2.1.69 - core
Regular core updates and fixes.
- Various updates to improve core functionality.
## 2023-06-03 - 2.1.65 - core
Core maintenance update.
- Fixed issues in core functionality.
## 2022-08-03 - 2.1.63 to 2.1.64 - core
Minor core updates and fixes.
- Updated core functionality with minor fixes.
## 2022-05-25 - 2.1.61 to 2.1.62 - core
Routine core updates.
- Fixed minor bugs in the core.
## 2022-03-24 - 2.1.60 - core
Core functionality update.
- Fixed various minor issues in core.
## 2022-03-18 - 2.1.57 to 2.1.59 - core
Minor core updates and enhancements.
- Updated core components with small fixes.
## 2022-03-15 - 2.1.50 to 2.1.56 - core
Several core bug fixes.
- Fixed various minor issues in the core functionality.
## 2022-03-14 - 2.1.49 - core
Core update.
- Fixed minor bugs in the core.
## 2022-03-12 - 2.1.43 to 2.1.48 - core
General core maintenance updates.
- Core updated to fix minor bugs and improve functionality.
## 2022-03-11 - 2.1.33 to 2.1.42 - core
Core functionality enhancements and fixes.
- Multiple updates to improve core functionality and fix bugs.
## 2022-03-11 - 2.1.29 to 2.1.32 - core
Routine core updates.
- Minor bug fixes and improvements in core functionality.
## 2022-01-19 - 2.1.28 to 2.1.29 - core
Core bug fixes.
- Updated core to address minor issues.
## 2021-10-06 - 2.1.27 - core
Minor core update.
- Fixed core bugs.
## 2021-09-08 - 2.1.26 - core
Core update.
- Fixed minor bugs in the core.
## 2021-08-17 - 2.1.25 - core
Core bug fixes.
- Small updates to improve core functionality.
## 2020-08-11 - 2.1.24 - core
Routine core update.
- Fixed minor bugs in the core.
## 2020-05-14 - 2.1.23 - core
General core updates.
- Fixes to improve core stability.
## 2020-03-13 - 2.1.20 to 2.1.22 - core
Minor core updates.
- Enhancements and fixes for core functionality.
## 2020-03-09 - 2.1.19 - core
Maintenance core update.
- Fixes addressing minor issues in core functionality.
## 2019-08-26 - 2.1.16 to 2.1.17 - core
Routine minor core updates.
- Improvement and fixes within the core functionality.
## 2019-01-27 - 2.1.6 - custom directory compilation
Now picking up TypeScript files correctly.
- Resolved compilation issues with custom directories.
## 2018-12-05 - 2.1.0 to 2.1.1 - core
Minor core updates.
- Small enhancements and fixes applied to core functionality.
## 2018-12-05 - 2.0.22 - cli options
Now support --web for web compilations targeting Google Chrome.
- Added new CLI option for web compilation.
## 2018-07-25 - 2.0.15 to 2.0.19 - various
Multiple fixes across core, dependency, and compiler modules.
- Packagename fix in core.
- Dependency updates.
- Compiler options fixed.
- Initial core updates.

4
cli.child.ts Normal file
View File

@@ -0,0 +1,4 @@
#!/usr/bin/env node
process.env.CLI_CALL = 'true';
import * as cliTool from './ts/index.js';
cliTool.runCli();

5
cli.js
View File

@@ -1,3 +1,4 @@
#!/usr/bin/env node
process.env.CLI_CALL_TSBUILD = 'true'
var index = require("./dist_ts/index.js");
process.env.CLI_CALL = 'true';
const cliTool = await import('./dist_ts/index.js');
cliTool.runCli();

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env node
process.env.CLI_CALL_TSBUILD = 'true';
require('@gitzone/tsrun');
require('./ts/index');
process.env.CLI_CALL = 'true';
import * as tsrun from '@git.zone/tsrun';
tsrun.runPath('./cli.child.js');

View File

@@ -9,9 +9,23 @@
"githost": "gitlab.com",
"gitscope": "gitzone",
"gitrepo": "tsbuild",
"shortDescription": "TypeScript nightly to easily make use of latest features",
"npmPackagename": "@gitzone/tsbuild",
"license": "MIT"
}
"description": "A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.",
"npmPackagename": "@git.zone/tsbuild",
"license": "MIT",
"keywords": [
"TypeScript",
"compilation",
"nightly features",
"CLI tool",
"file compilation",
"glob patterns",
"compiler options",
"development",
"API"
]
}
},
"tsdoc": {
"legal": "\n## License and Legal Information\n\nThis repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. \n\n**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.\n\n### Trademarks\n\nThis project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.\n\n### Company Information\n\nTask Venture Capital GmbH \nRegistered at District court Bremen HRB 35230 HB, Germany\n\nFor any legal inquiries or if you require further information, please contact us via email at hello@task.vc.\n\nBy using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.\n"
}
}

1411
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,57 +1,71 @@
{
"name": "@gitzone/tsbuild",
"version": "2.1.22",
"name": "@git.zone/tsbuild",
"version": "4.0.1",
"private": false,
"description": "TypeScript nightly to easily make use of latest features",
"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",
"typings": "dist/index.d.ts",
"typings": "dist_ts/index.d.ts",
"type": "module",
"bin": {
"tsbuild": "cli.js"
"tsbuild": "./cli.js"
},
"scripts": {
"test": "tsrun test/test.ts",
"testCustom": "node cli.ts.js custom ts_web",
"build": "node cli.ts.js"
"test": "tstest test/test.ts --verbose",
"build": "node cli.ts.js --web",
"buildDocs": "tsdoc"
},
"repository": {
"type": "git",
"url": "git+ssh://git@gitlab.com/pushrocks/tsn.git"
"url": "https://code.foss.global/git.zone/tsbuild.git"
},
"keywords": [
"TypeScript"
"TypeScript",
"compilation",
"nightly features",
"CLI tool",
"file compilation",
"glob patterns",
"compiler options",
"development",
"API"
],
"author": "Lossless GmbH",
"author": "Task Venture Capital GmbH",
"license": "MIT",
"bugs": {
"url": "https://gitlab.com/pushrocks/tsn/issues"
"url": "https://code.foss.global/git.zone/tsbuild/issues"
},
"homepage": "https://gitlab.com/pushrocks/tsn#README",
"homepage": "https://code.foss.global/git.zone/tsbuild#README",
"dependencies": {
"@pushrocks/smartcli": "^3.0.7",
"@pushrocks/smartfile": "^7.0.2",
"@pushrocks/smartlog": "^2.0.19",
"@pushrocks/smartpath": "^4.0.1",
"@pushrocks/smartpromise": "^3.0.2",
"typescript": "^3.5.2"
"@git.zone/tspublish": "^1.10.3",
"@push.rocks/early": "^4.0.4",
"@push.rocks/smartcli": "^4.0.19",
"@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartfile": "^13.1.2",
"@push.rocks/smartfs": "^1.2.0",
"@push.rocks/smartlog": "^3.1.10",
"@push.rocks/smartpath": "^6.0.0",
"@push.rocks/smartpromise": "^4.2.3",
"typescript": "5.9.3"
},
"devDependencies": {
"@gitzone/tsrun": "^1.2.8",
"@pushrocks/tapbundle": "^3.0.13",
"@types/node": "^12.7.2",
"tslint": "^5.19.0",
"tslint-config-prettier": "^1.18.0"
"@git.zone/tsrun": "^2.0.1",
"@git.zone/tstest": "^3.1.3",
"@types/node": "^25.0.1"
},
"files": [
"ts/**/*",
"ts_web/**/*",
"dist/**/*",
"dist_bundle/**/*",
"dist_web/**/*",
"dist_*/**/*",
"dist_ts/**/*",
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
"npmextra.json",
"readme.md"
]
],
"browserslist": [
"last 1 chrome versions"
],
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
}

8010
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

215
readme.hints.md Normal file
View File

@@ -0,0 +1,215 @@
# @git.zone/tsbuild - Project Memory
## Quick Reference
### Public API - Classes
1. **TsCompiler** - Main compilation class with compileFiles, compileGlob, checkTypes, checkEmit methods
2. **TsConfig** - TypeScript configuration management (tsconfig.json handling)
3. **TsPublishConfig** - TsPublish configuration (tspublish.json handling)
4. **TsUnpacker** - Output directory flattening
5. **FsHelpers** - Filesystem utilities (static methods)
6. **TsBuildCli** - CLI command handler
### CLI Commands (5)
1. **tsbuild** (default) - Compiles ./ts/**/*.ts → ./dist_ts/
2. **tsbuild custom <dir1> <dir2>** - Custom directory compilation
3. **tsbuild tsfolders** - Auto-discover ts_* folders, compile in order
4. **tsbuild emitcheck <pattern>** - Validate emit without output
5. **tsbuild check [pattern]** - Type check only
### CLI Flags
- `--skiplibcheck` - Skip .d.ts type checking (shows warning)
- `--confirmskiplibcheck` - Extended warning with 5s pause
- `--disallowimplicitany` - Stricter type checking
- `--commonjs` - Use CommonJS instead of ESNext
- `--quiet` - Suppress non-error output
- `--json` - JSON output format
## Architecture - Modular Structure
### Module Organization
```
ts/
index.ts # Main entry, re-exports all modules
plugins.ts # Dependency imports only
mod_fs/
index.ts # exports
classes.fshelpers.ts # FsHelpers - static filesystem utilities
mod_config/
index.ts # exports
classes.tsconfig.ts # TsConfig - tsconfig.json handling
classes.tspublishconfig.ts # TsPublishConfig - tspublish.json handling
mod_unpack/
index.ts # exports
classes.tsunpacker.ts # TsUnpacker - output flattening
mod_compiler/
index.ts # exports
classes.tscompiler.ts # TsCompiler + legacy compatibility functions
mod_cli/
index.ts # exports
classes.tsbuildcli.ts # TsBuildCli - CLI command handler
```
### Class Responsibilities
**TsCompiler** (`mod_compiler/classes.tscompiler.ts`)
- Core compilation with `compileFiles()`, `compileGlob()`
- Type checking with `checkTypes()`, `checkEmit()`
- Configuration via TsConfig
- Automatic unpacking via TsUnpacker
**TsConfig** (`mod_config/classes.tsconfig.ts`)
- Load and parse tsconfig.json
- Merge configuration with priority order
- Protected defaults handling
**TsPublishConfig** (`mod_config/classes.tspublishconfig.ts`)
- Load and parse tspublish.json
- `shouldUnpack` property (default true)
- `order` property for tsfolders ordering
**TsUnpacker** (`mod_unpack/classes.tsunpacker.ts`)
- Detect nested output structure
- Flatten output directories
- Configurable via TsPublishConfig
**FsHelpers** (`mod_fs/classes.fshelpers.ts`)
- `listFilesWithGlob()` - Glob pattern file listing
- `extractSourceFolder()` - Pattern parsing
- File/directory existence checks
- Directory operations
**TsBuildCli** (`mod_cli/classes.tsbuildcli.ts`)
- All CLI commands registered
- Uses TsCompiler for compilation
## Configuration Priority (5 levels)
1. Default options (hardcoded in TsConfig)
2. tsconfig.json (if exists)
3. Protected defaults (ensure integrity)
4. Programmatic options (function params)
5. CLI flags (highest priority)
### Protected Options
Cannot be overridden by tsconfig.json alone:
- `outDir: 'dist_ts/'` - Path transformation logic
- `noEmitOnError: true` - Build integrity
- `declaration: true` - Library support
- `inlineSourceMap: true` - Debugging
### Path Transformation
- Automatic: `./ts_interfaces``./dist_ts_interfaces`
- In tsconfig paths: `./ts_*``./dist_ts_*` (first array element only)
## Default Compiler Options
- Module: NodeNext (ESM with CommonJS fallback)
- Target: ESNext (latest JavaScript)
- Source Maps: Inline (no separate .map files)
- Declaration Files: ALWAYS generated (protected)
- Output: dist_ts/
- Implicit any: ALLOWED by default
- esModuleInterop: true
- verbatimModuleSyntax: true
## Error Handling
### Error Summary Structure
```typescript
interface IErrorSummary {
errorsByFile: Record<string, Diagnostic[]>
generalErrors: Diagnostic[]
totalErrors: number
totalFiles: number
}
```
### Compile Result Structure
```typescript
interface ICompileResult {
emittedFiles: string[]
errorSummary: IErrorSummary
}
```
## Dependencies Used
- @git.zone/tspublish@^1.10.3 - Module ordering
- @push.rocks/smartcli@^4.0.19 - CLI framework
- @push.rocks/smartfile@^13.1.2 - File content handling
- @push.rocks/smartfs@^1.2.0 - Filesystem operations
- @push.rocks/smartpath@^6.0.0 - Path transformation utilities
- @push.rocks/smartpromise@^4.2.3 - Promise utilities
- @push.rocks/smartdelay@^3.0.5 - Delay utilities
- typescript@5.9.3 - TypeScript compiler
### smartfs Usage
- File listing: `smartfs.directory(path).recursive().filter(pattern).list()`
- File existence: `smartfs.file(path).exists()`
- Directory existence: `smartfs.directory(path).exists()`
- FsHelpers wraps smartfs for glob pattern support
## Unpack Feature
When TypeScript compiles files that import from sibling directories, it creates nested output:
```
dist_ts_core/
ts_core/ ← nested output
ts_shared/ ← pulled-in dependency
```
The unpack feature automatically flattens this to:
```
dist_ts_core/
index.js ← flat
```
### Configuration
- Reads `tspublish.json` from source folder
- `unpack: true` (default if not present) → flatten output
- `unpack: false` → skip unpacking
### Implementation
- `TsUnpacker` class in `mod_unpack/classes.tsunpacker.ts`
- Called automatically after each successful compilation in `TsCompiler.compileGlob()`
## Special Behaviors
### tsfolders Command Ordering
1. Always: ts_interfaces first (if no tspublish.json)
2. Always: ts_shared second (if no tspublish.json)
3. Then: Other folders by `order` property in their tspublish.json
4. Finally: Folders without order property (Infinity)
### check Command Default (No Arguments)
Two-phase check:
1. Phase 1: Type check ts/**/* (strict, include .d.ts)
2. Phase 2: Type check test/**/* (relaxed, skipLibCheck: true)
## Edge Cases
1. **Empty file list** - Returns [], no error
2. **Glob duplicates** - Files compile multiple times
3. **Non-existent files** - TypeScript "file not found" errors
4. **skipLibCheck warning** - 1-line default, 5s pause with --confirmskiplibcheck
5. **Missing tsconfig.json** - Graceful fallback, no error
6. **Module resolution** - --commonjs switches to NodeJs (not NodeNext)
7. **Source maps** - Inline only (not separate .map files)
8. **File filtering** - Only .ts and .tsx; .d.ts and .js ignored
## Build Safety Features
- `noEmitOnError: true` - Prevents broken builds
- Error aggregation before final output
- Protected options ensure integrity
- Pre-emit checks before emit phase
- CLI exit code handling (0=success, 1=error)
## Recent Changes
- 3.1.4+ - Major restructuring with mod_* modules
- Full OO architecture with TsCompiler, TsConfig, TsPublishConfig, TsUnpacker, TsBuildCli, FsHelpers
- Backward compatibility maintained for legacy functions
- Automatic "unpack" feature for nested output directories
- Migrated filesystem operations from smartfile to smartfs

934
readme.md
View File

@@ -1,80 +1,898 @@
# @gitzone/tsbuild
TypeScript nightly to easily make use of latest features
# @git.zone/tsbuild
## Availabililty and Links
* [npmjs.org (npm package)](https://www.npmjs.com/package/@gitzone/tsbuild)
* [gitlab.com (source)](https://gitlab.com/gitzone/tsbuild)
* [github.com (source mirror)](https://github.com/gitzone/tsbuild)
* [docs (typedoc)](https://gitzone.gitlab.io/tsbuild/)
> 🚀 **A powerful, modern TypeScript build tool with smart defaults and full tsconfig.json support**
## Status for master
[![pipeline status](https://gitlab.com/gitzone/tsbuild/badges/master/pipeline.svg)](https://gitlab.com/gitzone/tsbuild/commits/master)
[![coverage report](https://gitlab.com/gitzone/tsbuild/badges/master/coverage.svg)](https://gitlab.com/gitzone/tsbuild/commits/master)
[![npm downloads per month](https://img.shields.io/npm/dm/@gitzone/tsbuild.svg)](https://www.npmjs.com/package/@gitzone/tsbuild)
[![Known Vulnerabilities](https://snyk.io/test/npm/@gitzone/tsbuild/badge.svg)](https://snyk.io/test/npm/@gitzone/tsbuild)
[![TypeScript](https://img.shields.io/badge/TypeScript->=%203.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![node](https://img.shields.io/badge/node->=%2010.x.x-blue.svg)](https://nodejs.org/dist/latest-v10.x/docs/api/)
[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-prettier-ff69b4.svg)](https://prettier.io/)
A production-ready TypeScript compiler that combines flexibility with safety. Built for modern JavaScript development with ESNext, NodeNext modules, and automatic decorator support.
## Usage
## Why tsbuild?
Tsn uses the **next** tagged npm version of typescript
-**Smart tsconfig.json Integration** - Respects all your compiler options with intelligent merging
-**Protected Defaults** - Critical build settings are safeguarded while staying flexible
-**Zero Config** - Works perfectly without tsconfig.json
-**Glob Pattern Support** - Compile multiple directories with a single command
-**Dependency-Aware** - Automatically orders compilation based on module dependencies
-**Type Checking** - Validate code without emitting files
-**CI/CD Ready** - JSON output mode and proper exit codes
-**Library Safe** - Never calls `process.exit()` in library code
-**Modern Defaults** - ESNext, NodeNext modules, decorators out of the box
## Issue Reporting and Security
For reporting issues or vulnerabilities, please visit our repository at [GitLab](https://code.foss.global/git.zone/tsbuild/issues).
## Install
```bash
npm install @git.zone/tsbuild --save-dev
```
or with pnpm:
```bash
pnpm install @git.zone/tsbuild --save-dev
```
## Quick Start
### CLI Usage
**Compile your TypeScript project:**
```bash
npx tsbuild
```
Compiles `./ts/**/*.ts``./dist_ts/`
**Custom directories:**
```bash
npx tsbuild custom src utils
```
Compiles:
- `./src/**/*.ts``./dist_src/`
- `./utils/**/*.ts``./dist_utils/`
**Auto-discover and compile in dependency order:**
```bash
npx tsbuild tsfolders
```
Finds all `ts_*` folders and compiles them respecting dependencies.
### Programmatic Usage
**Basic compilation:**
```typescript
import * as tsn from 'tsn';
import { compileFileArray } from '@git.zone/tsbuild';
let myGlobStringObject = {
'./myTsFolder/**/*.ts': './myDestinationFolder/',
'./someOtherTsFolder/**/*.ts': './myOtherDestinationFolder/'
};
await compileFileArray([
'./src/index.ts',
'./src/utils.ts'
]);
```
let tsOptions = {
target: tsn.ScriptTarget.ES2015,
module: tsn.ModuleKind.CommonJS
};
**Production-ready with error tracking (recommended):**
```typescript
import { compileFileArrayWithErrorTracking } from '@git.zone/tsbuild';
/*
note: since this only works in code, here are the target numbers
enum ScriptTarget {
ES3 = 0,
ES5 = 1,
ES2015 = 2,
ES2016 = 3,
ES2017 = 4,
ESNext = 5,
Latest = 5,
const result = await compileFileArrayWithErrorTracking([
'./src/**/*.ts'
], {
target: 'ES2022',
module: 'NodeNext'
});
if (result.hasErrors) {
console.error('Compilation failed!');
console.error('Files with errors:', result.fileCount);
process.exit(1);
}
and here are the module kinds
enum ModuleKind {
None = 0,
CommonJS = 1,
AMD = 2,
UMD = 3,
System = 4,
ES2015 = 5,
}
*/
console.log('✅ Compilation successful!');
```
let myCwd = process.cwd();
**Advanced glob compilation:**
```typescript
import { compileGlobStringObject } from '@git.zone/tsbuild';
tsn.compileGlobStringObject(
myGlobStringObject, // the glob string object describing from where to compile what to where
tsOptions, // the options for TypeScript
myCwd // a custom cwd, optional, defaults to process.cwd()
await compileGlobStringObject({
'./ts/**/*.ts': './dist_ts',
'./ts_web/**/*.ts': './dist_web'
}, {
target: 'ES2022',
module: 'ESNext'
});
```
## CLI Commands
### 1. Default Build
```bash
npx tsbuild [options]
```
Compiles all TypeScript files from `./ts/` to `./dist_ts/`
**Options:**
- `--web` - Include web-specific compilation
- `--skiplibcheck` - Skip type checking of declaration files (shows 5-second warning)
- `--confirmskiplibcheck` - Skip lib check without warning
- `--disallowimplicitany` - Disallow implicit `any` types
- `--commonjs` - Use CommonJS instead of ESNext modules
- `--json` - Output results as JSON (for CI/CD)
- `--quiet` - Suppress console output
**Examples:**
```bash
# Standard build
npx tsbuild
# Build with JSON output for CI
npx tsbuild --json --quiet
# CommonJS build
npx tsbuild --commonjs
```
### 2. Custom Directories
```bash
npx tsbuild custom <dir1> <dir2> ... [options]
```
Compile specific directories to their corresponding `dist_` folders.
**Examples:**
```bash
# Compile src and utils
npx tsbuild custom src utils
# Creates: ./dist_src/ and ./dist_utils/
# Multiple directories with options
npx tsbuild custom api models services --commonjs
```
### 3. TSFolders (Dependency-Aware)
```bash
npx tsbuild tsfolders [options]
```
Automatically discovers and compiles all `ts_*` folders in dependency order:
1. Prioritizes `ts_interfaces` and `ts_shared` first
2. Reads `tspublish.json` for `order` property
3. Compiles in correct sequence
**Example output:**
```
compiling in this order:
[ 'ts_interfaces', 'ts_shared', 'ts_core', 'ts_utils', 'ts_modules' ]
🔨 [1/5] Compiling ts_interfaces...
✅ [1/5] Task completed in 1234ms
...
```
### 4. Emit Check
```bash
npx tsbuild emitcheck <file_or_pattern> [more...] [options]
```
Validates TypeScript files can be compiled without actually emitting them.
**Examples:**
```bash
# Check specific files
npx tsbuild emitcheck src/main.ts src/utils.ts
# Check with glob patterns
npx tsbuild emitcheck "src/**/*.ts" "test/**/*.ts"
# CI/CD usage
npx tsbuild emitcheck "**/*.ts" --json
```
**Exit codes:**
- `0` - All files can be emitted
- `1` - One or more files have errors
### 5. Type Check
```bash
npx tsbuild check <pattern> [more...] [options]
```
Performs type checking without emitting files. Faster than emitcheck.
**Examples:**
```bash
# Check all TypeScript files
npx tsbuild check "ts/**/*.ts"
# Check multiple patterns
npx tsbuild check "src/**/*.ts" "test/**/*.ts"
# Check with specific options
npx tsbuild check "**/*.ts" --disallowimplicitany
```
## API Reference
### Core Functions
#### `compileFileArray(files, options?, argv?)`
Basic compilation of file array.
**Parameters:**
- `files: string[]` - File paths to compile
- `options?: CompilerOptions` - TypeScript compiler options
- `argv?: any` - CLI arguments object
**Returns:** `Promise<any[]>` - Compiled file results
**Example:**
```typescript
import { compileFileArray } from '@git.zone/tsbuild';
await compileFileArray(
['./src/index.ts', './src/utils.ts'],
{ target: 'ES2022' }
);
```
[![npm](https://push.rocks/assets/repo-header.svg)](https://push.rocks)
#### `compileFileArrayWithErrorTracking(files, options?, argv?, taskInfo?)`
**⭐ RECOMMENDED for production** - Provides detailed error tracking and metrics.
## Contribution
**Parameters:**
- `files: string[]` - File paths to compile
- `options?: CompilerOptions` - TypeScript compiler options
- `argv?: any` - CLI arguments object
- `taskInfo?: ITaskInfo` - Task metadata for progress reporting
We are always happy for code contributions. If you are not the code contributing type that is ok. Still, maintaining Open Source repositories takes considerable time and thought. If you like the quality of what we do and our modules are useful to you we would appreciate a little monthly contribution: You can [contribute one time](https://lossless.link/contribute-onetime) or [contribute monthly](https://lossless.link/contribute). :)
**Returns:** `Promise<IErrorSummary>` - Detailed error summary
For further information read the linked docs at the top of this readme.
**IErrorSummary Interface:**
```typescript
interface IErrorSummary {
hasErrors: boolean;
errorCount: number;
fileCount: number;
errorsByFile: Record<string, Diagnostic[]>;
generalErrors: Diagnostic[];
}
```
> MIT licensed | **&copy;** [Lossless GmbH](https://lossless.gmbh)
| By using this npm module you agree to our [privacy policy](https://lossless.gmbH/privacy)
**Example:**
```typescript
import { compileFileArrayWithErrorTracking } from '@git.zone/tsbuild';
[![repo-footer](https://lossless.gitlab.io/publicrelations/repofooter.svg)](https://maintainedby.lossless.com)
const result = await compileFileArrayWithErrorTracking(
['./src/**/*.ts'],
{ target: 'ES2022', strict: true }
);
if (result.hasErrors) {
console.error(`${result.errorCount} errors in ${result.fileCount} files`);
// Show errors by file
for (const [file, errors] of Object.entries(result.errorsByFile)) {
console.error(`\n${file}:`);
errors.forEach(err => console.error(` - ${err.messageText}`));
}
process.exit(1);
}
console.log('✅ Compilation successful!');
```
#### `compileGlobStringObject(globObject, options?, cwd?, argv?)`
**Most powerful API** - Compile multiple glob patterns to different destinations.
**Parameters:**
- `globObject: Record<string, string>` - Maps glob patterns to output directories
- `options?: CompilerOptions` - TypeScript compiler options
- `cwd?: string` - Working directory (defaults to `process.cwd()`)
- `argv?: any` - CLI arguments object
**Returns:** `Promise<any[]>` - Array of compilation results
**Example:**
```typescript
import { compileGlobStringObject } from '@git.zone/tsbuild';
await compileGlobStringObject(
{
'./ts/**/*.ts': './dist_ts',
'./ts_web/**/*.ts': './dist_web',
'./ts_node/**/*.ts': './dist_node'
},
{
target: 'ES2022',
module: 'NodeNext'
}
);
```
#### `checkTypes(files, options?, argv?)`
Type check files without emitting. Fast validation.
**Parameters:**
- `files: string[]` - Files to check
- `options?: CompilerOptions` - Compiler options
- `argv?: any` - CLI arguments
**Returns:** `Promise<IErrorSummary>` - Error summary
**Example:**
```typescript
import { checkTypes } from '@git.zone/tsbuild';
const result = await checkTypes(['./src/**/*.ts']);
if (result.hasErrors) {
console.error('Type errors found!');
process.exit(1);
}
```
#### `emitCheck(files, options?, argv?)`
Validate files can be emitted without actually emitting.
**Example:**
```typescript
import { emitCheck } from '@git.zone/tsbuild';
const result = await emitCheck(['./src/index.ts']);
if (result.hasErrors) {
console.error('Cannot emit these files!');
}
```
### TsBuild Class
Object-oriented API with full control.
**Constructor:**
```typescript
new TsBuild(
fileNames?: string[],
customOptions?: CompilerOptions,
argvArg?: any,
taskInfo?: ITaskInfo
)
```
**Methods:**
- `compile()` - Compile and emit files
- `compileWithErrorTracking()` - Compile with detailed error summary
- `checkTypes()` - Type check without emitting
- `emitCheck()` - Validate emit capability
- `mergeCompilerOptions()` - Merge options (public utility)
**Example:**
```typescript
import { TsBuild } from '@git.zone/tsbuild';
const builder = new TsBuild(
['./src/**/*.ts'],
{ target: 'ES2022', strict: true }
);
const result = await builder.compileWithErrorTracking();
if (result.hasErrors) {
console.error('Build failed!');
}
```
## Configuration
### tsconfig.json Support
tsbuild **fully supports** all compiler options from `tsconfig.json`. Your project configuration is respected and intelligently merged.
**Example tsconfig.json:**
```json
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"verbatimModuleSyntax": true
}
}
```
### Configuration Priority (Merge Order)
When multiple configuration sources exist, they merge in this order (later overrides earlier):
1. **Default Options** - tsbuild's sensible defaults
2. **tsconfig.json** - All options from your tsconfig.json (if present)
3. **Protected Defaults** - Critical options for build integrity
4. **Programmatic Options** - Options passed to API functions
5. **CLI Flags** - Command-line arguments (highest priority)
**Example:**
```typescript
// tsconfig.json has: { "target": "ES2020" }
// You call:
await compileFileArray(files, { target: 'ES2022' });
// Result: Uses ES2022 (programmatic overrides tsconfig.json)
```
### Protected Options
To ensure build integrity, these options are protected from tsconfig.json override (but can be overridden programmatically or via CLI):
- **`outDir: 'dist_ts/'`** - Required for automatic path transformations
- **`noEmitOnError: true`** - Prevents broken builds from being emitted
- **`declaration: true`** - Ensures `.d.ts` files for library consumers
- **`emitDecoratorMetadata: true`** - Required for DI frameworks
- **`inlineSourceMap: true`** - Consistent debugging experience
**Why?** These settings ensure tsbuild works correctly and produces consumable output.
**Override if needed:**
```typescript
// Override via programmatic API
await compileFileArray(files, {
outDir: './custom_dist',
noEmitOnError: false // ⚠️ Not recommended
});
```
### Default Compiler Options
When no tsconfig.json exists, tsbuild uses these modern defaults:
```typescript
{
declaration: true, // Generate .d.ts files
emitDecoratorMetadata: true, // Support DI frameworks
experimentalDecorators: true, // Enable decorators
inlineSourceMap: true, // Debug-friendly
noEmitOnError: true, // Fail-fast on errors
outDir: 'dist_ts/', // Output directory
module: ModuleKind.NodeNext, // Modern Node.js modules
target: ScriptTarget.ESNext, // Latest JavaScript
moduleResolution: ModuleResolutionKind.NodeNext,
lib: ['lib.dom.d.ts', 'lib.es2022.d.ts'],
noImplicitAny: false, // Flexible for quick development
esModuleInterop: true, // CJS/ESM interop
useDefineForClassFields: false, // Classic decorator behavior
verbatimModuleSyntax: true, // Explicit imports/exports
baseUrl: './'
}
```
### CLI Flags
Override any option via command-line:
- `--skiplibcheck` - Skip declaration file checking (shows 5-second warning)
- `--confirmskiplibcheck` - Skip without warning
- `--disallowimplicitany` - Enable strict `any` checking
- `--commonjs` - Use CommonJS modules
- `--json` - Output JSON (for CI/CD)
- `--quiet` - Suppress console output
**Examples:**
```bash
# Strict mode
npx tsbuild --disallowimplicitany
# CommonJS output
npx tsbuild --commonjs
# CI/CD pipeline
npx tsbuild --json --quiet > build-results.json
```
### Path Resolution
tsbuild automatically transforms path mappings from your tsconfig.json:
**tsconfig.json:**
```json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@models/*": ["./ts_models/*"],
"@utils/*": ["./ts_utils/*"]
}
}
}
```
**Automatic transformation:**
```
./ts_models/* → ./dist_ts_models/*
./ts_utils/* → ./dist_ts_utils/*
```
This ensures imports work correctly in compiled output.
## Advanced Usage
### Error Handling Patterns
**Pattern 1: Simple (throw on error)**
```typescript
import { compileFileArray } from '@git.zone/tsbuild';
try {
await compileFileArray(['./src/**/*.ts']);
console.log('✅ Build successful');
} catch (error) {
console.error('❌ Build failed:', error);
process.exit(1);
}
```
**Pattern 2: Detailed tracking (recommended)**
```typescript
import { compileFileArrayWithErrorTracking } from '@git.zone/tsbuild';
const result = await compileFileArrayWithErrorTracking(['./src/**/*.ts']);
if (result.hasErrors) {
console.error(`\n❌ Compilation failed with ${result.errorCount} errors in ${result.fileCount} files\n`);
// Group errors by file
for (const [file, errors] of Object.entries(result.errorsByFile)) {
console.error(`📄 ${file}:`);
errors.forEach(err => {
const line = err.file?.getLineAndCharacterOfPosition(err.start!);
console.error(` Line ${line?.line}: ${err.messageText}`);
});
}
process.exit(1);
}
console.log('✅ All files compiled successfully!');
```
**Pattern 3: CI/CD Integration**
```typescript
import { compileFileArrayWithErrorTracking } from '@git.zone/tsbuild';
const result = await compileFileArrayWithErrorTracking(['./src/**/*.ts']);
// Output machine-readable JSON
console.log(JSON.stringify({
success: !result.hasErrors,
errorCount: result.errorCount,
fileCount: result.fileCount,
timestamp: new Date().toISOString()
}, null, 2));
process.exit(result.hasErrors ? 1 : 0);
```
### Multi-Stage Builds
Compile different parts of your project with different configurations:
```typescript
import { compileGlobStringObject } from '@git.zone/tsbuild';
// Stage 1: Compile shared interfaces
await compileGlobStringObject(
{ './ts_interfaces/**/*.ts': './dist_interfaces' },
{ declaration: true, emitDecoratorMetadata: false }
);
// Stage 2: Compile Node.js code
await compileGlobStringObject(
{ './ts_node/**/*.ts': './dist_node' },
{ target: 'ES2022', module: 'NodeNext' }
);
// Stage 3: Compile browser code
await compileGlobStringObject(
{ './ts_web/**/*.ts': './dist_web' },
{ target: 'ES2020', module: 'ESNext', lib: ['lib.dom.d.ts', 'lib.es2020.d.ts'] }
);
```
### Watch Mode Integration
Integrate with file watchers for development:
```typescript
import { compileFileArray } from '@git.zone/tsbuild';
import chokidar from 'chokidar';
const watcher = chokidar.watch('./ts/**/*.ts');
watcher.on('change', async (path) => {
console.log(`📝 ${path} changed, recompiling...`);
try {
await compileFileArray([path]);
console.log('✅ Recompiled successfully');
} catch (error) {
console.error('❌ Compilation error:', error);
}
});
console.log('👀 Watching for changes...');
```
### Custom Task Progress
Track compilation progress in your UI:
```typescript
import { compileFileArrayWithErrorTracking } from '@git.zone/tsbuild';
const taskInfo = {
taskIndex: 1,
totalTasks: 5,
taskName: 'Compiling Core Modules'
};
const result = await compileFileArrayWithErrorTracking(
['./ts_core/**/*.ts'],
{ target: 'ES2022' },
undefined,
taskInfo
);
// Output: 🔨 [1/5] Compiling Core Modules...
// ✅ [1/5] Task completed in 2345ms
```
## TypeScript Decorator Support
tsbuild has **first-class decorator support** out of the box:
```typescript
// Works automatically with no configuration
@Injectable()
class UserService {
constructor(
@Inject('CONFIG') private config: Config,
private logger: Logger
) {}
}
```
The following options are enabled by default:
- `experimentalDecorators: true`
- `emitDecoratorMetadata: true`
This means frameworks like **NestJS**, **TypeORM**, **Inversify**, and **Angular** work without extra setup.
## CI/CD Integration
### GitHub Actions
```yaml
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- run: npm install
- run: npx tsbuild --json --quiet > build-results.json
- name: Check build results
run: |
if ! cat build-results.json | jq -e '.success' > /dev/null; then
echo "Build failed!"
cat build-results.json | jq '.errors'
exit 1
fi
```
### GitLab CI
```yaml
build:
stage: build
script:
- npm install
- npx tsbuild
artifacts:
paths:
- dist_ts/
```
### Package.json Scripts
```json
{
"scripts": {
"build": "tsbuild",
"build:watch": "tsbuild && chokidar 'ts/**/*.ts' -c 'tsbuild'",
"build:prod": "tsbuild --disallowimplicitany",
"typecheck": "tsbuild check 'ts/**/*.ts'",
"pretest": "tsbuild emitcheck 'test/**/*.ts'"
}
}
```
## Troubleshooting
### Common Issues
**"Cannot find module" errors in compiled output**
This happens when path mappings aren't configured correctly. Make sure your tsconfig.json includes:
```json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@myapp/*": ["./ts/*"]
}
}
}
```
tsbuild automatically transforms these to work in the output directory.
**Decorator errors**
If you see decorator-related errors, ensure:
1. `experimentalDecorators: true` in tsconfig.json
2. `emitDecoratorMetadata: true` for DI frameworks
3. You're using a compatible TypeScript version (4.0+)
**Build succeeds but types are wrong**
Run with strict checking:
```bash
npx tsbuild --disallowimplicitany
```
This catches implicit `any` types that can hide bugs.
**Slow compilation**
Use `--skiplibcheck` to skip declaration file checking:
```bash
npx tsbuild --confirmskiplibcheck
```
⚠️ Only use this if you trust your dependencies' type definitions.
### Debug Mode
For troubleshooting, combine flags:
```bash
# See exactly what's happening
npx tsbuild --json | jq '.'
# Check types without emitting
npx tsbuild check "**/*.ts"
# Verify emit capability
npx tsbuild emitcheck "**/*.ts"
```
## Best Practices
### ✅ DO
- **Use `compileFileArrayWithErrorTracking()`** for production builds
- **Enable strict mode** in tsconfig.json for better type safety
- **Organize code in `ts_*` folders** for automatic dependency ordering
- **Use protected defaults** - they exist for good reasons
- **Type check in CI/CD** with `npx tsbuild check`
- **Version lock** tsbuild in package.json
### ❌ DON'T
- **Don't override `noEmitOnError`** - broken builds cause runtime errors
- **Don't skip lib check** in production builds
- **Don't ignore type errors** - they indicate real problems
- **Don't mix module formats** - stick to NodeNext for Node.js projects
- **Don't use `--quiet` without `--json`** - you'll lose error information
## Performance Tips
1. **Use glob patterns** instead of explicit file lists
2. **Enable `--skiplibcheck`** for faster development (not production!)
3. **Compile in stages** for large multi-package projects
4. **Use tsfolders** for automatic dependency ordering
5. **Cache `dist_*` folders** in CI/CD pipelines
## Migration Guide
### From tsc
**Before (package.json):**
```json
{
"scripts": {
"build": "tsc"
}
}
```
**After:**
```json
{
"scripts": {
"build": "tsbuild"
}
}
```
Your tsconfig.json continues to work! tsbuild respects all your settings.
### From other build tools
tsbuild is a drop-in replacement focused on TypeScript compilation. If you need bundling, combine with your bundler:
```json
{
"scripts": {
"build": "tsbuild && esbuild dist_ts/index.js --bundle --outfile=bundle.js"
}
}
```
## FAQ
**Q: Does tsbuild bundle code?**
A: No, tsbuild compiles TypeScript to JavaScript. For bundling, use esbuild, webpack, or rollup after compilation.
**Q: Can I use tsbuild with monorepos?**
A: Yes! Use `tsbuild tsfolders` to automatically compile all packages in dependency order.
**Q: Does it work with decorators?**
A: Yes, decorator support is enabled by default (both `experimentalDecorators` and `emitDecoratorMetadata`).
**Q: Can I disable the protected defaults?**
A: Yes, via programmatic API or CLI flags. But consider why they're protected first!
**Q: Does tsbuild support incremental compilation?**
A: tsbuild respects TypeScript's incremental flag if you set it in tsconfig.json.
**Q: Is tsbuild compatible with TypeScript 5.x?**
A: Yes, tsbuild works with all modern TypeScript versions.
## License and Legal Information
This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository.
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
### Trademarks
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH.
### Company Information
Task Venture Capital GmbH
Registered at District court Bremen HRB 35230 HB, Germany
For any legal inquiries or if you require further information, please contact us via email at hello@task.vc.
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.

45
readme.plan.md Normal file
View File

@@ -0,0 +1,45 @@
# Implementation Plan for tsbuild `check` Command
## Overview
Add a new `check` command to tsbuild that allows checking TypeScript files against a glob pattern without emitting them, similar to running the TypeScript compiler with the `--noEmit` flag.
## Implementation Steps
1. **Reread CLAUDE.md** to ensure we follow project guidelines
2. **Extend TsBuild Class**
- The existing `TsBuild` class already has a `checkEmit()` method
- We can leverage this method for our implementation
3. **Implement Check Command in CLI**
- Add a new `check` command to `tsbuild.cli.ts`
- Command should accept glob patterns as arguments
- Process glob patterns to find matching TypeScript files
- Use the `TsBuild` class to check the files without emitting
4. **Update Exports**
- Ensure any new functionality is properly exported
5. **Testing**
- Test the command with various glob patterns
- Verify error reporting works correctly
## Differences from Existing `emitcheck` Command
The `emitcheck` command already exists and checks specific files without emitting. Our new `check` command will:
- Be designed specifically for checking files against glob patterns
- Use a simpler, more intuitive command name
- Potentially add additional benefits (like summary statistics of checked files)
## Example Usage
Once implemented, the command would work like this:
```bash
npx tsbuild check ts/**/*
npx tsbuild check "src/**/*.ts" "test/**/*.ts"
```
## Expected Output
The command should:
- Report any TypeScript errors in the matched files
- Provide a count of files checked and any errors found
- Exit with code 0 if successful, or 1 if errors are found

View File

@@ -1,5 +1 @@
declare class test2 {
test: string[];
constructor();
}
declare const run: () => Promise<string>;
export {};

View File

@@ -1,12 +1,17 @@
console.log('test');
console.log('test2');
import * as early from '@push.rocks/early';
early.start();
early.stop();
import { anExportedString } from './tocompile2.js';
console.log(anExportedString);
class test2 {
test = [];
constructor() {
this.test = [];
console.log('hi');
}
}
const run = async () => {
return 'hi';
};
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9jb21waWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdG9jb21waWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDcEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUNyQixNQUFNLEtBQUs7SUFFVDtRQURBLFNBQUksR0FBYSxFQUFFLENBQUM7UUFFbEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNwQixDQUFDO0NBQ0Y7QUFFRCxNQUFNLEdBQUcsR0FBRyxLQUFLLElBQXFCLEVBQUU7SUFDdEMsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDLENBQUMifQ==
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9jb21waWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdG9jb21waWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDcEIsT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUVyQixPQUFPLEtBQUssS0FBSyxNQUFNLG1CQUFtQixDQUFDO0FBRTNDLEtBQUssQ0FBQyxLQUFLLEVBQUUsQ0FBQztBQUNkLEtBQUssQ0FBQyxJQUFJLEVBQUUsQ0FBQztBQUViLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ25ELE9BQU8sQ0FBQyxHQUFHLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztBQUU5QixNQUFNLEtBQUs7SUFDVCxJQUFJLEdBQWEsRUFBRSxDQUFDO0lBQ3BCO1FBQ0UsT0FBTyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUNwQixDQUFDO0NBQ0Y7QUFFRCxNQUFNLEdBQUcsR0FBRyxLQUFLLElBQXFCLEVBQUU7SUFDdEMsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDLENBQUMifQ==

View File

@@ -0,0 +1 @@
export declare const anExportedString = "exported string";

View File

@@ -1,2 +1,3 @@
console.log('hello');
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9jb21waWxlMi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RvY29tcGlsZTIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQyJ9
export const anExportedString = 'exported string';
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9jb21waWxlMi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RvY29tcGlsZTIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUNyQixNQUFNLENBQUMsTUFBTSxnQkFBZ0IsR0FBRyxpQkFBaUIsQ0FBQyJ9

View File

@@ -1,5 +1,14 @@
console.log('test');
console.log('test2');
import * as early from '@push.rocks/early';
early.start();
early.stop();
import { anExportedString } from './tocompile2.js';
console.log(anExportedString);
class test2 {
test: string[] = [];
constructor() {

View File

@@ -1 +1,2 @@
console.log('hello');
export const anExportedString = 'exported string';

View File

@@ -1,19 +1,21 @@
import { tap, expect } from '@pushrocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as tsn from '../ts/index';
import { TsCompiler } from '../ts/index.js';
let assetfiles: string[] = ['./test/assets/tocompile.ts', './test/assets/tocompile2.ts'];
const assetfiles: string[] = ['./test/assets/tocompile.ts', './test/assets/tocompile2.ts'];
let assetfiles2 = {
'./test/assets/**/!(*.d.ts|*.js|output)': './test/assets/output'
const assetfiles2 = {
'./test/assets/**/!(*.d.ts|*.js|output)': './test/assets/output',
};
tap.test('should convert files from an array with single files to output', async tools => {
tsn.compileFileArray(assetfiles, { outDir: './test/assets/output' });
tap.test('should compile files from an array', async () => {
const compiler = new TsCompiler();
await compiler.compileFilesOrThrow(assetfiles, { outDir: './test/assets/output' });
});
tap.test('should convert files from an array with single files to output', async tools => {
tsn.compileGlobStringObject(assetfiles2);
tap.test('should compile files from glob pattern object', async () => {
const compiler = new TsCompiler();
await compiler.compileGlob(assetfiles2);
});
tap.start();

8
ts/00_commitinfo_data.ts Normal file
View File

@@ -0,0 +1,8 @@
/**
* autocreated commitinfo by @push.rocks/commitinfo
*/
export const commitinfo = {
name: '@git.zone/tsbuild',
version: '3.1.3',
description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.'
}

View File

@@ -1,3 +1,14 @@
export * from './tsbuild.exports';
import * as early from '@push.rocks/early';
early.start('tsbuild');
import './tsbuild.cli';
// Export from new modular structure
export * from './mod_fs/index.js';
export * from './mod_config/index.js';
export * from './mod_unpack/index.js';
export * from './mod_compiler/index.js';
export * from './mod_cli/index.js';
// Re-export TypeScript types for convenience
export type { CompilerOptions, ScriptTarget, ModuleKind } from 'typescript';
early.stop();

View File

@@ -0,0 +1,320 @@
import * as path from 'path';
import * as smartcli from '@push.rocks/smartcli';
import * as smartpath from '@push.rocks/smartpath';
import * as tspublish from '@git.zone/tspublish';
import { TsCompiler } from '../mod_compiler/index.js';
import { FsHelpers } from '../mod_fs/index.js';
/**
* TsBuildCli handles all CLI commands for tsbuild.
* Provides commands for compiling, type checking, and emit validation.
*/
export class TsBuildCli {
private cli: smartcli.Smartcli;
private cwd: string;
constructor(cwd: string = process.cwd()) {
this.cwd = cwd;
this.cli = new smartcli.Smartcli();
this.registerCommands();
}
/**
* Register all CLI commands
*/
private registerCommands(): void {
this.registerStandardCommand();
this.registerCustomCommand();
this.registerEmitCheckCommand();
this.registerTsFoldersCommand();
this.registerCheckCommand();
}
/**
* Standard command: compiles ts folder to dist_ts
*/
private registerStandardCommand(): void {
this.cli.standardCommand().subscribe(async (argvArg) => {
const compiler = new TsCompiler(this.cwd, argvArg);
await compiler.compileGlob({
'./ts/**/*.ts': './dist_ts',
});
const summary = (argvArg as any)?.__tsbuildFinalErrorSummary;
if (summary && summary.totalErrors > 0) {
process.exit(1);
}
});
}
/**
* Custom command: compiles specified directories to dist_ prefixed directories
*/
private registerCustomCommand(): void {
this.cli.addCommand('custom').subscribe(async (argvArg) => {
const listedDirectories = argvArg._;
listedDirectories.shift(); // removes the first element that is "custom"
const compilationCommandObject: Record<string, string> = {};
for (const directory of listedDirectories) {
compilationCommandObject[`./${directory}/**/*.ts`] = `./dist_${directory}`;
}
const compiler = new TsCompiler(this.cwd, argvArg);
await compiler.compileGlob(compilationCommandObject);
const summary = (argvArg as any)?.__tsbuildFinalErrorSummary;
if (summary && summary.totalErrors > 0) {
process.exit(1);
}
});
}
/**
* Emit check command: validates files can be emitted without producing output
*/
private registerEmitCheckCommand(): void {
this.cli.addCommand('emitcheck').subscribe(async (argvArg) => {
const patterns = argvArg._.slice(1);
if (patterns.length === 0) {
console.error('\n❌ Error: Please provide at least one TypeScript file path or glob pattern');
console.error(' Usage: tsbuild emitcheck <file_or_glob_pattern> [additional_patterns ...]\n');
console.error(' Example: tsbuild emitcheck "src/**/*.ts" "test/**/*.ts"\n');
process.exit(1);
}
const allFiles = await this.collectFilesFromPatterns(patterns);
if (allFiles.length === 0) {
console.error('\n❌ Error: No TypeScript files found to check');
console.error(' Please verify your file paths or glob patterns.\n');
process.exit(1);
}
console.log(`\n🔎 Found ${allFiles.length} TypeScript file${allFiles.length !== 1 ? 's' : ''} to check`);
const compiler = new TsCompiler(this.cwd, argvArg);
const success = await compiler.checkEmit(allFiles);
process.exit(success ? 0 : 1);
});
}
/**
* TsFolders command: compiles all ts_* directories in order
*/
private registerTsFoldersCommand(): void {
this.cli.addCommand('tsfolders').subscribe(async (argvArg) => {
// List folders matching /^ts/ regex
const allEntries = await FsHelpers.listDirectory(this.cwd);
const tsFolders = allEntries
.filter((e) => e.isDirectory && /^ts/.test(e.name))
.map((e) => e.name);
// Get tspublish.json based ranking
const tsPublishInstance = new tspublish.TsPublish();
const tsPublishModules = await tsPublishInstance.getModuleSubDirs(this.cwd);
// Create an array with folder names and their ranks
const foldersWithOrder: Array<{ folder: string; rank: number }> = [];
for (const folder of tsFolders) {
let rank = Infinity;
if (tsPublishModules[folder] && tsPublishModules[folder].order !== undefined) {
rank = tsPublishModules[folder].order;
}
foldersWithOrder.push({ folder, rank });
}
// Sort the folders based on rank
foldersWithOrder.sort((a, b) => a.rank - b.rank);
// Construct the sorted list of folders
const sortedTsFolders: string[] = [];
for (const item of foldersWithOrder) {
sortedTsFolders.push(item.folder);
}
// Ensure ts_interfaces is first and ts_shared is second if they exist
const ensurePosition = (folderName: string, position: number) => {
if (tsFolders.indexOf(folderName) > -1 && Object.keys(tsPublishModules).indexOf(folderName) === -1) {
const currentIndex = sortedTsFolders.indexOf(folderName);
if (currentIndex > -1) {
sortedTsFolders.splice(currentIndex, 1);
}
sortedTsFolders.splice(position, 0, folderName);
}
};
ensurePosition('ts_interfaces', 0);
ensurePosition('ts_shared', 1);
// Display compilation plan
const folderCount = sortedTsFolders.length;
console.log(`\n📂 TypeScript Folder Compilation Plan (${folderCount} folder${folderCount !== 1 ? 's' : ''})`);
console.log('┌' + '─'.repeat(60) + '┐');
console.log('│ 🔄 Compilation Order │');
console.log('├' + '─'.repeat(60) + '┤');
sortedTsFolders.forEach((folder, index) => {
const prefix = index === folderCount - 1 ? '└─' : '├─';
const position = `${index + 1}/${folderCount}`;
console.log(`${prefix} ${position.padStart(5)} ${folder.padEnd(46)}`);
});
console.log('└' + '─'.repeat(60) + '┘\n');
// Build compilation object
const compilationCommandObject: Record<string, string> = {};
for (const tsFolder of sortedTsFolders) {
compilationCommandObject[`./${tsFolder}/**/*.ts`] = `./dist_${tsFolder}`;
}
const compiler = new TsCompiler(this.cwd, argvArg);
await compiler.compileGlob(compilationCommandObject);
const summary = (argvArg as any)?.__tsbuildFinalErrorSummary;
if (summary && summary.totalErrors > 0) {
process.exit(1);
}
});
}
/**
* Check command: type checks files without emitting
*/
private registerCheckCommand(): void {
this.cli.addCommand('check').subscribe(async (argvArg) => {
const patterns = argvArg._.slice(1);
// If no patterns provided, default to checking ts/**/* and then test/**/*
if (patterns.length === 0) {
await this.runDefaultTypeChecks(argvArg);
return;
}
const allFiles = await this.collectFilesFromPatterns(patterns);
if (allFiles.length === 0) {
console.error('\n❌ Error: No TypeScript files found to check');
console.error(' Please verify your file paths or glob patterns.\n');
process.exit(1);
}
console.log(`\n🔎 Found ${allFiles.length} TypeScript file${allFiles.length !== 1 ? 's' : ''} to check`);
const compiler = new TsCompiler(this.cwd, argvArg);
const success = await compiler.checkTypes(allFiles);
process.exit(success ? 0 : 1);
});
}
/**
* Run default type checks for ts/ and test/ directories
*/
private async runDefaultTypeChecks(argvArg: any): Promise<void> {
console.log('\n🔬 Running default type checking sequence...\n');
// First check ts/**/* without skiplibcheck
console.log('📂 Checking ts/**/* files...');
const tsTsFiles = await FsHelpers.listFilesWithGlob(this.cwd, 'ts/**/*.ts');
if (tsTsFiles.length > 0) {
console.log(` Found ${tsTsFiles.length} TypeScript files in ts/`);
const tsAbsoluteFiles = smartpath.transform.toAbsolute(tsTsFiles, this.cwd) as string[];
const tsCompiler = new TsCompiler(this.cwd, argvArg);
const tsSuccess = await tsCompiler.checkTypes(tsAbsoluteFiles);
if (!tsSuccess) {
console.error('❌ Type checking failed for ts/**/*');
process.exit(1);
}
console.log('✅ Type checking passed for ts/**/*\n');
} else {
console.log(' No TypeScript files found in ts/\n');
}
// Then check test/**/* with skiplibcheck
console.log('📂 Checking test/**/* files with --skiplibcheck...');
const testTsFiles = await FsHelpers.listFilesWithGlob(this.cwd, 'test/**/*.ts');
if (testTsFiles.length > 0) {
console.log(` Found ${testTsFiles.length} TypeScript files in test/`);
const testAbsoluteFiles = smartpath.transform.toAbsolute(testTsFiles, this.cwd) as string[];
const testArgvArg = { ...argvArg, skiplibcheck: true };
const testCompiler = new TsCompiler(this.cwd, testArgvArg);
const testSuccess = await testCompiler.checkTypes(testAbsoluteFiles);
if (!testSuccess) {
console.error('❌ Type checking failed for test/**/*');
process.exit(1);
}
console.log('✅ Type checking passed for test/**/*\n');
} else {
console.log(' No TypeScript files found in test/\n');
}
console.log('✅ All default type checks passed!\n');
process.exit(0);
}
/**
* Collect files from patterns (glob or direct paths)
*/
private async collectFilesFromPatterns(patterns: string[]): Promise<string[]> {
let allFiles: string[] = [];
for (const pattern of patterns) {
if (pattern.includes('*') || pattern.includes('{') || pattern.includes('?')) {
// Handle as glob pattern
console.log(`Processing glob pattern: ${pattern}`);
try {
const stringMatchedFiles = await FsHelpers.listFilesWithGlob(this.cwd, pattern);
if (stringMatchedFiles.length === 0) {
console.warn(`⚠️ Warning: No files matched the pattern '${pattern}'`);
} else {
console.log(`📂 Found ${stringMatchedFiles.length} files matching pattern '${pattern}'`);
const absoluteMatchedFiles = smartpath.transform.toAbsolute(stringMatchedFiles, this.cwd) as string[];
allFiles = allFiles.concat(absoluteMatchedFiles);
}
} catch (err) {
console.error(`❌ Error processing glob pattern '${pattern}': ${err}`);
}
} else {
// Handle as direct file path
const filePath = path.isAbsolute(pattern) ? pattern : path.join(this.cwd, pattern);
const fileExists = await FsHelpers.fileExists(filePath);
if (fileExists) {
allFiles.push(filePath);
} else {
console.error(`❌ Error: File not found: ${filePath}`);
process.exit(1);
}
}
}
// Filter to only TypeScript files
return allFiles.filter((file) => file.endsWith('.ts') || file.endsWith('.tsx'));
}
/**
* Start parsing CLI arguments
*/
public run(): void {
this.cli.startParse();
}
}
/**
* Run the CLI
*/
export const runCli = async (): Promise<void> => {
const cli = new TsBuildCli();
cli.run();
};

1
ts/mod_cli/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './classes.tsbuildcli.js';

View File

@@ -0,0 +1,536 @@
import type { CompilerOptions, Diagnostic, Program } from 'typescript';
import typescript from 'typescript';
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartpromise from '@push.rocks/smartpromise';
import * as smartpath from '@push.rocks/smartpath';
import { TsConfig } from '../mod_config/index.js';
import { FsHelpers } from '../mod_fs/index.js';
import { performUnpack } from '../mod_unpack/index.js';
/**
* Interface for error summary data
*/
export interface IErrorSummary {
errorsByFile: Record<string, Diagnostic[]>;
generalErrors: Diagnostic[];
totalErrors: number;
totalFiles: number;
}
/**
* Interface for task information
*/
export interface ITaskInfo {
taskNumber: number;
totalTasks: number;
sourcePattern: string;
destDir: string;
fileCount: number;
}
/**
* Interface for compilation result
*/
export interface ICompileResult {
emittedFiles: string[];
errorSummary: IErrorSummary;
}
/**
* TsCompiler handles TypeScript compilation with error tracking,
* configuration management, and output unpacking.
*/
export class TsCompiler {
private config: TsConfig;
private cwd: string;
private argvArg?: any;
constructor(cwd: string = process.cwd(), argvArg?: any) {
this.cwd = cwd;
this.config = new TsConfig(cwd);
this.argvArg = argvArg;
}
/**
* Get the current working directory
*/
public getCwd(): string {
return this.cwd;
}
/**
* Get the TsConfig instance
*/
public getConfig(): TsConfig {
return this.config;
}
/**
* Create compiler options by merging defaults, tsconfig.json, and custom options
*/
public createOptions(customOptions: CompilerOptions = {}): CompilerOptions {
return this.config.merge(customOptions, this.argvArg);
}
/**
* Create a TypeScript program from file names and options
*/
private createProgram(fileNames: string[], options: CompilerOptions): Program {
return typescript.createProgram(fileNames, options);
}
/**
* Process TypeScript diagnostics and return error summary
*/
private processDiagnostics(diagnostics: readonly Diagnostic[]): IErrorSummary {
const errorsByFile: Record<string, Diagnostic[]> = {};
const generalErrors: Diagnostic[] = [];
diagnostics.forEach((diagnostic) => {
if (diagnostic.file) {
const fileName = diagnostic.file.fileName;
if (!errorsByFile[fileName]) {
errorsByFile[fileName] = [];
}
errorsByFile[fileName].push(diagnostic);
} else {
generalErrors.push(diagnostic);
}
});
return {
errorsByFile,
generalErrors,
totalErrors: diagnostics.length,
totalFiles: Object.keys(errorsByFile).length,
};
}
/**
* Display error summary to console
*/
private displayErrorSummary(errorSummary: IErrorSummary): void {
if (errorSummary.totalErrors === 0) {
return;
}
const { errorsByFile, generalErrors, totalErrors, totalFiles } = errorSummary;
// Print error summary header
console.log('\n' + '='.repeat(80));
console.log(
`❌ Found ${totalErrors} error${totalErrors !== 1 ? 's' : ''} in ${totalFiles} file${totalFiles !== 1 ? 's' : ''}:`
);
console.log('='.repeat(80));
// Color codes for error formatting
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
yellow: '\x1b[33m',
cyan: '\x1b[36m',
white: '\x1b[37m',
brightRed: '\x1b[91m',
};
// Print file-specific errors
Object.entries(errorsByFile).forEach(([fileName, fileErrors]) => {
// Show relative path if possible for cleaner output
const displayPath = fileName.replace(process.cwd(), '').replace(/^\//, '');
console.log(
`\n${colors.cyan}File: ${displayPath} ${colors.yellow}(${fileErrors.length} error${fileErrors.length !== 1 ? 's' : ''})${colors.reset}`
);
console.log('-'.repeat(80));
fileErrors.forEach((diagnostic) => {
if (diagnostic.file && diagnostic.start !== undefined) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
const message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
const errorCode = diagnostic.code ? `TS${diagnostic.code}` : 'Error';
console.log(
`${colors.white}Line ${line + 1}, Col ${character + 1}${colors.reset}: ${colors.brightRed}${errorCode}${colors.reset} - ${message}`
);
// Try to show the code snippet if possible
try {
const lineContent = diagnostic.file.text.split('\n')[line];
if (lineContent) {
console.log(` ${lineContent.trimEnd()}`);
const indicator = ' '.repeat(character) + `${colors.red}^${colors.reset}`;
console.log(` ${indicator}`);
}
} catch {
// Failed to get source text, skip showing the code snippet
}
}
});
});
// Print general errors
if (generalErrors.length > 0) {
console.log(`\n${colors.yellow}General Errors:${colors.reset}`);
console.log('-'.repeat(80));
generalErrors.forEach((diagnostic) => {
const message = typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
const errorCode = diagnostic.code ? `TS${diagnostic.code}` : 'Error';
console.log(`${colors.brightRed}${errorCode}${colors.reset}: ${message}`);
});
}
console.log('\n' + '='.repeat(80) + '\n');
}
/**
* Handle skipLibCheck warning display
*/
private async handleSkipLibCheckWarning(): Promise<void> {
if (this.argvArg?.confirmskiplibcheck) {
console.log('\n⚠ WARNING ⚠️');
console.log('You are skipping libcheck... Is that really wanted?');
console.log('Continuing in 5 seconds...\n');
await smartdelay.delayFor(5000);
} else if (!this.argvArg?.quiet && !this.argvArg?.json) {
console.log('⚠️ skipLibCheck enabled; use --confirmskiplibcheck to pause with warning.');
}
}
/**
* Compile files with error tracking (returns result instead of throwing)
*/
public async compileFiles(
fileNames: string[],
customOptions: CompilerOptions = {},
taskInfo?: ITaskInfo
): Promise<ICompileResult> {
const options = this.createOptions(customOptions);
if (options.skipLibCheck) {
await this.handleSkipLibCheckWarning();
}
// Enhanced logging with task info
const startTime = Date.now();
if (taskInfo) {
const { taskNumber, totalTasks, sourcePattern, fileCount } = taskInfo;
const relativeDestDir = taskInfo.destDir.replace(process.cwd(), '').replace(/^\//, '');
console.log(
`\n🔨 [${taskNumber}/${totalTasks}] Compiling ${fileCount} file${fileCount !== 1 ? 's' : ''} from ${sourcePattern}`
);
console.log(` 📁 Output: ${relativeDestDir}`);
} else {
console.log(`🔨 Compiling ${fileNames.length} files...`);
}
const done = smartpromise.defer<ICompileResult>();
const program = this.createProgram(fileNames, options);
// Check for pre-emit diagnostics first
const preEmitDiagnostics = typescript.getPreEmitDiagnostics(program);
const preEmitErrorSummary = this.processDiagnostics(preEmitDiagnostics);
// Only continue to emit phase if no pre-emit errors
if (preEmitErrorSummary.totalErrors > 0) {
this.displayErrorSummary(preEmitErrorSummary);
console.error('\n❌ TypeScript pre-emit checks failed. Please fix the issues listed above before proceeding.');
console.error(' Type errors must be resolved before the compiler can emit output files.\n');
done.resolve({ emittedFiles: [], errorSummary: preEmitErrorSummary });
return done.promise;
}
// If no pre-emit errors, proceed with emit
const emitResult = program.emit();
const emitErrorSummary = this.processDiagnostics(emitResult.diagnostics);
// Combine error summaries
const combinedErrorSummary: IErrorSummary = {
errorsByFile: { ...preEmitErrorSummary.errorsByFile, ...emitErrorSummary.errorsByFile },
generalErrors: [...preEmitErrorSummary.generalErrors, ...emitErrorSummary.generalErrors],
totalErrors: preEmitErrorSummary.totalErrors + emitErrorSummary.totalErrors,
totalFiles: Object.keys({ ...preEmitErrorSummary.errorsByFile, ...emitErrorSummary.errorsByFile }).length,
};
const exitCode = emitResult.emitSkipped ? 1 : 0;
if (exitCode === 0) {
const endTime = Date.now();
const duration = endTime - startTime;
if (taskInfo) {
const { taskNumber, totalTasks } = taskInfo;
console.log(`✅ [${taskNumber}/${totalTasks}] Task completed in ${duration}ms`);
} else {
console.log(`✅ TypeScript emit succeeded! (${duration}ms)`);
}
// Get count of emitted files by type
const jsFiles = emitResult.emittedFiles?.filter((f) => f.endsWith('.js')).length || 0;
const dtsFiles = emitResult.emittedFiles?.filter((f) => f.endsWith('.d.ts')).length || 0;
const mapFiles = emitResult.emittedFiles?.filter((f) => f.endsWith('.map')).length || 0;
if (emitResult.emittedFiles && emitResult.emittedFiles.length > 0) {
console.log(
` 📄 Generated ${emitResult.emittedFiles.length} files: ${jsFiles} .js, ${dtsFiles} .d.ts, ${mapFiles} source maps`
);
}
done.resolve({ emittedFiles: emitResult.emittedFiles || [], errorSummary: combinedErrorSummary });
} else {
this.displayErrorSummary(combinedErrorSummary);
console.error('\n❌ TypeScript emit failed. Please investigate the errors listed above!');
console.error(' No output files have been generated.\n');
done.resolve({ emittedFiles: [], errorSummary: combinedErrorSummary });
}
return done.promise;
}
/**
* Compile files (throws on error)
*/
public async compileFilesOrThrow(fileNames: string[], customOptions: CompilerOptions = {}): Promise<string[]> {
const result = await this.compileFiles(fileNames, customOptions);
if (result.errorSummary.totalErrors > 0) {
throw new Error('TypeScript compilation failed.');
}
return result.emittedFiles;
}
/**
* Compile glob patterns with automatic unpacking
*/
public async compileGlob(
globPatterns: Record<string, string>,
customOptions: CompilerOptions = {}
): Promise<ICompileResult> {
const emittedFiles: string[] = [];
const errorSummaries: IErrorSummary[] = [];
const totalTasks = Object.keys(globPatterns).length;
let currentTask = 0;
const isQuiet = this.argvArg?.quiet === true;
const isJson = this.argvArg?.json === true;
if (!isQuiet && !isJson) {
console.log(`\n👷 TypeScript Compilation Tasks (${totalTasks} task${totalTasks !== 1 ? 's' : ''}):`);
Object.entries(globPatterns).forEach(([source, dest]) => {
console.log(` 📂 ${source}${dest}`);
});
console.log('');
}
for (const pattern of Object.keys(globPatterns)) {
const destPath = globPatterns[pattern];
if (!pattern || !destPath) continue;
// Get files matching the glob pattern
const files = await FsHelpers.listFilesWithGlob(this.cwd, pattern);
// Transform to absolute paths
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;
// Update compiler options with the output directory
const options: CompilerOptions = {
...customOptions,
outDir: destDir,
};
currentTask++;
const taskInfo: ITaskInfo = {
taskNumber: currentTask,
totalTasks,
sourcePattern: pattern,
destDir: destPath,
fileCount: absoluteFiles.length,
};
const result = await this.compileFiles(absoluteFiles, options, taskInfo);
emittedFiles.push(...result.emittedFiles);
errorSummaries.push(result.errorSummary);
// Perform unpack if compilation succeeded
if (result.errorSummary.totalErrors === 0) {
await performUnpack(pattern, destDir, this.cwd);
}
}
// Merge all error summaries
const finalErrorSummary = this.mergeErrorSummaries(errorSummaries);
// Output summary based on mode
if (isJson) {
const result = {
success: finalErrorSummary.totalErrors === 0,
totals: {
errors: finalErrorSummary.totalErrors,
filesWithErrors: finalErrorSummary.totalFiles,
tasks: totalTasks,
},
errorsByFile: Object.fromEntries(
Object.entries(finalErrorSummary.errorsByFile).map(([file, diags]) => [
file,
diags.map((d) => ({
code: d.code,
message: typescript.flattenDiagnosticMessageText(d.messageText as any, '\n'),
})),
])
),
};
console.log(JSON.stringify(result));
} else if (!isQuiet) {
this.displayFinalSummary(finalErrorSummary);
}
// Attach summary to argvArg for CLI exit behavior
if (this.argvArg && typeof this.argvArg === 'object') {
(this.argvArg as any).__tsbuildFinalErrorSummary = finalErrorSummary;
}
return {
emittedFiles,
errorSummary: finalErrorSummary,
};
}
/**
* Check if files can be emitted without actually emitting
*/
public async checkEmit(fileNames: string[], customOptions: CompilerOptions = {}): Promise<boolean> {
const options = { ...this.createOptions(customOptions), noEmit: true };
const fileCount = fileNames.length;
console.log(`\n🔍 Checking if ${fileCount} file${fileCount !== 1 ? 's' : ''} can be emitted...`);
const program = this.createProgram(fileNames, options);
const preEmitDiagnostics = typescript.getPreEmitDiagnostics(program);
const preEmitErrorSummary = this.processDiagnostics(preEmitDiagnostics);
const emitResult = program.emit(undefined, undefined, undefined, true);
const emitErrorSummary = this.processDiagnostics(emitResult.diagnostics);
const combinedErrorSummary: IErrorSummary = {
errorsByFile: { ...preEmitErrorSummary.errorsByFile, ...emitErrorSummary.errorsByFile },
generalErrors: [...preEmitErrorSummary.generalErrors, ...emitErrorSummary.generalErrors],
totalErrors: preEmitErrorSummary.totalErrors + emitErrorSummary.totalErrors,
totalFiles: Object.keys({ ...preEmitErrorSummary.errorsByFile, ...emitErrorSummary.errorsByFile }).length,
};
const success = combinedErrorSummary.totalErrors === 0 && !emitResult.emitSkipped;
if (success) {
console.log('\n✅ TypeScript emit check passed! All files can be emitted successfully.');
console.log(` ${fileCount} file${fileCount !== 1 ? 's' : ''} ${fileCount !== 1 ? 'are' : 'is'} ready to be compiled.\n`);
} else {
this.displayErrorSummary(combinedErrorSummary);
console.error('\n❌ TypeScript emit check failed. Please fix the issues listed above.');
console.error(' The compilation cannot proceed until these errors are resolved.\n');
}
return success;
}
/**
* Check TypeScript files for type errors without emission
*/
public async checkTypes(fileNames: string[], customOptions: CompilerOptions = {}): Promise<boolean> {
const options = { ...this.createOptions(customOptions), noEmit: true };
const fileCount = fileNames.length;
console.log(`\n🔍 Type checking ${fileCount} TypeScript file${fileCount !== 1 ? 's' : ''}...`);
const program = this.createProgram(fileNames, options);
const diagnostics = typescript.getPreEmitDiagnostics(program);
const errorSummary = this.processDiagnostics(diagnostics);
const success = errorSummary.totalErrors === 0;
if (success) {
console.log('\n✅ TypeScript type check passed! No type errors found.');
console.log(` All ${fileCount} file${fileCount !== 1 ? 's' : ''} passed type checking successfully.\n`);
} else {
this.displayErrorSummary(errorSummary);
console.error('\n❌ TypeScript type check failed. Please fix the type errors listed above.');
console.error(' The type checker found issues that need to be resolved.\n');
}
return success;
}
/**
* Merge multiple error summaries into one
*/
private mergeErrorSummaries(summaries: IErrorSummary[]): IErrorSummary {
const mergedErrorsByFile: Record<string, Diagnostic[]> = {};
const mergedGeneralErrors: Diagnostic[] = [];
let totalErrors = 0;
summaries.forEach((summary) => {
Object.entries(summary.errorsByFile).forEach(([fileName, errors]) => {
if (!mergedErrorsByFile[fileName]) {
mergedErrorsByFile[fileName] = [];
}
mergedErrorsByFile[fileName] = mergedErrorsByFile[fileName].concat(errors);
});
mergedGeneralErrors.push(...summary.generalErrors);
totalErrors += summary.totalErrors;
});
return {
errorsByFile: mergedErrorsByFile,
generalErrors: mergedGeneralErrors,
totalErrors,
totalFiles: Object.keys(mergedErrorsByFile).length,
};
}
/**
* Display final compilation summary
*/
private displayFinalSummary(errorSummary: IErrorSummary): void {
if (errorSummary.totalErrors === 0) {
console.log('\n📊 \x1b[32mCompilation Summary: All tasks completed successfully! ✅\x1b[0m\n');
return;
}
const colors = {
reset: '\x1b[0m',
red: '\x1b[31m',
yellow: '\x1b[33m',
cyan: '\x1b[36m',
brightRed: '\x1b[91m',
brightYellow: '\x1b[93m',
};
console.log('\n' + '='.repeat(80));
console.log(`📊 ${colors.brightYellow}Final Compilation Summary${colors.reset}`);
console.log('='.repeat(80));
if (errorSummary.totalFiles > 0) {
console.log(`${colors.brightRed}❌ Files with errors (${errorSummary.totalFiles}):${colors.reset}`);
Object.entries(errorSummary.errorsByFile).forEach(([fileName, errors]) => {
const displayPath = fileName.replace(process.cwd(), '').replace(/^\//, '');
console.log(
` ${colors.red}${colors.reset} ${colors.cyan}${displayPath}${colors.reset} ${colors.yellow}(${errors.length} error${errors.length !== 1 ? 's' : ''})${colors.reset}`
);
});
}
if (errorSummary.generalErrors.length > 0) {
console.log(`${colors.brightRed}❌ General errors: ${errorSummary.generalErrors.length}${colors.reset}`);
}
console.log(
`\n${colors.brightRed}Total: ${errorSummary.totalErrors} error${errorSummary.totalErrors !== 1 ? 's' : ''} across ${errorSummary.totalFiles} file${errorSummary.totalFiles !== 1 ? 's' : ''}${colors.reset}`
);
console.log('='.repeat(80) + '\n');
}
}

1
ts/mod_compiler/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './classes.tscompiler.js';

View File

@@ -0,0 +1,180 @@
import * as fs from 'fs';
import * as path from 'path';
import type { CompilerOptions } from 'typescript';
import typescript from 'typescript';
/**
* Default compiler options for TypeScript compilation
*/
export const compilerOptionsDefault: CompilerOptions = {
declaration: true,
inlineSourceMap: true,
noEmitOnError: true,
outDir: 'dist_ts/',
module: typescript.ModuleKind.NodeNext,
target: typescript.ScriptTarget.ESNext,
moduleResolution: typescript.ModuleResolutionKind.NodeNext,
lib: ['lib.dom.d.ts', 'lib.esnext.d.ts'],
noImplicitAny: false,
esModuleInterop: true,
verbatimModuleSyntax: true,
baseUrl: './',
};
/**
* TsConfig handles loading and merging TypeScript compiler configurations.
* It supports reading tsconfig.json and merging with defaults and custom options.
*/
export class TsConfig {
private cwd: string;
private cachedTsConfig: CompilerOptions | null = null;
constructor(cwd: string = process.cwd()) {
this.cwd = cwd;
}
/**
* Get the current working directory
*/
public getCwd(): string {
return this.cwd;
}
/**
* Load and parse tsconfig.json from the current directory
*/
public load(): CompilerOptions {
if (this.cachedTsConfig !== null) {
return this.cachedTsConfig;
}
let tsconfig: any;
try {
const tsconfigPath = path.join(this.cwd, 'tsconfig.json');
const tsconfigContent = fs.readFileSync(tsconfigPath, 'utf8');
tsconfig = JSON.parse(tsconfigContent);
} catch {
this.cachedTsConfig = {};
return {};
}
if (!tsconfig || !tsconfig.compilerOptions) {
this.cachedTsConfig = {};
return {};
}
const returnObject: CompilerOptions = { ...tsconfig.compilerOptions };
// Convert target string to enum
if (tsconfig.compilerOptions.target && typeof tsconfig.compilerOptions.target === 'string') {
const targetKey = tsconfig.compilerOptions.target.toUpperCase();
if (targetKey in typescript.ScriptTarget) {
returnObject.target = typescript.ScriptTarget[targetKey as keyof typeof typescript.ScriptTarget];
}
}
// Convert module string to enum
if (tsconfig.compilerOptions.module && typeof tsconfig.compilerOptions.module === 'string') {
const moduleKey = tsconfig.compilerOptions.module.toUpperCase();
if (moduleKey in typescript.ModuleKind) {
returnObject.module = typescript.ModuleKind[moduleKey as keyof typeof typescript.ModuleKind];
} else if (moduleKey === 'NODENEXT') {
returnObject.module = typescript.ModuleKind.NodeNext;
}
}
// Convert moduleResolution string to enum
if (tsconfig.compilerOptions.moduleResolution && typeof tsconfig.compilerOptions.moduleResolution === 'string') {
const moduleResolutionKey = tsconfig.compilerOptions.moduleResolution.toUpperCase();
if (moduleResolutionKey in typescript.ModuleResolutionKind) {
returnObject.moduleResolution = typescript.ModuleResolutionKind[
moduleResolutionKey as keyof typeof typescript.ModuleResolutionKind
];
} else if (moduleResolutionKey === 'NODENEXT') {
returnObject.moduleResolution = typescript.ModuleResolutionKind.NodeNext;
}
}
// Apply path transformations (ts_ → dist_ts_)
if (tsconfig.compilerOptions.paths) {
returnObject.paths = { ...tsconfig.compilerOptions.paths };
for (const pathKey of Object.keys(returnObject.paths)) {
if (Array.isArray(returnObject.paths[pathKey]) && returnObject.paths[pathKey].length > 0) {
returnObject.paths[pathKey][0] = returnObject.paths[pathKey][0].replace('./ts_', './dist_ts_');
}
}
}
this.cachedTsConfig = returnObject;
return returnObject;
}
/**
* Get default compiler options
*/
public getDefaultOptions(): CompilerOptions {
return { ...compilerOptionsDefault };
}
/**
* Get critical/protected default options that shouldn't be overridden by tsconfig.json
*/
public getProtectedDefaults(): CompilerOptions {
return {
outDir: 'dist_ts/',
noEmitOnError: true,
declaration: true,
inlineSourceMap: true,
};
}
/**
* Process command line arguments and return applicable compiler options
*/
public getCommandLineOptions(argvArg?: any): CompilerOptions {
if (!argvArg) return {};
const options: CompilerOptions = {};
if (argvArg.skiplibcheck) {
options.skipLibCheck = true;
}
if (argvArg.disallowimplicitany) {
options.noImplicitAny = true;
}
if (argvArg.commonjs) {
options.module = typescript.ModuleKind.CommonJS;
options.moduleResolution = typescript.ModuleResolutionKind.NodeJs;
}
return options;
}
/**
* Merge compiler options with proper priority order:
* 1. Default options
* 2. tsconfig.json options
* 3. Protected defaults (cannot be overridden by tsconfig)
* 4. Custom options (programmatic)
* 5. CLI options (highest priority)
*/
public merge(customOptions: CompilerOptions = {}, argvArg?: any): CompilerOptions {
return {
...this.getDefaultOptions(),
...this.load(),
...this.getProtectedDefaults(),
...customOptions,
...this.getCommandLineOptions(argvArg),
};
}
/**
* Clear the cached tsconfig (useful for reloading)
*/
public clearCache(): void {
this.cachedTsConfig = null;
}
}

View File

@@ -0,0 +1,116 @@
import * as fs from 'fs';
import * as path from 'path';
/**
* Interface for tspublish.json configuration
*/
export interface ITsPublishJson {
order?: number;
unpack?: boolean;
[key: string]: any;
}
/**
* TsPublishConfig handles loading and parsing tspublish.json files.
* These configuration files control module-specific settings like
* compilation order and output unpacking behavior.
*/
export class TsPublishConfig {
private folderPath: string;
private cachedConfig: ITsPublishJson | null | undefined = undefined;
constructor(folderPath: string) {
this.folderPath = folderPath;
}
/**
* Get the folder path this config is for
*/
public getFolderPath(): string {
return this.folderPath;
}
/**
* Load and parse tspublish.json from the folder
* Returns null if file doesn't exist or is invalid
*/
public async load(): Promise<ITsPublishJson | null> {
if (this.cachedConfig !== undefined) {
return this.cachedConfig;
}
try {
const configPath = path.join(this.folderPath, 'tspublish.json');
const content = await fs.promises.readFile(configPath, 'utf8');
this.cachedConfig = JSON.parse(content);
return this.cachedConfig;
} catch {
this.cachedConfig = null;
return null;
}
}
/**
* Synchronously load and parse tspublish.json from the folder
* Returns null if file doesn't exist or is invalid
*/
public loadSync(): ITsPublishJson | null {
if (this.cachedConfig !== undefined) {
return this.cachedConfig;
}
try {
const configPath = path.join(this.folderPath, 'tspublish.json');
const content = fs.readFileSync(configPath, 'utf8');
this.cachedConfig = JSON.parse(content);
return this.cachedConfig;
} catch {
this.cachedConfig = null;
return null;
}
}
/**
* Check if output should be unpacked (flattened)
* Default is true if not specified
*/
public get shouldUnpack(): boolean {
const config = this.loadSync();
if (!config || config.unpack === undefined) {
return true; // Default to true
}
return config.unpack === true;
}
/**
* Get the compilation order for tsfolders command
* Returns Infinity if not specified (sorted last)
*/
public get order(): number {
const config = this.loadSync();
if (!config || config.order === undefined) {
return Infinity;
}
return config.order;
}
/**
* Check if tspublish.json exists in the folder
*/
public async exists(): Promise<boolean> {
try {
const configPath = path.join(this.folderPath, 'tspublish.json');
await fs.promises.access(configPath, fs.constants.F_OK);
return true;
} catch {
return false;
}
}
/**
* Clear the cached config (useful for reloading)
*/
public clearCache(): void {
this.cachedConfig = undefined;
}
}

2
ts/mod_config/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './classes.tsconfig.js';
export * from './classes.tspublishconfig.js';

View File

@@ -0,0 +1,144 @@
import * as fs from 'fs';
import * as path from 'path';
import * as smartfsModule from '@push.rocks/smartfs';
// Create a smartfs instance with Node.js provider
const smartfs = new smartfsModule.SmartFs(new smartfsModule.SmartFsProviderNode());
/**
* FsHelpers provides filesystem utility methods for tsbuild.
* All methods are static for convenience.
*/
export class FsHelpers {
/**
* The smartfs instance for filesystem operations
*/
public static readonly smartfs = smartfs;
/**
* List files matching a glob pattern like './ts/**\/*.ts'
* Parses the pattern to extract base directory and filter pattern
*/
public static async listFilesWithGlob(basePath: string, globPattern: string): Promise<string[]> {
// Remove leading ./ if present
const pattern = globPattern.replace(/^\.\//, '');
// Find the first directory part before any glob characters
const globChars = ['*', '?', '{', '['];
let baseDir = basePath;
// Find where the glob pattern starts
const parts = pattern.split('/');
const staticParts: string[] = [];
const filterParts: string[] = [];
let foundGlob = false;
for (const part of parts) {
if (!foundGlob && !globChars.some(c => part.includes(c))) {
staticParts.push(part);
} else {
foundGlob = true;
filterParts.push(part);
}
}
// Build the base directory
if (staticParts.length > 0) {
baseDir = path.join(basePath, ...staticParts);
}
// Build the filter pattern (just the filename part, ignoring ** for directories)
// The recursive() handles the ** part
const fileFilter = filterParts[filterParts.length - 1] || '*';
// Check if we need recursive search
const needsRecursive = filterParts.some(p => p === '**' || p.includes('**'));
let dirBuilder = smartfs.directory(baseDir);
if (needsRecursive) {
dirBuilder = dirBuilder.recursive();
}
try {
const entries = await dirBuilder.filter(fileFilter).list();
return entries.filter(e => e.isFile).map(e => e.path);
} catch {
// Directory doesn't exist or other error
return [];
}
}
/**
* Extract source folder name from a glob pattern
* './ts_core/**\/*.ts' → 'ts_core'
* 'ts_foo/**\/*.ts' → 'ts_foo'
*/
public static extractSourceFolder(pattern: string): string | null {
const match = pattern.match(/^\.?\/?([^\/\*]+)/);
return match ? match[1] : null;
}
/**
* Get the current working directory
*/
public static getCwd(): string {
return process.cwd();
}
/**
* Get the package directory (where package.json is located)
*/
public static getPackageDir(): string {
return path.resolve(__dirname, '../../');
}
/**
* Check if a file exists
*/
public static async fileExists(filePath: string): Promise<boolean> {
return smartfs.file(filePath).exists();
}
/**
* Check if a directory exists
*/
public static async directoryExists(dirPath: string): Promise<boolean> {
return smartfs.directory(dirPath).exists();
}
/**
* Read a JSON file and parse it
*/
public static readJsonSync<T = any>(filePath: string): T {
const content = fs.readFileSync(filePath, 'utf8');
return JSON.parse(content);
}
/**
* List directory contents
*/
public static async listDirectory(dirPath: string) {
return smartfs.directory(dirPath).list();
}
/**
* Remove a directory recursively
*/
public static async removeDirectory(dirPath: string): Promise<void> {
await fs.promises.rm(dirPath, { recursive: true });
}
/**
* Move/rename a file or directory
*/
public static async move(src: string, dest: string): Promise<void> {
await fs.promises.rename(src, dest);
}
/**
* Remove an empty directory
*/
public static async removeEmptyDirectory(dirPath: string): Promise<void> {
await fs.promises.rmdir(dirPath);
}
}

1
ts/mod_fs/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './classes.fshelpers.js';

View File

@@ -0,0 +1,153 @@
import * as fs from 'fs';
import * as path from 'path';
import { TsPublishConfig } from '../mod_config/index.js';
import { FsHelpers } from '../mod_fs/index.js';
/**
* TsUnpacker handles flattening of nested TypeScript output directories.
*
* When TypeScript compiles files that import from sibling directories,
* it creates a nested structure like:
* dist_ts_core/ts_core/index.js
* dist_ts_core/ts_shared/helper.js
*
* This class flattens it to:
* dist_ts_core/index.js
*/
export class TsUnpacker {
private sourceFolderName: string;
private destDir: string;
private cwd: string;
private config: TsPublishConfig;
constructor(sourceFolderName: string, destDir: string, cwd: string = process.cwd()) {
this.sourceFolderName = sourceFolderName;
this.destDir = destDir;
this.cwd = cwd;
this.config = new TsPublishConfig(path.join(cwd, sourceFolderName));
}
/**
* Create an unpacker from a glob pattern
* './ts_core/**\/*.ts' → sourceFolderName = 'ts_core'
*/
public static fromGlobPattern(
sourcePattern: string,
destDir: string,
cwd: string = process.cwd()
): TsUnpacker | null {
const sourceFolderName = FsHelpers.extractSourceFolder(sourcePattern);
if (!sourceFolderName) {
return null;
}
return new TsUnpacker(sourceFolderName, destDir, cwd);
}
/**
* Get the source folder name
*/
public getSourceFolderName(): string {
return this.sourceFolderName;
}
/**
* Get the destination directory
*/
public getDestDir(): string {
return this.destDir;
}
/**
* Check if unpacking should be performed based on tspublish.json config
* Default is true if not specified
*/
public async shouldUnpack(): Promise<boolean> {
return this.config.shouldUnpack;
}
/**
* Check if nested structure exists in the destination directory
*/
public async detectNesting(): Promise<boolean> {
const nestedPath = path.join(this.destDir, this.sourceFolderName);
return FsHelpers.directoryExists(nestedPath);
}
/**
* Get the path to the nested directory
*/
public getNestedPath(): string {
return path.join(this.destDir, this.sourceFolderName);
}
/**
* Perform the unpack operation - flatten nested output directories
* Returns true if unpacking was performed, false if skipped
*/
public async unpack(): Promise<boolean> {
// Check if we should unpack based on config
if (!(await this.shouldUnpack())) {
return false;
}
// Check if nested structure exists
if (!(await this.detectNesting())) {
return false;
}
const nestedPath = this.getNestedPath();
// Delete sibling folders (not the source folder)
await this.removeSiblingDirectories();
// Move contents from nested folder up
await this.moveNestedContentsUp();
// Remove empty nested folder
await FsHelpers.removeEmptyDirectory(nestedPath);
return true;
}
/**
* Remove sibling directories in the destination folder
* (directories other than the source folder being unpacked)
*/
private async removeSiblingDirectories(): Promise<void> {
const entries = await FsHelpers.listDirectory(this.destDir);
for (const entry of entries) {
if (entry.isDirectory && entry.name !== this.sourceFolderName) {
await FsHelpers.removeDirectory(path.join(this.destDir, entry.name));
}
}
}
/**
* Move contents from the nested folder up to the destination directory
*/
private async moveNestedContentsUp(): Promise<void> {
const nestedPath = this.getNestedPath();
const entries = await FsHelpers.listDirectory(nestedPath);
for (const entry of entries) {
const src = path.join(nestedPath, entry.name);
const dest = path.join(this.destDir, entry.name);
await FsHelpers.move(src, dest);
}
}
}
/**
* Convenience function to perform unpack operation
* Can be used directly without instantiating the class
*/
export async function performUnpack(
sourcePattern: string,
destDir: string,
cwd: string = process.cwd()
): Promise<boolean> {
const unpacker = TsUnpacker.fromGlobPattern(sourcePattern, destDir, cwd);
if (!unpacker) {
return false;
}
return unpacker.unpack();
}

1
ts/mod_unpack/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './classes.tsunpacker.js';

24
ts/plugins.ts Normal file
View File

@@ -0,0 +1,24 @@
// node native
import * as fs from 'fs';
import * as path from 'path';
export { fs, path };
// @git.zone scope
import * as tspublish from '@git.zone/tspublish';
export { tspublish };
// @push.rocks scope
import * as smartcli from '@push.rocks/smartcli';
import * as smartdelay from '@push.rocks/smartdelay';
import * as smartfile from '@push.rocks/smartfile';
import * as smartpath from '@push.rocks/smartpath';
import * as smartpromise from '@push.rocks/smartpromise';
export { smartcli, smartdelay, smartfile, smartpath, smartpromise };
// third party scope
import typescript from 'typescript';
export { typescript };

View File

@@ -1,95 +0,0 @@
// import all the stuff we need
import * as plugins from './tsbuild.plugins';
import { CompilerOptions } from 'typescript';
export { CompilerOptions, ScriptTarget, ModuleKind } from 'typescript';
/**
* the default typescript compilerOptions
*/
export const compilerOptionsDefault: CompilerOptions = {
declaration: true,
emitDecoratorMetadata: true,
experimentalDecorators: true,
inlineSourceMap: true,
noEmitOnError: true,
outDir: 'dist_ts/',
module: plugins.typescript.ModuleKind.CommonJS,
lib: ['lib.es2017.d.ts'],
noImplicitAny: false,
esModuleInterop: true,
target: plugins.typescript.ScriptTarget.ES2017
};
export const compilerOptionsWebDefault: CompilerOptions = {
...compilerOptionsDefault,
lib: [...compilerOptionsDefault.lib, 'lib.dom.d.ts']
};
/**
* merges compilerOptions with the default compiler options
*/
export const mergeCompilerOptions = (
customTsOptions: CompilerOptions,
argvArg?: any
): CompilerOptions => {
const defaultOptionsToMerge = (() => {
if (argvArg && argvArg.web) {
return compilerOptionsWebDefault;
} else {
return compilerOptionsDefault;
}
})();
// create merged options
const mergedOptions: CompilerOptions = {
...defaultOptionsToMerge,
...customTsOptions
};
return mergedOptions;
};
/**
* the internal main compiler function that compiles the files
*/
export const compiler = (
fileNames: string[],
options: plugins.typescript.CompilerOptions,
argvArg?: any
): Promise<any[]> => {
console.log(`Compiling ${fileNames.length} files...`);
const done = plugins.smartpromise.defer<any[]>();
const program = plugins.typescript.createProgram(fileNames, options);
const emitResult = program.emit();
// implement check only
/*let emitResult = program.emit(undefined,(args) => {
console.log(args)
});*/
const allDiagnostics = plugins.typescript
.getPreEmitDiagnostics(program)
.concat(emitResult.diagnostics);
allDiagnostics.forEach(diagnostic => {
if (diagnostic.file) {
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
const message = plugins.typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
} else {
console.log(
`${plugins.typescript.flattenDiagnosticMessageText(diagnostic.messageText, '\n')}`
);
}
});
const exitCode = emitResult.emitSkipped ? 1 : 0;
if (exitCode === 0) {
console.log('TypeScript emit succeeded!');
done.resolve(emitResult.emittedFiles);
} else {
console.error('TypeScript emit failed. Please investigate!');
process.exit(exitCode);
}
return done.promise;
};

View File

@@ -1,46 +0,0 @@
import * as plugins from './tsbuild.plugins';
import * as tsbuild from './tsbuild.exports';
const tsbuildCli = new plugins.smartcli.Smartcli();
/**
* the standard task compiles anything in ts/ directory to dist directory
*/
tsbuildCli.standardTask().subscribe(async argvArg => {
if (process.env.CLI_CALL_TSBUILD === 'true') {
tsbuild.compileGlobStringObject(
{
'./ts/**/*.ts': './dist_ts'
},
{},
process.cwd(),
argvArg
);
}
});
/**
* the custom command compiles any customDir to dist_customDir
*/
tsbuildCli.addCommand('custom').subscribe(async argvArg => {
const listedDirectories = argvArg._;
listedDirectories.shift();
const compilationCommandObject: { [key: string]: string } = {};
for (const directory of listedDirectories) {
compilationCommandObject[`./${directory}/**/*.ts`] = `./dist_${directory}`;
}
await tsbuild.compileGlobStringObject(compilationCommandObject, {}, process.cwd(), argvArg);
});
tsbuildCli.addCommand('element').subscribe(async argvArg => {
await tsbuild.compileGlobStringObject(
{
'./ts_web/**/*.ts': 'dist_ts_web'
},
{},
process.cwd(),
{ web: true }
);
});
tsbuildCli.startParse();

View File

@@ -1,56 +0,0 @@
import * as plugins from './tsbuild.plugins';
import { compiler, CompilerOptions, mergeCompilerOptions } from './tsbuild.classes.compiler';
export * from './tsbuild.classes.compiler';
/**
* compile am array of absolute file paths
*/
export let compileFileArray = (
fileStringArrayArg: string[],
compilerOptionsArg: CompilerOptions = {},
argvArg?: any
): Promise<any[]> => {
return compiler(fileStringArrayArg, mergeCompilerOptions(compilerOptionsArg, argvArg), argvArg);
};
/**
* compile advanced glob configurations
* @param globStringArrayArg a array of glob strings
* {
* './some/origin/folder/**\/*.ts': './some/destination/folder'
* }
*/
export let compileGlobStringObject = async (
globStringObjectArg: any,
tsOptionsArg: CompilerOptions = {},
cwdArg: string = process.cwd(),
argvArg?: any
) => {
let compiledFiles = [];
for (const keyArg in globStringObjectArg) {
if (globStringObjectArg[keyArg]) {
console.log(
`TypeScript assignment: transpile from ${keyArg} to ${globStringObjectArg[keyArg]}`
);
const fileTreeArray = await plugins.smartfile.fs.listFileTree(cwdArg, keyArg);
let absoluteFilePathArray: string[] = plugins.smartpath.transform.toAbsolute(
fileTreeArray,
cwdArg
);
let destDir: string = plugins.smartpath.transform.toAbsolute(
globStringObjectArg[keyArg],
cwdArg
);
tsOptionsArg = {
...tsOptionsArg,
outDir: destDir
};
compiledFiles = compiledFiles.concat(
compiledFiles,
await compileFileArray(absoluteFilePathArray, tsOptionsArg, argvArg)
);
}
}
return compiledFiles;
};

View File

@@ -1,7 +0,0 @@
import * as smartcli from '@pushrocks/smartcli';
import * as smartfile from '@pushrocks/smartfile';
import * as smartpath from '@pushrocks/smartpath';
import * as smartpromise from '@pushrocks/smartpromise';
import * as typescript from 'typescript';
export { smartcli, smartfile, smartpath, smartpromise, typescript };

View File

@@ -1,14 +1,9 @@
{
"compilerOptions": {
"outDir": "dist_ts/",
"module": "commonjs",
"target": "es2015",
"lib":[
"es2016",
"es2017"
]
},
"include": [
"ts/**/*.ts"
]
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"verbatimModuleSyntax": true
}
}

View File

@@ -1,17 +0,0 @@
{
"extends": ["tslint:latest", "tslint-config-prettier"],
"rules": {
"semicolon": [true, "always"],
"no-console": false,
"ordered-imports": false,
"object-literal-sort-keys": false,
"member-ordering": {
"options":{
"order": [
"static-method"
]
}
}
},
"defaultSeverity": "warning"
}