如何使用 TypeScript API 将 tsconfig.json 编译为配置对象?

Dmi*_*sky 9 typescript tsconfig

我正在寻找一个公共官方 TypeScript API(最好是 4.x 版本),用于将tsconfig.json/jsconfig.json文件解析为完整的 TypeScript 配置对象(我所说的“完整”是指,它的所有属性都填充了值,\xe2\x80\ x93 要么显式地取自*.json,要么隐式地取自已知的默认值),如下所示:

\n
import ts from "typescript";\n\nconst tsconfig = await ts.parseConfigFile(\'./tsconfig.json\');\n\ntsconfig.compilerOptions.noEmit;\n// false\n// (or whatever it is set to in *.json)\n
Run Code Online (Sandbox Code Playgroud)\n

typescriptnpm包里有这样的功能吗?我自己在文档Ctrl和 IntelliSense 建议(由+调用的建议)中都找不到它Space

\n

我看到了另一个具有相同类型请求的问题,但是a)它没有得到答复,b)评论中的链接引用了一个5.5年前的解决方案,\ xe2 \ x80 \ x93,那里\必须更改从那时起API。

\n
\n

实际上,我只需要配置文件中的少数属性,因此编写几个帮助程序来解析文件*.json并获取其内容相对容易。然而:

\n
    \n
  • 还有更多奇特的用例(例如使用项目引用和/或扩展父配置对象),在这些用例中,这样的解决方案不起作用;
  • \n
  • 前面提到的*.json对象可能有注释和尾随逗号,因此首先解析该文件并不简单;
  • \n
  • 重写已知已经写过的东西会很奇怪。
  • \n
\n

Cra*_*cks 13

编辑

在下面的评论中,@Hiroki Osame 解释说,通过使用这个答案,ts.parseJsonConfigFileContent他能够extends自动获得以下答案,而无需任何“手工制作”。

同样在此页面上,@Simon Buchan 的答案看起来同样正确。

简答

extends在正确处理 tsconfig关键字继承的同时从 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,
  };
}

Run Code Online (Sandbox Code Playgroud)

其结果可以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
Run Code Online (Sandbox Code Playgroud)

长话短说

这些相关函数存在于typescript@4.3.2

ts.readConfigFile
ts.parseConfigFileTextToJson
ts.convertCompilerOptionsFromJson
ts.parseJsonConfigFileContent
ts.parseJsonSourceFileConfigFileContent
Run Code Online (Sandbox Code Playgroud)

这篇文章只讨论前三个:

ts.readConfigFile

console.log(
  JSON.stringify(
    ts.readConfigFile('./tsconfig.base.json', ts.sys.readFile),
    null,
    2
  )
);
Run Code Online (Sandbox Code Playgroud)

哪里tsconfig.base.json有内容

{
  "extends": "@tsconfig/node14/tsconfig.json",
//comment
  "compilerOptions": {
    "declaration": true,
    "skipLibCheck": true,
    "sourceMap": true,
    "lib": ["es2020"],// trailing comma
  }
}
Run Code Online (Sandbox Code Playgroud)

结果是

{
  "config": {
    "extends": "@tsconfig/node14/tsconfig.json",
    "compilerOptions": {
      "declaration": true,
      "skipLibCheck": true,
      "sourceMap": true,
      "lib": [
        "es2020"
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这里需要注意的事项:

  1. extends 引用的配置文件不会被拉入和扩展。
  2. 编译器选项不会转换为 typescript 编译器 API 函数所需的内部形式。(不是类型ts.CompilerOptions
  3. 注释被删除,尾随逗号被忽略。

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));
Run Code Online (Sandbox Code Playgroud)

结果是

{
  "config": {
    "extends": "@tsconfig/node14/tsconfig.json",
    "compilerOptions": {
      "declaration": true,
      "skipLibCheck": true,
      "sourceMap": true,
      "lib": [
        "es2020"
      ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

该功能与 相同,ts.readConfigFile只是传递的是文本
而不是文件名。

注意:第一个参数(文件名)将被忽略,除非可能存在错误。添加真实的文件名但将第二个参数留空会导致空输出。该函数无法读取文件。

ts.convertCompilerOptionsFromJson

  const parsed1 = ts.convertCompilerOptionsFromJson(
    {
      lib: ['es2020'],
      module: 'commonjs',
      target: 'es2020',
    },
    ''
  );
  console.log(JSON.stringify(parsed1, null, 2));
Run Code Online (Sandbox Code Playgroud)

结果是

{
  "options": {
    "lib": [
      "lib.es2020.d.ts"
    ],
    "module": 1,
    "target": 7
  },
  "errors": []
}
Run Code Online (Sandbox Code Playgroud)

结果的属性值options采用 TypeScript 编译器 API 所需的内部格式。(即它是类型ts.CompilerOptions

的值(1)module实际上是 的编译值ts.ModuleKind.CommonJS,值(7)target实际上是 的编译值ts.ScriptTarget.ES2020

讨论/延伸

extends关键字不起作用时,可以使用以下函数:

  • ts.readConfigFile
  • ts.convertCompilerOptionsFromJson

如上所示,你应该能够得到你想要的。

然而,当extends关键字真正发挥作用时,情况就会变得更加复杂。我找不到现有的 API 函数可以自动遵循扩展。

然而,有一个 CLI 功能可以做到这一点

npx tsc -p tsconfig.base.json --showConfig
Run Code Online (Sandbox Code Playgroud)

结果是

{
    "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",
        ....
        ....
    ]
}    
Run Code Online (Sandbox Code Playgroud)

其中隐式包含的所有文件也会输出。

bash 中的以下一行将仅产生编译选项 -

echo 'console.log(JSON.stringify(JSON.parse('\'`npx tsc -p tsconfig.base.json --showConfig`\'').compilerOptions,null,2))' | node
Run Code Online (Sandbox Code Playgroud)

仅产生编译选项

{
  "lib": [
    "es2020"
  ],
  "module": "commonjs",
  "target": "es2020",
  "strict": true,
  "esModuleInterop": true,
  "skipLibCheck": true,
  "forceConsistentCasingInFileNames": true,
  "declaration": true,
  "sourceMap": true
}
Run Code Online (Sandbox Code Playgroud)

显然,从程序调用 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))
Run Code Online (Sandbox Code Playgroud)

结果是

{
  "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
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,它require.resolve解决了我们想要的问题,但ts.sys.resolve没有解决。

这是一个正确返回从扩展继承的编译器选项的函数:

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;
}
Run Code Online (Sandbox Code Playgroud)

测试运行 -

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('');
Run Code Online (Sandbox Code Playgroud)

结果是

{
  "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": []
}
Run Code Online (Sandbox Code Playgroud)

  • 写得非常透彻!我发现 `parseJsonConfigFileContent` 解析了扩展。例如。/sf/answers/3772875361/ (4认同)

dob*_*ler -1

tsconfig.json我可能会误解你的问题,但是加载内容文件并用 解析它还不够吗JSON.parse

const fs = require('fs');

const txt = fs.readFileSync('./tsconfig.json');
try {
   const obj = JSON.parse(txt);
   console.log(obj);
} catch (e) {
   console.error(e);
}
Run Code Online (Sandbox Code Playgroud)