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:
2025-11-02 05:31:55 +00:00
parent 787becc4d3
commit 82ae8a0e4a
6 changed files with 2448 additions and 1527 deletions

View File

@@ -62,19 +62,56 @@ export class TsBuild {
* Helper function to read and process tsconfig.json
*/
private getTsConfigOptions(): CompilerOptions {
const tsconfig = plugins.smartfile.fs.toObjectSync(plugins.path.join(paths.cwd, 'tsconfig.json'));
const returnObject: CompilerOptions = {};
let tsconfig: any;
// 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) {
return returnObject;
return {};
}
// Process baseUrl
if (tsconfig.compilerOptions.baseUrl) {
returnObject.baseUrl = tsconfig.compilerOptions.baseUrl;
// Start by copying ALL compiler options from tsconfig.json
const returnObject: CompilerOptions = { ...tsconfig.compilerOptions };
// 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) {
returnObject.paths = { ...tsconfig.compilerOptions.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;
}
/**
* 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
*/
@@ -162,6 +165,13 @@ export class TsBuild {
/**
* 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(
customTsOptions: CompilerOptions = {},
@@ -169,10 +179,11 @@ export class TsBuild {
): CompilerOptions {
// create merged options
const mergedOptions: CompilerOptions = {
...compilerOptionsDefault,
...customTsOptions,
...this.getCommandLineOptions(argvArg),
...this.getTsConfigOptions(),
...compilerOptionsDefault, // 1. All defaults
...this.getTsConfigOptions(), // 2. User's tsconfig.json (all options)
...this.getCriticalDefaults(), // 3. Protected overrides
...customTsOptions, // 4. Programmatic options
...this.getCommandLineOptions(argvArg), // 5. CLI flags (highest priority)
};
return mergedOptions;