feat(tsbuild): Add tsconfig.json support and safer compiler option merging; protect critical options; apply path and enum transforms; bump dependencies.
This commit is contained in:
12
changelog.md
12
changelog.md
@@ -1,5 +1,17 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 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)
|
## 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
|
Avoid process.exit in library, add confirmskiplibcheck flag, improve CLI exit handling and JSON/quiet modes, update test script
|
||||||
|
|
||||||
|
|||||||
10
package.json
10
package.json
@@ -38,17 +38,17 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@git.zone/tspublish": "^1.10.3",
|
"@git.zone/tspublish": "^1.10.3",
|
||||||
"@push.rocks/early": "^4.0.4",
|
"@push.rocks/early": "^4.0.4",
|
||||||
"@push.rocks/smartcli": "^4.0.11",
|
"@push.rocks/smartcli": "^4.0.19",
|
||||||
"@push.rocks/smartdelay": "^3.0.5",
|
"@push.rocks/smartdelay": "^3.0.5",
|
||||||
"@push.rocks/smartfile": "^11.2.7",
|
"@push.rocks/smartfile": "^11.2.7",
|
||||||
"@push.rocks/smartlog": "^3.1.8",
|
"@push.rocks/smartlog": "^3.1.10",
|
||||||
"@push.rocks/smartpath": "^6.0.0",
|
"@push.rocks/smartpath": "^6.0.0",
|
||||||
"@push.rocks/smartpromise": "^4.2.3",
|
"@push.rocks/smartpromise": "^4.2.3",
|
||||||
"typescript": "5.9.2"
|
"typescript": "5.9.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsrun": "^1.2.47",
|
"@git.zone/tsrun": "^1.6.2",
|
||||||
"@git.zone/tstest": "^2.3.4",
|
"@git.zone/tstest": "^2.7.0",
|
||||||
"@types/node": "^22.15.21"
|
"@types/node": "^22.15.21"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
|||||||
3767
pnpm-lock.yaml
generated
3767
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
47
readme.md
47
readme.md
@@ -184,6 +184,49 @@ Example:
|
|||||||
npx tsbuild --skiplibcheck --disallowimplicitany
|
npx tsbuild --skiplibcheck --disallowimplicitany
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Configuration with tsconfig.json
|
||||||
|
|
||||||
|
`@git.zone/tsbuild` fully supports `tsconfig.json` for compiler configuration. All compiler options from your `tsconfig.json` will be respected and merged with tsbuild's defaults.
|
||||||
|
|
||||||
|
### Option Priority (Merge Order)
|
||||||
|
|
||||||
|
When multiple configuration sources are present, they are merged in the following order (later sources override earlier ones):
|
||||||
|
|
||||||
|
1. **Default compiler options** - Base defaults for all options
|
||||||
|
2. **tsconfig.json** - All compiler options from your project's `tsconfig.json` (if present)
|
||||||
|
3. **Protected defaults** - Critical options for build integrity (see below)
|
||||||
|
4. **Programmatic options** - Options passed to API functions
|
||||||
|
5. **CLI flags** - Command-line arguments (highest priority)
|
||||||
|
|
||||||
|
### Protected Compiler Options
|
||||||
|
|
||||||
|
To ensure build integrity and correct functionality, the following options are protected and cannot be overridden by `tsconfig.json` alone (but can be overridden programmatically or via CLI):
|
||||||
|
|
||||||
|
- `outDir: 'dist_ts/'` - Required for path transformation logic
|
||||||
|
- `noEmitOnError: true` - Prevents broken builds from being emitted
|
||||||
|
- `declaration: true` - Ensures .d.ts files for library consumers
|
||||||
|
- `emitDecoratorMetadata: true` - Required for dependency injection frameworks
|
||||||
|
- `inlineSourceMap: true` - Provides consistent debugging experience
|
||||||
|
|
||||||
|
### Working Without tsconfig.json
|
||||||
|
|
||||||
|
`@git.zone/tsbuild` works perfectly fine without a `tsconfig.json` file. If no `tsconfig.json` is found, it will gracefully fall back to sensible defaults without errors.
|
||||||
|
|
||||||
|
Example `tsconfig.json`:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"verbatimModuleSyntax": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Default Compiler Options
|
## Default Compiler Options
|
||||||
|
|
||||||
By default, `@git.zone/tsbuild` uses the following compiler options:
|
By default, `@git.zone/tsbuild` uses the following compiler options:
|
||||||
@@ -200,7 +243,7 @@ By default, `@git.zone/tsbuild` uses the following compiler options:
|
|||||||
target: ScriptTarget.ESNext,
|
target: ScriptTarget.ESNext,
|
||||||
moduleResolution: ModuleResolutionKind.NodeNext,
|
moduleResolution: ModuleResolutionKind.NodeNext,
|
||||||
lib: ['lib.dom.d.ts', 'lib.es2022.d.ts'],
|
lib: ['lib.dom.d.ts', 'lib.es2022.d.ts'],
|
||||||
noImplicitAny: false, // Now allowing implicit any by default
|
noImplicitAny: false,
|
||||||
esModuleInterop: true,
|
esModuleInterop: true,
|
||||||
useDefineForClassFields: false,
|
useDefineForClassFields: false,
|
||||||
verbatimModuleSyntax: true,
|
verbatimModuleSyntax: true,
|
||||||
@@ -208,7 +251,7 @@ By default, `@git.zone/tsbuild` uses the following compiler options:
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
These options can be overridden by providing a custom `CompilerOptions` object.
|
These defaults are merged with your `tsconfig.json` options (if present), programmatic options, and CLI flags according to the priority order described above.
|
||||||
|
|
||||||
## Path Resolution
|
## Path Resolution
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@git.zone/tsbuild',
|
name: '@git.zone/tsbuild',
|
||||||
version: '2.6.8',
|
version: '2.7.0',
|
||||||
description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.'
|
description: 'A tool for compiling TypeScript files using the latest nightly features, offering flexible APIs and a CLI for streamlined development.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,19 +62,56 @@ export class TsBuild {
|
|||||||
* Helper function to read and process tsconfig.json
|
* Helper function to read and process tsconfig.json
|
||||||
*/
|
*/
|
||||||
private getTsConfigOptions(): CompilerOptions {
|
private getTsConfigOptions(): CompilerOptions {
|
||||||
const tsconfig = plugins.smartfile.fs.toObjectSync(plugins.path.join(paths.cwd, 'tsconfig.json'));
|
let tsconfig: any;
|
||||||
const returnObject: CompilerOptions = {};
|
|
||||||
|
// Try to read tsconfig.json, but don't fail if it doesn't exist
|
||||||
|
try {
|
||||||
|
tsconfig = plugins.smartfile.fs.toObjectSync(plugins.path.join(paths.cwd, 'tsconfig.json'));
|
||||||
|
} catch (error) {
|
||||||
|
// tsconfig.json doesn't exist or is invalid - use defaults
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (!tsconfig || !tsconfig.compilerOptions) {
|
if (!tsconfig || !tsconfig.compilerOptions) {
|
||||||
return returnObject;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process baseUrl
|
// Start by copying ALL compiler options from tsconfig.json
|
||||||
if (tsconfig.compilerOptions.baseUrl) {
|
const returnObject: CompilerOptions = { ...tsconfig.compilerOptions };
|
||||||
returnObject.baseUrl = tsconfig.compilerOptions.baseUrl;
|
|
||||||
|
// Apply special transformations for string-to-enum conversions
|
||||||
|
|
||||||
|
// Process target (convert string to enum)
|
||||||
|
if (tsconfig.compilerOptions.target && typeof tsconfig.compilerOptions.target === 'string') {
|
||||||
|
const targetKey = tsconfig.compilerOptions.target.toUpperCase();
|
||||||
|
if (targetKey in plugins.typescript.ScriptTarget) {
|
||||||
|
returnObject.target = plugins.typescript.ScriptTarget[targetKey as keyof typeof plugins.typescript.ScriptTarget];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process paths
|
// Process module (convert string to enum)
|
||||||
|
if (tsconfig.compilerOptions.module && typeof tsconfig.compilerOptions.module === 'string') {
|
||||||
|
const moduleKey = tsconfig.compilerOptions.module.toUpperCase();
|
||||||
|
if (moduleKey in plugins.typescript.ModuleKind) {
|
||||||
|
returnObject.module = plugins.typescript.ModuleKind[moduleKey as keyof typeof plugins.typescript.ModuleKind];
|
||||||
|
} else if (moduleKey === 'NODENEXT') {
|
||||||
|
returnObject.module = plugins.typescript.ModuleKind.NodeNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process moduleResolution (convert string to enum)
|
||||||
|
if (tsconfig.compilerOptions.moduleResolution && typeof tsconfig.compilerOptions.moduleResolution === 'string') {
|
||||||
|
const moduleResolutionKey = tsconfig.compilerOptions.moduleResolution.toUpperCase();
|
||||||
|
if (moduleResolutionKey in plugins.typescript.ModuleResolutionKind) {
|
||||||
|
returnObject.moduleResolution = plugins.typescript.ModuleResolutionKind[
|
||||||
|
moduleResolutionKey as keyof typeof plugins.typescript.ModuleResolutionKind
|
||||||
|
];
|
||||||
|
} else if (moduleResolutionKey === 'NODENEXT') {
|
||||||
|
returnObject.moduleResolution = plugins.typescript.ModuleResolutionKind.NodeNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply path transformations (ts_ → dist_ts_)
|
||||||
if (tsconfig.compilerOptions.paths) {
|
if (tsconfig.compilerOptions.paths) {
|
||||||
returnObject.paths = { ...tsconfig.compilerOptions.paths };
|
returnObject.paths = { ...tsconfig.compilerOptions.paths };
|
||||||
for (const path of Object.keys(returnObject.paths)) {
|
for (const path of Object.keys(returnObject.paths)) {
|
||||||
@@ -83,58 +120,24 @@ export class TsBuild {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process target
|
|
||||||
if (tsconfig.compilerOptions.target) {
|
|
||||||
if (typeof tsconfig.compilerOptions.target === 'string') {
|
|
||||||
const targetKey = tsconfig.compilerOptions.target.toUpperCase();
|
|
||||||
if (targetKey in plugins.typescript.ScriptTarget) {
|
|
||||||
returnObject.target = plugins.typescript.ScriptTarget[targetKey as keyof typeof plugins.typescript.ScriptTarget];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process module
|
|
||||||
if (tsconfig.compilerOptions.module) {
|
|
||||||
if (typeof tsconfig.compilerOptions.module === 'string') {
|
|
||||||
const moduleKey = tsconfig.compilerOptions.module.toUpperCase();
|
|
||||||
if (moduleKey in plugins.typescript.ModuleKind) {
|
|
||||||
returnObject.module = plugins.typescript.ModuleKind[moduleKey as keyof typeof plugins.typescript.ModuleKind];
|
|
||||||
} else if (moduleKey === 'NODENEXT') {
|
|
||||||
returnObject.module = plugins.typescript.ModuleKind.NodeNext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process moduleResolution
|
|
||||||
if (tsconfig.compilerOptions.moduleResolution) {
|
|
||||||
if (typeof tsconfig.compilerOptions.moduleResolution === 'string') {
|
|
||||||
const moduleResolutionKey = tsconfig.compilerOptions.moduleResolution.toUpperCase();
|
|
||||||
if (moduleResolutionKey in plugins.typescript.ModuleResolutionKind) {
|
|
||||||
returnObject.moduleResolution = plugins.typescript.ModuleResolutionKind[
|
|
||||||
moduleResolutionKey as keyof typeof plugins.typescript.ModuleResolutionKind
|
|
||||||
];
|
|
||||||
} else if (moduleResolutionKey === 'NODENEXT') {
|
|
||||||
returnObject.moduleResolution = plugins.typescript.ModuleResolutionKind.NodeNext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy boolean options directly
|
|
||||||
const booleanOptions = [
|
|
||||||
'experimentalDecorators', 'useDefineForClassFields',
|
|
||||||
'esModuleInterop', 'verbatimModuleSyntax'
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const option of booleanOptions) {
|
|
||||||
if (option in tsconfig.compilerOptions) {
|
|
||||||
(returnObject as any)[option] = (tsconfig.compilerOptions as any)[option];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnObject;
|
return returnObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns critical default options that should not be overridden by tsconfig.json
|
||||||
|
* These options are essential for tsbuild's functionality and build integrity
|
||||||
|
*/
|
||||||
|
private getCriticalDefaults(): CompilerOptions {
|
||||||
|
return {
|
||||||
|
outDir: 'dist_ts/', // Required for path transformation logic
|
||||||
|
noEmitOnError: true, // Build integrity - prevent broken builds
|
||||||
|
declaration: true, // Library consumers depend on .d.ts files
|
||||||
|
emitDecoratorMetadata: true, // Required for dependency injection frameworks
|
||||||
|
inlineSourceMap: true, // Consistent debugging experience
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process command line arguments and return applicable compiler options
|
* Process command line arguments and return applicable compiler options
|
||||||
*/
|
*/
|
||||||
@@ -162,6 +165,13 @@ export class TsBuild {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges compilerOptions with the default compiler options
|
* Merges compilerOptions with the default compiler options
|
||||||
|
*
|
||||||
|
* Merge order (later overwrites earlier):
|
||||||
|
* 1. compilerOptionsDefault - Base defaults for all options
|
||||||
|
* 2. getTsConfigOptions() - User's tsconfig.json (all options)
|
||||||
|
* 3. getCriticalDefaults() - Protected options that shouldn't be overridden by tsconfig.json
|
||||||
|
* 4. customTsOptions - Programmatic options (can override critical defaults)
|
||||||
|
* 5. getCommandLineOptions() - CLI flags (highest priority)
|
||||||
*/
|
*/
|
||||||
public mergeCompilerOptions(
|
public mergeCompilerOptions(
|
||||||
customTsOptions: CompilerOptions = {},
|
customTsOptions: CompilerOptions = {},
|
||||||
@@ -169,10 +179,11 @@ export class TsBuild {
|
|||||||
): CompilerOptions {
|
): CompilerOptions {
|
||||||
// create merged options
|
// create merged options
|
||||||
const mergedOptions: CompilerOptions = {
|
const mergedOptions: CompilerOptions = {
|
||||||
...compilerOptionsDefault,
|
...compilerOptionsDefault, // 1. All defaults
|
||||||
...customTsOptions,
|
...this.getTsConfigOptions(), // 2. User's tsconfig.json (all options)
|
||||||
...this.getCommandLineOptions(argvArg),
|
...this.getCriticalDefaults(), // 3. Protected overrides
|
||||||
...this.getTsConfigOptions(),
|
...customTsOptions, // 4. Programmatic options
|
||||||
|
...this.getCommandLineOptions(argvArg), // 5. CLI flags (highest priority)
|
||||||
};
|
};
|
||||||
|
|
||||||
return mergedOptions;
|
return mergedOptions;
|
||||||
|
|||||||
Reference in New Issue
Block a user