Files
tsbuild/readme.md

895 lines
22 KiB
Markdown

# @git.zone/tsbuild
> 🚀 **A powerful, modern TypeScript build tool with smart defaults and full tsconfig.json support**
A production-ready TypeScript compiler that combines flexibility with safety. Built for modern JavaScript development with ESNext, NodeNext modules, and automatic decorator support.
## Why tsbuild?
-**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
## 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 { compileFileArray } from '@git.zone/tsbuild';
await compileFileArray([
'./src/index.ts',
'./src/utils.ts'
]);
```
**Production-ready with error tracking (recommended):**
```typescript
import { compileFileArrayWithErrorTracking } from '@git.zone/tsbuild';
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);
}
console.log('✅ Compilation successful!');
```
**Advanced glob compilation:**
```typescript
import { compileGlobStringObject } from '@git.zone/tsbuild';
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' }
);
```
#### `compileFileArrayWithErrorTracking(files, options?, argv?, taskInfo?)`
**⭐ RECOMMENDED for production** - Provides detailed error tracking and metrics.
**Parameters:**
- `files: string[]` - File paths to compile
- `options?: CompilerOptions` - TypeScript compiler options
- `argv?: any` - CLI arguments object
- `taskInfo?: ITaskInfo` - Task metadata for progress reporting
**Returns:** `Promise<IErrorSummary>` - Detailed error summary
**IErrorSummary Interface:**
```typescript
interface IErrorSummary {
hasErrors: boolean;
errorCount: number;
fileCount: number;
errorsByFile: Record<string, Diagnostic[]>;
generalErrors: Diagnostic[];
}
```
**Example:**
```typescript
import { compileFileArrayWithErrorTracking } from '@git.zone/tsbuild';
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.