简答
在正确处理 tsconfig extends 关键字继承时从 tsconfig 文件读取编译器选项的函数
function getCompilerOptionsJSONFollowExtends(filename: string): {[key: string]: any} {
let compopts = {};
const config = ts.readConfigFile(filename, ts.sys.readFile).config;
if (config.extends) {
const rqrpath = require.resolve(config.extends);
compopts = getCompilerOptionsJSONFollowExtends(rqrpath);
}
return {
...compopts,
...config.compilerOptions,
};
}
其结果可以通过ts.CompilerOptions 类型转换
const jsonCompopts = getCompilerOptionsJSONFollowExtends('tsconfig.json')
const tmp = ts.convertCompilerOptionsFromJson(jsonCompopts,'')
if (tmp.errors.length>0) throw new Error('...')
const tsCompopts:ts.CompilerOptions = tmp.options
TL;DR
这些相关函数存在于typescript@4.3.2:
ts.readConfigFile
ts.parseConfigFileTextToJson
ts.convertCompilerOptionsFromJson
ts.parseJsonConfigFileContent
ts.parseJsonSourceFileConfigFileContent
这篇文章只针对前三个:
ts.readConfigFile
console.log(
JSON.stringify(
ts.readConfigFile('./tsconfig.base.json', ts.sys.readFile),
null,
2
)
);
tsconfig.base.json 有内容的地方
{
"extends": "@tsconfig/node14/tsconfig.json",
//comment
"compilerOptions": {
"declaration": true,
"skipLibCheck": true,
"sourceMap": true,
"lib": ["es2020"],// trailing comma
}
}
结果
{
"config": {
"extends": "@tsconfig/node14/tsconfig.json",
"compilerOptions": {
"declaration": true,
"skipLibCheck": true,
"sourceMap": true,
"lib": [
"es2020"
]
}
}
}
这里要注意的事情:
- extends 引用的配置文件未拉入和展开。
- 编译器选项未转换为 typescript 编译器 API 函数所需的内部形式。 (不是
ts.CompilerOptions 类型)
- 评论被删除,尾随逗号被忽略。
ts.parseConfigFileTextToJson
const parsed2 = ts.parseConfigFileTextToJson(
''/*'./tsconfig.base.json'*/, `
{
"extends": "@tsconfig/node14/tsconfig.json",
// comment
"compilerOptions": {
"declaration": true,
"skipLibCheck": true,
"sourceMap": true,
"lib": ["es2020"], // trailing comma
}
}
`);
console.log(JSON.stringify(parsed2, null, 2));
结果
{
"config": {
"extends": "@tsconfig/node14/tsconfig.json",
"compilerOptions": {
"declaration": true,
"skipLibCheck": true,
"sourceMap": true,
"lib": [
"es2020"
]
}
}
}
功能与ts.readConfigFile相同,只是文字为
传递而不是文件名。
注意:第一个参数(文件名)被忽略,除非可能有错误。添加真实文件名但将第二个参数留空会导致空输出。此函数无法读取文件。
ts.convertCompilerOptionsFromJson
const parsed1 = ts.convertCompilerOptionsFromJson(
{
lib: ['es2020'],
module: 'commonjs',
target: 'es2020',
},
''
);
console.log(JSON.stringify(parsed1, null, 2));
结果
{
"options": {
"lib": [
"lib.es2020.d.ts"
],
"module": 1,
"target": 7
},
"errors": []
}
结果的 options 属性的值是 typescript 编译器 API 所需的内部格式。 (即它的类型为ts.CompilerOptions)
module的值(1)实际上是ts.ModuleKind.CommonJS的编译值,target的值(7)实际上是ts.ScriptTarget.ES2020的编译值。
讨论/扩展
当extends 关键字没有发挥作用时
通过使用以下函数:
ts.readConfigFile
ts.convertCompilerOptionsFromJson
如上图,你应该可以得到你想要的。
但是,当extends 关键字确实发挥作用时,情况就更加复杂了。我找不到现有的 API 函数可以自动扩展。
不过,有一个 CLI 函数可以做到这一点
npx tsc -p tsconfig.base.json --showConfig
结果
{
"compilerOptions": {
"lib": [
"es2020"
],
"module": "commonjs",
"target": "es2020",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"sourceMap": true
},
"files": [
"./archive/doc-generator.ts",
"./archive/func-params-exp.ts",
"./archive/reprinting.ts",
"./archive/sw.ts",
....
....
]
}
所有隐含的文件也被输出。
bash 中的以下一行将只产生编译选项 -
echo 'console.log(JSON.stringify(JSON.parse('\'`npx tsc -p tsconfig.base.json --showConfig`\'').compilerOptions,null,2))' | node
只产生编译选项
{
"lib": [
"es2020"
],
"module": "commonjs",
"target": "es2020",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"sourceMap": true
}
显然,从程序中调用 CLI 远非理想。
如何使用 API 进行扩展
显示原理:
const config1 = ts.readConfigFile('./tsconfig.base.json', ts.sys.readFile).config
console.log(JSON.stringify(config1,null,2))
const tsrpath = ts.sys.resolvePath(config1.extends)
console.log(tsrpath)
const rqrpath = require.resolve(config1.extends)
console.log(rqrpath)
const config2 = ts.readConfigFile(rqrpath, ts.sys.readFile).config
console.log(JSON.stringify(config2,null,2))
结果
{
"extends": "@tsconfig/node14/tsconfig.json",
"compilerOptions": {
"declaration": true,
"skipLibCheck": true,
"sourceMap": true,
"lib": [
"es2020"
]
}
}
/mnt/common/github/tscapi/@tsconfig/node14/tsconfig.json
/mnt/common/github/tscapi/node_modules/@tsconfig/node14/tsconfig.json
{
"$schema": "https://json.schemastore.org/tsconfig",
"display": "Node 14",
"compilerOptions": {
"lib": [
"es2020"
],
"module": "commonjs",
"target": "es2020",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
请注意,require.resolve 解析为我们想要的,但 ts.sys.resolve 没有。
这是一个返回编译器选项的函数,它正确地继承自extends:
function getCompileOptionsJSONFollowExtends(filename: string): {[key: string]: any} {
let compopts: ts.CompilerOptions = {};
const config = ts.readConfigFile(filename, ts.sys.readFile).config;
if (config.extends) {
const rqrpath = require.resolve(config.extends);
compopts = getCompileOptionsJSONFollowExtends(rqrpath);
}
compopts = {
...compopts,
...config.compilerOptions,
};
return compopts;
}
试运行-
const jsonCompopts = getCompileOptionsJSONFollowExtends('./tsconfig.base.json')
console.log(JSON.stringify(jsonCompopts,null,2))
const tsCompopts = ts.convertCompilerOptionsFromJson(jsonCompopts,'')
console.log(JSON.stringify(tsCompopts,null,2))
console.log('');
结果
{
"lib": [
"es2020"
],
"module": "commonjs",
"target": "es2020",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"sourceMap": true
}
{
"options": {
"lib": [
"lib.es2020.d.ts"
],
"module": 1,
"target": 7,
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"sourceMap": true
},
"errors": []
}