Išsamus „TypeScript Compiler API“ vadovas, apimantis abstrakčios sintaksės medžius (AST), kodo analizę, transformavimą ir generavimą tarptautiniams programuotojams.
TypeScript Compiler API: AST manipuliavimo ir kodo transformavimo įvaldymas
„TypeScript Compiler API“ suteikia galingą sąsają, leidžiančią analizuoti, manipuliuoti ir generuoti „TypeScript“ bei „JavaScript“ kodą. Jos pagrindas – abstraktus sintaksės medis (AST), struktūrizuotas jūsų išeities kodo vaizdas. Supratimas, kaip dirbti su AST, atveria galimybes kurti pažangius įrankius, tokius kaip linterius, kodo formatuotojus, statinius analizatorius ir individualius kodo generatorius.
Kas yra „TypeScript Compiler API“?
„TypeScript Compiler API“ yra „TypeScript“ sąsajų ir funkcijų rinkinys, atveriantis vidinį „TypeScript“ kompiliatoriaus veikimą. Tai leidžia programuotojams programiškai sąveikauti su kompiliavimo procesu, neapsiribojant vien kodo kompiliavimu. Ją galite naudoti:
- Kodo analizei: Tikrinti kodo struktūrą, identifikuoti galimas problemas ir išgauti semantinę informaciją.
- Kodo transformavimui: Keisti esamą kodą, pridėti naujų funkcijų arba automatiškai pertvarkyti kodą.
- Kodo generavimui: Kurti naują kodą nuo nulio, remiantis šablonais ar kita įvestimi.
Ši API yra būtina kuriant sudėtingus programavimo įrankius, kurie gerina kodo kokybę, automatizuoja pasikartojančias užduotis ir didina programuotojų produktyvumą.
Abstraktus sintaksės medis (AST) ir jo supratimas
AST yra į medį panašus jūsų kodo struktūros vaizdas. Kiekvienas medžio mazgas atspindi sintaksinę konstrukciją, pavyzdžiui, kintamojo deklaraciją, funkcijos iškvietimą ar valdymo srauto sakinį. „TypeScript Compiler API“ suteikia įrankius, leidžiančius peržvelgti AST, tikrinti jo mazgus ir juos keisti.
Panagrinėkime šį paprastą „TypeScript“ kodą:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("World"));
Šio kodo AST atspindėtų funkcijos deklaraciją, grąžinimo sakinį, šablono literalą, „console.log“ iškvietimą ir kitus kodo elementus. Vizualizuoti AST gali būti sudėtinga, tačiau tokie įrankiai kaip AST explorer (astexplorer.net) gali padėti. Šie įrankiai leidžia įvesti kodą ir matyti atitinkamą AST patogiu formatu. Naudodami AST Explorer suprasite, kokio tipo kodo struktūrą manipuliuosite.
Pagrindiniai AST mazgų tipai
„TypeScript Compiler API“ apibrėžia įvairius AST mazgų tipus, kurių kiekvienas atspindi skirtingą sintaksinę konstrukciją. Štai keletas dažniausiai pasitaikančių mazgų tipų:
- SourceFile: Atspindi visą „TypeScript“ failą.
- FunctionDeclaration: Atspindi funkcijos apibrėžimą.
- VariableDeclaration: Atspindi kintamojo deklaraciją.
- Identifier: Atspindi identifikatorių (pvz., kintamojo pavadinimą, funkcijos pavadinimą).
- StringLiteral: Atspindi eilutės literalą.
- CallExpression: Atspindi funkcijos iškvietimą.
- ReturnStatement: Atspindi grąžinimo sakinį.
Kiekvienas mazgo tipas turi savybes, kurios suteikia informacijos apie atitinkamą kodo elementą. Pavyzdžiui, `FunctionDeclaration` mazgas gali turėti savybes, apibūdinančias jo pavadinimą, parametrus, grąžinimo tipą ir kūną.
Darbo su Compiler API pradžia
Norėdami pradėti naudoti Compiler API, turėsite įdiegti „TypeScript“ ir turėti pagrindinį „TypeScript“ sintaksės supratimą. Štai paprastas pavyzdys, kaip nuskaityti „TypeScript“ failą ir atspausdinti jo AST:
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015, // Tikslinė ECMAScript versija
true // SetParentNodes: true, kad AST išsaugotų nuorodas į tėvinius mazgus
);
function printAST(node: ts.Node, indent = 0) {
const indentStr = " ".repeat(indent);
console.log(`${indentStr}${ts.SyntaxKind[node.kind]}`);
node.forEachChild(child => printAST(child, indent + 1));
}
printAST(sourceFile);
Paaiškinimas:
- Modulių importavimas: Importuojamas `typescript` modulis ir `fs` modulis failų sistemos operacijoms.
- Išeities failo nuskaitymas: Nuskaitomas „TypeScript“ failo, pavadinto `example.ts`, turinys. Kad tai veiktų, turėsite sukurti `example.ts` failą.
- SourceFile sukūrimas: Sukuriamas `SourceFile` objektas, kuris yra AST šaknis. `ts.createSourceFile` funkcija išanalizuoja išeities kodą ir sugeneruoja AST.
- AST atspausdinimas: Apibrėžiama rekursinė funkcija `printAST`, kuri peržvelgia AST ir atspausdina kiekvieno mazgo tipą (kind).
- printAST iškvietimas: Iškviečiama `printAST` funkcija, kad būtų pradėtas spausdinti AST nuo šakninio `SourceFile` mazgo.
Norėdami paleisti šį kodą, išsaugokite jį kaip `.ts` failą (pvz., `ast-example.ts`), sukurkite `example.ts` failą su „TypeScript“ kodu, o tada sukompiliuokite ir paleiskite kodą:
tsc ast-example.ts
node ast-example.js
Tai atspausdins jūsų `example.ts` failo AST konsolėje. Išvestyje matysite mazgų hierarchiją ir jų tipus. Pavyzdžiui, galite pamatyti `FunctionDeclaration`, `Identifier`, `Block` ir kitus mazgų tipus.
AST peržvelgimas
Compiler API siūlo kelis būdus peržvelgti AST. Paprasčiausias yra naudoti `forEachChild` metodą, kaip parodyta ankstesniame pavyzdyje. Šis metodas aplanko kiekvieną nurodyto mazgo vaiką.
Sudėtingesniems peržvelgimo scenarijams galite naudoti lankytojo (Visitor) šabloną. Lankytojas yra objektas, apibrėžiantis metodus, kurie bus iškviesti tam tikriems mazgų tipams. Tai leidžia pritaikyti peržvelgimo procesą ir atlikti veiksmus priklausomai nuo mazgo tipo.
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
class IdentifierVisitor {
visit(node: ts.Node) {
if (ts.isIdentifier(node)) {
console.log(`Rastas identifikatorius: ${node.text}`);
}
ts.forEachChild(node, n => this.visit(n));
}
}
const visitor = new IdentifierVisitor();
visitor.visit(sourceFile);
Paaiškinimas:
- IdentifierVisitor klasė: Apibrėžiama `IdentifierVisitor` klasė su `visit` metodu.
- Visit metodas: `visit` metodas tikrina, ar dabartinis mazgas yra `Identifier`. Jei taip, jis atspausdina identifikatoriaus tekstą. Tada jis rekursiškai iškviečia `ts.forEachChild`, kad aplankytų vaikus.
- Lankytojo sukūrimas: Sukuriama `IdentifierVisitor` instancija.
- Peržvelgimo pradžia: Iškviečiamas `visit` metodas su `SourceFile` objektu, kad būtų pradėtas peržvelgimas.
Šis pavyzdys parodo, kaip rasti visus identifikatorius AST. Galite pritaikyti šį šabloną ieškant kitų mazgų tipų ir atliekant skirtingus veiksmus.
AST transformavimas
Tikroji Compiler API galia slypi jos gebėjime transformuoti AST. Galite modifikuoti AST, kad pakeistumėte savo kodo struktūrą ir elgseną. Tai yra pagrindas kodo pertvarkymo įrankiams, kodo generatoriams ir kitiems pažangiems įrankiams.
Norėdami transformuoti AST, turėsite naudoti `ts.transform` funkciją. Ši funkcija priima `SourceFile` ir `TransformerFactory` funkcijų sąrašą. `TransformerFactory` yra funkcija, kuri priima `TransformationContext` ir grąžina `Transformer` funkciją. `Transformer` funkcija yra atsakinga už AST mazgų lankymą ir transformavimą.
Štai paprastas pavyzdys, kaip pridėti komentarą „TypeScript“ failo pradžioje:
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
const transformerFactory: ts.TransformerFactory = context => {
return transformer => {
return node => {
if (ts.isSourceFile(node)) {
// Sukurti pradinį komentarą
const comment = ts.addSyntheticLeadingComment(
node,
ts.SyntaxKind.MultiLineCommentTrivia,
" Šis failas buvo automatiškai transformuotas ",
true // hasTrailingNewLine
);
return node;
}
return node;
};
};
};
const { transformed } = ts.transform(sourceFile, [transformerFactory]);
const printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed
});
const result = printer.printFile(transformed[0]);
fs.writeFileSync("example.transformed.ts", result);
Paaiškinimas:
- TransformerFactory: Apibrėžiama `TransformerFactory` funkcija, kuri grąžina `Transformer` funkciją.
- Transformer: `Transformer` funkcija tikrina, ar dabartinis mazgas yra `SourceFile`. Jei taip, ji prideda pradinį komentarą prie mazgo naudodama `ts.addSyntheticLeadingComment`.
- ts.transform: Iškviečiama `ts.transform` funkcija, kad transformacija būtų pritaikyta `SourceFile`.
- Printer: Sukuriamas `Printer` objektas, skirtas generuoti kodą iš transformuoto AST.
- Spausdinimas ir įrašymas: Atspausdinamas transformuotas kodas ir įrašomas į naują failą, pavadintą `example.transformed.ts`.
Šis pavyzdys demonstruoja paprastą transformaciją, tačiau galite naudoti tą patį šabloną atlikdami sudėtingesnes transformacijas, tokias kaip kodo pertvarkymas, registravimo sakinių pridėjimas ar dokumentacijos generavimas.
Pažangios transformavimo technikos
Štai keletas pažangių transformavimo technikų, kurias galite naudoti su Compiler API:
- Naujų mazgų kūrimas: Naudokite `ts.createXXX` funkcijas naujiems AST mazgams kurti. Pavyzdžiui, `ts.createVariableDeclaration` sukuria naują kintamojo deklaracijos mazgą.
- Mazgų pakeitimas: Pakeiskite esamus mazgus naujais, naudodami `ts.visitEachChild` funkciją.
- Mazgų pridėjimas: Pridėkite naujus mazgus į AST, naudodami `ts.updateXXX` funkcijas. Pavyzdžiui, `ts.updateBlock` atnaujina bloko sakinį naujais sakiniais.
- Mazgų šalinimas: Pašalinkite mazgus iš AST grąžindami `undefined` iš transformatoriaus funkcijos.
Kodo generavimas
Po AST transformavimo turėsite iš jo sugeneruoti kodą. Compiler API šiam tikslui suteikia `Printer` objektą. `Printer` priima AST ir generuoja kodo eilutės reprezentaciją.
`ts.createPrinter` funkcija sukuria `Printer` objektą. Galite konfigūruoti spausdintuvą su įvairiomis parinktimis, pavyzdžiui, kokį naujos eilutės simbolį naudoti ir ar išvesti komentarus.
`printer.printFile` metodas priima `SourceFile` ir grąžina kodo eilutės reprezentaciją. Tada šią eilutę galite įrašyti į failą.
Praktinis Compiler API taikymas
„TypeScript Compiler API“ turi daugybę praktinių taikymų programinės įrangos kūrime. Štai keletas pavyzdžių:
- Linteriai: Kurkite individualius linterius, kad užtikrintumėte kodavimo standartus ir identifikuotumėte galimas problemas jūsų kode.
- Kodo formatuotojai: Kurkite kodo formatuotojus, kad automatiškai formatuotumėte kodą pagal konkretų stiliaus vadovą.
- Statiniai analizatoriai: Kurkite statinius analizatorius, kad aptiktumėte klaidas, saugumo pažeidžiamumus ir našumo problemas jūsų kode.
- Kodo generatoriai: Generuokite kodą iš šablonų ar kitos įvesties, automatizuodami pasikartojančias užduotis ir mažindami standartinio kodo kiekį. Pavyzdžiui, generuoti API klientus ar duomenų bazių schemas iš aprašymo failo.
- Pertvarkymo įrankiai: Kurkite pertvarkymo įrankius, kad automatiškai pervadintumėte kintamuosius, iškeltumėte funkcijas ar perkeltumėte kodą tarp failų.
- Internacionalizavimo (i18n) automatizavimas: Automatiškai ištraukite verčiamas eilutes iš „TypeScript“ kodo ir generuokite lokalizacijos failus skirtingoms kalboms. Pavyzdžiui, įrankis galėtų nuskaityti kodą, ieškodamas eilučių, perduodamų `translate()` funkcijai, ir automatiškai pridėti jas į vertimų resurso failą.
Pavyzdys: Paprasto linterio kūrimas
Sukurkime paprastą linterį, kuris tikrina nenaudojamus kintamuosius „TypeScript“ kode. Šis linteris identifikuos kintamuosius, kurie yra deklaruoti, bet niekada nenaudojami.
import * as ts from "typescript";
import * as fs from "fs";
const fileName = "example.ts";
const sourceCode = fs.readFileSync(fileName, "utf8");
const sourceFile = ts.createSourceFile(
fileName,
sourceCode,
ts.ScriptTarget.ES2015,
true
);
function findUnusedVariables(sourceFile: ts.SourceFile) {
const usedVariables = new Set();
function visit(node: ts.Node) {
if (ts.isIdentifier(node)) {
usedVariables.add(node.text);
}
ts.forEachChild(node, visit);
}
visit(sourceFile);
const unusedVariables: string[] = [];
function checkVariableDeclaration(node: ts.Node) {
if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name)) {
const variableName = node.name.text;
if (!usedVariables.has(variableName)) {
unusedVariables.push(variableName);
}
}
ts.forEachChild(node, checkVariableDeclaration);
}
checkVariableDeclaration(sourceFile);
return unusedVariables;
}
const unusedVariables = findUnusedVariables(sourceFile);
if (unusedVariables.length > 0) {
console.log("Nenaudojami kintamieji:");
unusedVariables.forEach(variable => console.log(`- ${variable}`));
} else {
console.log("Nenaudojamų kintamųjų nerasta.");
}
Paaiškinimas:
- findUnusedVariables funkcija: Apibrėžiama funkcija `findUnusedVariables`, kuri kaip įvestį priima `SourceFile`.
- usedVariables aibė: Sukuriama `Set` aibė, kurioje saugomi naudojamų kintamųjų pavadinimai.
- visit funkcija: Apibrėžiama rekursinė funkcija `visit`, kuri peržvelgia AST ir prideda visų identifikatorių pavadinimus į `usedVariables` aibę.
- checkVariableDeclaration funkcija: Apibrėžiama rekursinė funkcija `checkVariableDeclaration`, kuri tikrina, ar kintamojo deklaracija yra nenaudojama. Jei taip, ji prideda kintamojo pavadinimą į `unusedVariables` masyvą.
- Grąžinamas unusedVariables: Grąžinamas masyvas su nenaudojamų kintamųjų pavadinimais.
- Išvestis: Atspausdinami nenaudojami kintamieji į konsolę.
Šis pavyzdys demonstruoja paprastą linterį. Galite jį išplėsti, kad tikrintumėte kitus kodavimo standartus ir identifikuotumėte kitas galimas problemas jūsų kode. Pavyzdžiui, galėtumėte tikrinti nenaudojamus importus, per daug sudėtingas funkcijas ar galimus saugumo pažeidžiamumus. Svarbiausia yra suprasti, kaip peržvelgti AST ir identifikuoti konkrečius mazgų tipus, kurie jus domina.
Geriausios praktikos ir svarstymai
- Supraskite AST: Skirkite laiko suprasti AST struktūrą. Naudokite tokius įrankius kaip AST explorer, kad vizualizuotumėte savo kodo AST.
- Naudokite tipo apsaugas (Type Guards): Naudokite tipo apsaugas (`ts.isXXX`), kad užtikrintumėte, jog dirbate su teisingais mazgų tipais.
- Atsižvelkite į našumą: AST transformacijos gali būti skaičiavimo požiūriu brangios. Optimizuokite savo kodą, kad sumažintumėte lankomų ir transformuojamų mazgų skaičių.
- Tvarkykite klaidas: Tinkamai tvarkykite klaidas. Compiler API gali išmesti išimtis, jei bandysite atlikti netinkamas operacijas su AST.
- Kruopščiai testuokite: Kruopščiai testuokite savo transformacijas, kad įsitikintumėte, jog jos duoda norimus rezultatus ir neįveda naujų klaidų.
- Naudokite esamas bibliotekas: Apsvarstykite galimybę naudoti esamas bibliotekas, kurios suteikia aukštesnio lygio abstrakcijas virš Compiler API. Šios bibliotekos gali supaprastinti įprastas užduotis ir sumažinti rašomo kodo kiekį. Pavyzdžiai: `ts-morph` ir `typescript-eslint`.
Išvada
„TypeScript Compiler API“ yra galingas įrankis kuriant pažangius programavimo įrankius. Suprasdami, kaip dirbti su AST, galite kurti linterius, kodo formatuotojus, statinius analizatorius ir kitus įrankius, kurie gerina kodo kokybę, automatizuoja pasikartojančias užduotis ir didina programuotojų produktyvumą. Nors API gali būti sudėtinga, jos įvaldymo nauda yra didelė. Šis išsamus vadovas suteikia pagrindą efektyviam Compiler API tyrinėjimui ir naudojimui jūsų projektuose. Nepamirškite naudotis tokiais įrankiais kaip AST Explorer, atidžiai tvarkyti mazgų tipus ir kruopščiai testuoti savo transformacijas. Su praktika ir atsidavimu galėsite atskleisti visą „TypeScript Compiler API“ potencialą ir kurti inovatyvius sprendimus programinės įrangos kūrimo srityje.
Tolimesni tyrinėjimai:
- TypeScript Compiler API dokumentacija: [https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API](https://github.com/microsoft/TypeScript/wiki/Using-the-Compiler-API)
- AST Explorer: [https://astexplorer.net/](https://astexplorer.net/)
- ts-morph biblioteka: [https://ts-morph.com/](https://ts-morph.com/)
- typescript-eslint: [https://typescript-eslint.io/](https://typescript-eslint.io/)