Hur man kompilera tsconfig.json till ett konfigurationsobjekt med TypeScript API?
Jag letar efter ett offentligt TypeScript-API (helst version 4.x) för att parsas tsconfig.json/jsconfig.json-filer till kompletta TypeScript-konfigurationsobjekt (med "kompletta" menar jag att alla dess egenskaper är ifyllda med värden - antingen tagna uttryckligen från *.json eller implicit från kända standardvärden), som i:
import ts from "typescript";
const tsconfig = await ts.parseConfigFile('./tsconfig.json');
tsconfig.compilerOptions.noEmit;
// false
// (or whatever it is set to in *.json)
Finns det en sådan funktion i typescript npm-paketet? Jag kunde inte hitta den själv, varken i dokumentationen eller i IntelliSense-förslag (de som visas med Ctrl + Space).
Jag har sett en annan fråga med samma typ av begäran, men a) den är obesvarad, b) länken i kommentarerna hänvisar till en 5,5 år gammal lösning, - det måste ha skett en förändring i API sedan dess.
I verkligheten behöver jag bara ett fåtal egenskaper från konfigurationsfilen, så det skulle vara relativt enkelt att skriva ett par hjälpmetoder för att parsa *.json-filen och hämta dess innehåll. Men:
- det finns mer exotiska användningsfall (såsom att använda projektreferenser och/eller utöka föräldrakonfigurationsobjektet), där en sådan lösning inte skulle fungera;
- de ovan nämnda *.json-objekten kan ha kommentarer och avslutande kommatecken, så det är inte trivialt att ens analysera filen från början;
- det skulle vara konstigt att skriva om något som redan är känt för att vara skrivet.
Svar #1
Redigera
I en kommentar nedan förklarar @Hiroki Osame att detta svar genom att använda ts.parseJsonConfigFileContent gjorde det möjligt för honom att få extends följd automatiskt utan någon "handtillverkning".
Även på den här sidan verkar @Simon Buchans svar vara liknande korrekt.
Kort svar
En funktion för att läsa kompilatoralternativ från en tsconfig-fil samtidigt som tsconfig extends-nyckelns arv hanteras korrekt
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,
};
}
Resultatet av det kan konverteras till typen ts.CompilerOptions via
const jsonCompopts = getCompilerOptionsJSONFollowExtends('tsconfig.json')
const tmp = ts.convertCompilerOptionsFromJson(jsonCompopts,'')
if (tmp.errors.length>0) throw new Error('...')
const tsCompopts:ts.CompilerOptions = tmp.options
Sammanfattning
Dessa relaterade funktioner finns i [email :
ts.readConfigFile
ts.parseConfigFileTextToJson
ts.convertCompilerOptionsFromJson
ts.parseJsonConfigFileContent
ts.parseJsonSourceFileConfigFileContent
Detta inlägg tar bara upp de första tre:
ts.readConfigFile
console.log(
JSON.stringify(
ts.readConfigFile('./tsconfig.base.json', ts.sys.readFile),
null,
2
)
);
där tsconfig.base.json har innehåll
{
"extends": "@tsconfig/node14/tsconfig.json",
//comment
"compilerOptions": {
"declaration": true,
"skipLibCheck": true,
"sourceMap": true,
"lib": ["es2020"],// trailing comma
}
}
resulterar i
{
"config": {
"extends": "@tsconfig/node14/tsconfig.json",
"compilerOptions": {
"declaration": true,
"skipLibCheck": true,
"sourceMap": true,
"lib": [
"es2020"
]
}
}
}
Sakerna att lägga märke till här:
- Konfigurationsfilen som refereras av extends hämtas inte in och expanderas.
- Kompileringsalternativen omvandlas inte till det interna format som krävs av TypeScript-kompilatorns API-funktioner. (Inte av typen
ts.CompilerOptions) - Kommentarer tas bort och avslutande kommatecken ignoreras.
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));
resulterar i
{
"config": {
"extends": "@tsconfig/node14/tsconfig.json",
"compilerOptions": {
"declaration": true,
"skipLibCheck": true,
"sourceMap": true,
"lib": [
"es2020"
]
}
}
}
Funktionen är densamma som ts.readConfigFile förutom att text skickas istället för ett filnamn.
Observera: Det första argumentet (filnamn) ignoreras om det inte kanske finns ett fel. Att lägga till ett riktigt filnamn men lämna det andra argumentet tomt resulterar i tomt output. Denna funktion kan inte läsa in filer.
ts.convertCompilerOptionsFromJson
const parsed1 = ts.convertCompilerOptionsFromJson(
{
lib: ['es2020'],
module: 'commonjs',
target: 'es2020',
},
''
);
console.log(JSON.stringify(parsed1, null, 2));
resulterar i
{
"options": {
"lib": [
"lib.es2020.d.ts"
],
"module": 1,
"target": 7
},
"errors": []
}
Värdet av options-egenskapen i resultatet är i det interna format som krävs av TypeScript-kompilator-API:et. (Dvs. det är av typen ts.CompilerOptions)
Värdet (1) av module är faktiskt det kompilerade värdet av ts.ModuleKind.CommonJS, och värdet (7) av target är faktiskt det kompilerade värdet av ts.ScriptTarget.ES2020.
diskussion/utvidgar
När extends-nyckelordet INTE spelar någon roll, då genom att använda följande funktioner:
ts.readConfigFilets.convertCompilerOptionsFromJson
Som visas ovan bör du kunna få vad du vill.
Men när extends-nyckelordet faktiskt används är det mer komplicerat. Jag kan inte hitta någon befintlig API-funktion för att automatiskt följa extends.
Det finns dock en CLI-funktion för att göra det
npx tsc -p tsconfig.base.json --showConfig
resulterar i
{
"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",
....
....
]
}
där alla filer som implicit inkluderas också skrivs ut.
Följande enradare i bash kommer att ge enbart kompilationsalternativen -
echo 'console.log(JSON.stringify(JSON.parse('\'`npx tsc -p tsconfig.base.json --showConfig`\'').compilerOptions,null,2))' | node
resulterar endast i kompilationsalternativen
{
"lib": [
"es2020"
],
"module": "commonjs",
"target": "es2020",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"sourceMap": true
}
Uppenbarligen är det långt ifrån idealiskt att anropa CLI från ett program.
hur man följer extends med hjälp av API
Visa principen:
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))
resulterar i
{
"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
}
}
Observera att require.resolve löser till det vi vill, men ts.sys.resolve gör det inte.
Här är en funktion som returnerar kompilatoralternativ korrekt ärvt från 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;
}
Testkörning -
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('');
resulterar i
{
"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": []
}
Svar #2
Lite förvirrande bör du använda ts.getParsedCommandLineOfConfigFile():
> ts.getParsedCommandLineOfConfigFile('tsconfig.json', {}, ts.sys)
{
options: {
moduleResolution: 2,
module: 99,
target: 6,
lib: [ 'lib.es2019.d.ts' ],
types: [ 'node' ],
strict: true,
sourceMap: true,
esModuleInterop: true,
importsNotUsedAsValues: 2,
importHelpers: true,
incremental: true,
composite: true,
skipLibCheck: true,
noEmit: true,
configFilePath: 'C:/code/.../tsconfig.json'
},
watchOptions: undefined,
fileNames: [
'C:/code/.../src/index.tsx',
...
Den tredje parametern är faktiskt en ts.ParseConfigFileHost, så du bör förmodligen implementera den manuellt (med hjälp av implementationer från ts.sys)
Du kan också använda ts.parseJsonFileContent(tsconfigContent, ts.sys, baseDir, {}, errorMessageFileName) om du redan har parsat konfigurationen, till exempel när den är inline i någon större konfigurationsfil.
