Celovit vodnik po TypeScript Compiler API, ki zajema abstraktna sintaktična drevesa (AST), analizo kode, preoblikovanje in generiranje za mednarodne razvijalce.
TypeScript Compiler API: Obvladovanje manipulacije AST in preoblikovanja kode
TypeScript Compiler API ponuja zmogljiv vmesnik za analiziranje, manipuliranje in generiranje kode TypeScript in JavaScript. V svojem jedru ima abstraktno sintaktično drevo (AST), strukturirano predstavitev vaše izvorne kode. Razumevanje dela z AST odpira možnosti za ustvarjanje naprednih orodij, kot so linterji, oblikovalci kode, statični analizatorji in generatorji kode po meri.
Kaj je TypeScript Compiler API?
TypeScript Compiler API je nabor vmesnikov in funkcij TypeScript, ki razkrivajo notranje delovanje prevajalnika TypeScript. Razvijalcem omogoča programsko interakcijo s postopkom prevajanja, ki presega zgolj prevajanje kode. Uporabite ga lahko za:
- Analizo kode: Pregled strukture kode, prepoznavanje morebitnih težav in pridobivanje semantičnih informacij.
- Preoblikovanje kode: Spreminjanje obstoječe kode, dodajanje novih funkcij ali samodejno refaktoriranje kode.
- Generiranje kode: Ustvarjanje nove kode iz nič na podlagi predlog ali drugih vhodnih podatkov.
Ta API je bistvenega pomena za ustvarjanje sofisticiranih razvojnih orodij, ki izboljšujejo kakovost kode, avtomatizirajo ponavljajoče se naloge in povečujejo produktivnost razvijalcev.
Razumevanje abstraktnega sintaktičnega drevesa (AST)
AST je drevesna predstavitev strukture vaše kode. Vsako vozlišče v drevesu predstavlja sintaktični konstrukt, kot je deklaracija spremenljivke, klic funkcije ali stavek nadzora poteka. TypeScript Compiler API ponuja orodja za prečkanje AST, pregledovanje njegovih vozlišč in njihovo spreminjanje.
Razmislite o tej preprosti kodi TypeScript:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("World"));
AST za to kodo bi predstavljal deklaracijo funkcije, stavek return, predlogo literal, klic console.log in druge elemente kode. Vizualizacija AST je lahko zahtevna, vendar lahko pomagajo orodja, kot je AST explorer (astexplorer.net). Ta orodja vam omogočajo, da vnesete kodo in si ogledate ustrezni AST v uporabniku prijazni obliki. Uporaba AST Explorer vam bo pomagala razumeti, kakšno strukturo kode boste manipulirali.
Ključne vrste vozlišč AST
TypeScript Compiler API definira različne vrste vozlišč AST, od katerih vsaka predstavlja drugačen sintaktični konstrukt. Tukaj je nekaj pogostih vrst vozlišč:
- SourceFile: Predstavlja celotno datoteko TypeScript.
- FunctionDeclaration: Predstavlja definicijo funkcije.
- VariableDeclaration: Predstavlja deklaracijo spremenljivke.
- Identifier: Predstavlja identifikator (npr. ime spremenljivke, ime funkcije).
- StringLiteral: Predstavlja nizovni literal.
- CallExpression: Predstavlja klic funkcije.
- ReturnStatement: Predstavlja stavek return.
Vsaka vrsta vozlišča ima lastnosti, ki zagotavljajo informacije o ustreznem elementu kode. Na primer, vozlišče `FunctionDeclaration` ima lahko lastnosti za njegovo ime, parametre, tip povratne vrednosti in telo.
Začetek z Compiler API
Če želite začeti uporabljati Compiler API, morate namestiti TypeScript in imeti osnovno razumevanje sintakse TypeScript. Tukaj je preprost primer, ki prikazuje, kako prebrati datoteko TypeScript in natisniti njen 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, // Target ECMAScript version
true // SetParentNodes: true to retain parent references in the AST
);
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);
Pojasnilo:
- Uvoz modulov: Uvozi modul `typescript` in modul `fs` za operacije datotečnega sistema.
- Branje izvorne datoteke: Prebere vsebino datoteke TypeScript z imenom `example.ts`. Za pravilno delovanje morate ustvariti datoteko `example.ts`.
- Ustvarjanje SourceFile: Ustvari objekt `SourceFile`, ki predstavlja koren AST. Funkcija `ts.createSourceFile` razčleni izvorno kodo in ustvari AST.
- Tiskanje AST: Definira rekurzivno funkcijo `printAST`, ki prečka AST in natisne vrsto vsakega vozlišča.
- Klic printAST: Pokliče `printAST`, da začne tiskanje AST iz korenskega vozlišča `SourceFile`.
Če želite zagnati to kodo, jo shranite kot datoteko `.ts` (npr. `ast-example.ts`), ustvarite datoteko `example.ts` z nekaj kodo TypeScript, nato pa prevedite in zaženite kodo:
tsc ast-example.ts
node ast-example.js
To bo natisnilo AST vaše datoteke `example.ts` v konzolo. Izhod bo prikazal hierarhijo vozlišč in njihove vrste. Na primer, lahko prikaže `FunctionDeclaration`, `Identifier`, `Block` in druge vrste vozlišč.
Prečkanje AST
Compiler API ponuja več načinov za prečkanje AST. Najenostavnejši je uporaba metode `forEachChild`, kot je prikazano v prejšnjem primeru. Ta metoda obišče vsako podrejeno vozlišče danega vozlišča.
Za bolj zapletene scenarije prečkanja lahko uporabite vzorec `Visitor`. Obiskovalec je objekt, ki definira metode, ki se kličejo za določene vrste vozlišč. To vam omogoča, da prilagodite postopek prečkanja in izvajate dejanja na podlagi vrste vozlišča.
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(`Found identifier: ${node.text}`);
}
ts.forEachChild(node, n => this.visit(n));
}
}
const visitor = new IdentifierVisitor();
visitor.visit(sourceFile);
Pojasnilo:
- Razred IdentifierVisitor: Definira razred `IdentifierVisitor` z metodo `visit`.
- Metoda Visit: Metoda `visit` preveri, ali je trenutno vozlišče `Identifier`. Če je, natisne besedilo identifikatorja. Nato rekurzivno pokliče `ts.forEachChild`, da obišče podrejena vozlišča.
- Ustvarjanje obiskovalca: Ustvari primerek `IdentifierVisitor`.
- Začetek prečkanja: Pokliče metodo `visit` na `SourceFile`, da začne prečkanje.
Ta primer prikazuje, kako najti vse identifikatorje v AST. Ta vzorec lahko prilagodite za iskanje drugih vrst vozlišč in izvajanje različnih dejanj.
Preoblikovanje AST
Prava moč Compiler API je v njegovi sposobnosti preoblikovanja AST. Spremenite lahko AST, da spremenite strukturo in vedenje vaše kode. To je osnova za orodja za refaktoriranje kode, generatorje kode in druga napredna orodja.
Če želite preoblikovati AST, boste morali uporabiti funkcijo `ts.transform`. Ta funkcija sprejme `SourceFile` in seznam funkcij `TransformerFactory`. `TransformerFactory` je funkcija, ki sprejme `TransformationContext` in vrne funkcijo `Transformer`. Funkcija `Transformer` je odgovorna za obiskovanje in preoblikovanje vozlišč v AST.
Tukaj je preprost primer, ki prikazuje, kako dodati komentar na začetek datoteke TypeScript:
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)) {
// Create a leading comment
const comment = ts.addSyntheticLeadingComment(
node,
ts.SyntaxKind.MultiLineCommentTrivia,
" This file was automatically transformed ",
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);
Pojasnilo:
- TransformerFactory: Definira funkcijo `TransformerFactory`, ki vrne funkcijo `Transformer`.
- Transformer: Funkcija `Transformer` preveri, ali je trenutno vozlišče `SourceFile`. Če je, doda vodilni komentar vozlišču z uporabo `ts.addSyntheticLeadingComment`.
- ts.transform: Pokliče `ts.transform`, da uporabi transformacijo na `SourceFile`.
- Printer: Ustvari objekt `Printer` za generiranje kode iz preoblikovanega AST.
- Tiskanje in pisanje: Natisne preoblikovano kodo in jo zapiše v novo datoteko z imenom `example.transformed.ts`.
Ta primer prikazuje preprosto transformacijo, vendar lahko uporabite isti vzorec za izvajanje bolj zapletenih transformacij, kot so refaktoriranje kode, dodajanje izjav za beleženje ali ustvarjanje dokumentacije.
Napredne tehnike transformacije
Tukaj je nekaj naprednih tehnik transformacije, ki jih lahko uporabite z Compiler API:
- Ustvarjanje novih vozlišč: Uporabite funkcije `ts.createXXX` za ustvarjanje novih vozlišč AST. Na primer, `ts.createVariableDeclaration` ustvari novo vozlišče deklaracije spremenljivke.
- Zamenjava vozlišč: Zamenjajte obstoječa vozlišča z novimi vozlišči z uporabo funkcije `ts.visitEachChild`.
- Dodajanje vozlišč: Dodajte nova vozlišča v AST z uporabo funkcij `ts.updateXXX`. Na primer, `ts.updateBlock` posodobi blok stavek z novimi stavki.
- Odstranjevanje vozlišč: Odstranite vozlišča iz AST tako, da iz funkcije transformatorja vrnete `undefined`.
Generiranje kode
Po preoblikovanju AST morate iz njega ustvariti kodo. Compiler API ponuja objekt `Printer` za ta namen. `Printer` vzame AST in ustvari nizovno predstavitev kode.
Funkcija `ts.createPrinter` ustvari objekt `Printer`. Tiskalnik lahko konfigurirate z različnimi možnostmi, kot so znak za novo vrstico in ali želite oddati komentarje.
Metoda `printer.printFile` vzame `SourceFile` in vrne nizovno predstavitev kode. Ta niz lahko nato zapišete v datoteko.
Praktične aplikacije Compiler API
TypeScript Compiler API ima številne praktične aplikacije v razvoju programske opreme. Tukaj je nekaj primerov:
- Linterji: Izdelajte linterje po meri za uveljavljanje standardov kodiranja in prepoznavanje morebitnih težav v vaši kodi.
- Oblikovalci kode: Ustvarite oblikovalce kode za samodejno oblikovanje kode v skladu z določenim slogovnim vodnikom.
- Statični analizatorji: Razvijte statične analizatorje za odkrivanje napak, varnostnih ranljivosti in ozkih grl učinkovitosti v vaši kodi.
- Generatorji kode: Ustvarite kodo iz predlog ali drugih vhodnih podatkov, avtomatizirajte ponavljajoče se naloge in zmanjšajte izvorno kodo. Na primer, ustvarjanje API odjemalcev ali shem baze podatkov iz opisne datoteke.
- Orodja za refaktoriranje: Izdelajte orodja za refaktoriranje za samodejno preimenovanje spremenljivk, ekstrahiranje funkcij ali premikanje kode med datotekami.
- Avtomatizacija internacionalizacije (i18n): Samodejno izvlecite nize, ki jih je mogoče prevesti, iz vaše kode TypeScript in ustvarite lokalizacijske datoteke za različne jezike. Na primer, orodje bi lahko pregledalo kodo za nize, posredovane funkciji `translate()`, in jih samodejno dodalo v datoteko z viri prevodov.
Primer: Izdelava preprostega linterja
Ustvarimo preprost linter, ki preverja neuporabljene spremenljivke v kodi TypeScript. Ta linter bo prepoznal spremenljivke, ki so deklarirane, vendar nikoli uporabljene.
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("Unused variables:");
unusedVariables.forEach(variable => console.log(`- ${variable}`));
} else {
console.log("No unused variables found.");
}
Pojasnilo:
- Funkcija findUnusedVariables: Definira funkcijo `findUnusedVariables`, ki kot vhod vzame `SourceFile`.
- Množica usedVariables: Ustvari `Set` za shranjevanje imen uporabljenih spremenljivk.
- Funkcija visit: Definira rekurzivno funkcijo `visit`, ki prečka AST in doda imena vseh identifikatorjev v množico `usedVariables`.
- Funkcija checkVariableDeclaration: Definira rekurzivno funkcijo `checkVariableDeclaration`, ki preveri, ali je deklaracija spremenljivke neuporabljena. Če je, doda ime spremenljivke v niz `unusedVariables`.
- Vrne unusedVariables: Vrne niz, ki vsebuje imena vseh neuporabljenih spremenljivk.
- Izhod: Natisne neuporabljene spremenljivke v konzolo.
Ta primer prikazuje preprost linter. Razširite ga lahko, da preveri druge standarde kodiranja in prepozna druge morebitne težave v vaši kodi. Na primer, lahko preverite neuporabljene uvoze, preveč zapletene funkcije ali morebitne varnostne ranljivosti. Ključno je, da razumete, kako prečkati AST in prepoznati določene vrste vozlišč, ki vas zanimajo.
Najboljše prakse in premisleki
- Razumevanje AST: Vložite čas v razumevanje strukture AST. Uporabite orodja, kot je AST explorer, za vizualizacijo AST vaše kode.
- Uporaba varoval za tipe: Uporabite varovala za tipe (`ts.isXXX`), da zagotovite, da delate s pravilnimi vrstami vozlišč.
- Upoštevajte učinkovitost delovanja: Transformacije AST so lahko računsko zahtevne. Optimizirajte svojo kodo, da zmanjšate število vozlišč, ki jih obiščete in preoblikujete.
- Obravnavanje napak: Obravnavajte napake elegantno. Compiler API lahko vrže izjeme, če poskušate izvesti neveljavne operacije na AST.
- Temeljito testiranje: Temeljito preizkusite svoje transformacije, da zagotovite, da prinesejo želene rezultate in ne povzročijo novih napak.
- Uporaba obstoječih knjižnic: Razmislite o uporabi obstoječih knjižnic, ki ponujajo abstrakcije višje ravni nad Compiler API. Te knjižnice lahko poenostavijo pogoste naloge in zmanjšajo količino kode, ki jo morate napisati. Primeri vključujejo `ts-morph` in `typescript-eslint`.
Zaključek
TypeScript Compiler API je zmogljivo orodje za ustvarjanje naprednih razvojnih orodij. Z razumevanjem dela z AST lahko ustvarite linterje, oblikovalce kode, statične analizatorje in druga orodja, ki izboljšujejo kakovost kode, avtomatizirajo ponavljajoče se naloge in povečujejo produktivnost razvijalcev. Čeprav je API lahko zapleten, so koristi obvladovanja precejšnje. Ta celovit vodnik ponuja temelj za učinkovito raziskovanje in uporabo Compiler API v vaših projektih. Ne pozabite uporabljati orodij, kot je AST Explorer, skrbno ravnati z vrstami vozlišč in temeljito preizkusiti svoje transformacije. S prakso in predanostjo lahko odklenete ves potencial TypeScript Compiler API in ustvarite inovativne rešitve za področje razvoja programske opreme.
Nadaljnje raziskovanje:
- Dokumentacija TypeScript Compiler API: [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/)
- Knjižnica ts-morph: [https://ts-morph.com/](https://ts-morph.com/)
- typescript-eslint: [https://typescript-eslint.io/](https://typescript-eslint.io/)