TypeScript Compiler API bo'yicha keng qamrovli qo'llanma. Abstrakt Sintaksis Daraxtlari (AST), kod tahlili, transformatsiya va generatsiya xalqaro dasturchilar uchun.
TypeScript Compiler API: AST Manipulyatsiyasi va Kod Transformatsiyasini O'zlashtirish
TypeScript Compiler API TypeScript va JavaScript kodlarini tahlil qilish, manipulyatsiya qilish va generatsiya qilish uchun kuchli interfeysni taqdim etadi. Uning markazida manba kodingizning tuzilmaviy tasviri bo'lgan Abstrakt Sintaksis Daraxti (AST) yotadi. AST bilan ishlashni tushunish linterlar, kod formatlovchilar, statik analizatorlar va maxsus kod generatorlari kabi ilg'or vositalarni yaratish imkoniyatlarini ochadi.
TypeScript Compiler API nima?
TypeScript Compiler API — bu TypeScript kompilyatorining ichki ish jarayonlarini ochib beruvchi TypeScript interfeyslari va funksiyalari to'plami. U dasturchilarga kompilyatsiya jarayoni bilan dasturiy ravishda o'zaro ishlash imkonini beradi, bu shunchaki kodni kompilyatsiya qilishdan tashqariga chiqadi. Undan quyidagi maqsadlarda foydalanishingiz mumkin:
- Kodni tahlil qilish: Kod tuzilishini tekshirish, potentsial muammolarni aniqlash va semantik ma'lumotlarni olish.
- Kodni transformatsiya qilish: Mavjud kodni o'zgartirish, yangi funksiyalarni qo'shish yoki kodni avtomatik ravishda refaktoring qilish.
- Kod generatsiya qilish: Andozalar yoki boshqa kiritilgan ma'lumotlar asosida noldan yangi kod yaratish.
Ushbu API kod sifatini yaxshilaydigan, takrorlanuvchi vazifalarni avtomatlashtiradigan va dasturchi unumdorligini oshiradigan murakkab dasturlash vositalarini yaratish uchun zarurdir.
Abstrakt Sintaksis Daraxtini (AST) tushunish
AST bu kodingiz tuzilishining daraxtsimon tasviridir. Daraxtdagi har bir tugun o'zgaruvchini e'lon qilish, funksiyani chaqirish yoki boshqaruv operatori kabi sintaktik konstruksiyani ifodalaydi. TypeScript Compiler API AST bo'ylab harakatlanish, uning tugunlarini tekshirish va ularni o'zgartirish uchun vositalarni taqdim etadi.
Ushbu oddiy TypeScript kodini ko'rib chiqing:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("World"));
Ushbu kod uchun AST funksiya e'lonini, return operatorini, shablon literalini, console.log chaqiruvini va kodning boshqa elementlarini ifodalaydi. ASTni vizualizatsiya qilish qiyin bo'lishi mumkin, ammo AST explorer (astexplorer.net) kabi vositalar yordam beradi. Ushbu vositalar kodni kiritish va unga mos keladigan ASTni foydalanuvchiga qulay formatda ko'rish imkonini beradi. AST Explorer'dan foydalanish siz manipulyatsiya qiladigan kod tuzilmasi turini tushunishga yordam beradi.
Asosiy AST Tugun Turlari
TypeScript Compiler API turli xil sintaktik konstruksiyalarni ifodalovchi turli AST tugun turlarini belgilaydi. Mana ba'zi keng tarqalgan tugun turlari:
- SourceFile: Butun bir TypeScript faylini ifodalaydi.
- FunctionDeclaration: Funksiya ta'rifini ifodalaydi.
- VariableDeclaration: O'zgaruvchi e'lonini ifodalaydi.
- Identifier: Identifikatorni (masalan, o'zgaruvchi nomi, funksiya nomi) ifodalaydi.
- StringLiteral: Satr literalini ifodalaydi.
- CallExpression: Funksiya chaqiruvini ifodalaydi.
- ReturnStatement: return operatorini ifodalaydi.
Har bir tugun turi tegishli kod elementi haqida ma'lumot beruvchi xususiyatlarga ega. Masalan, `FunctionDeclaration` tuguni uning nomi, parametrlari, qaytariladigan qiymat turi va tanasi uchun xususiyatlarga ega bo'lishi mumkin.
Compiler API bilan ishlashni boshlash
Compiler API'dan foydalanishni boshlash uchun sizga TypeScript'ni o'rnatish va TypeScript sintaksisi bo'yicha asosiy tushunchaga ega bo'lish kerak. Mana bir TypeScript faylini o'qish va uning AST'sini chop etishni ko'rsatuvchi oddiy misol:
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);
Tushuntirish:
- Modullarni import qilish: Fayl tizimi operatsiyalari uchun `typescript` moduli va `fs` modulini import qiladi.
- Manba faylni o'qish: `example.ts` nomli TypeScript faylining tarkibini o'qiydi. Bu ishlashi uchun siz `example.ts` faylini yaratishingiz kerak bo'ladi.
- SourceFile yaratish: AST'ning ildizini ifodalovchi `SourceFile` ob'ektini yaratadi. `ts.createSourceFile` funksiyasi manba kodini tahlil qiladi va AST'ni generatsiya qiladi.
- AST'ni chop etish: AST bo'ylab harakatlanadigan va har bir tugunning turini chop etadigan rekursiv `printAST` funksiyasini belgilaydi.
- printAST'ni chaqirish: Ildiz `SourceFile` tugunidan boshlab AST'ni chop etishni boshlash uchun `printAST`ni chaqiradi.
Ushbu kodni ishga tushirish uchun uni `.ts` fayli sifatida saqlang (masalan, `ast-example.ts`), biror TypeScript kodi bilan `example.ts` faylini yarating, so'ngra kodni kompilyatsiya qiling va ishga tushiring:
tsc ast-example.ts
node ast-example.js
Bu sizning `example.ts` faylingizning AST'sini konsolga chiqaradi. Chiqishda tugunlar ierarxiyasi va ularning turlari ko'rsatiladi. Masalan, u `FunctionDeclaration`, `Identifier`, `Block` va boshqa tugun turlarini ko'rsatishi mumkin.
AST bo'ylab harakatlanish
Compiler API AST bo'ylab harakatlanishning bir necha usulini taqdim etadi. Eng oddiysi, avvalgi misolda ko'rsatilganidek, `forEachChild` usulidan foydalanishdir. Bu usul berilgan tugunning har bir bola tuguniga tashrif buyuradi.
Murakkabroq harakatlanish stsenariylari uchun `Visitor` naqshidan (pattern) foydalanishingiz mumkin. Visitor — bu ma'lum bir tugun turlari uchun chaqiriladigan metodlarni belgilaydigan ob'ekt. Bu sizga harakatlanish jarayonini sozlash va tugun turiga qarab amallarni bajarish imkonini beradi.
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);
Tushuntirish:
- IdentifierVisitor Klassi: `visit` metodiga ega `IdentifierVisitor` klassini belgilaydi.
- Visit Metodi: `visit` metodi joriy tugunning `Identifier` ekanligini tekshiradi. Agar shunday bo'lsa, u identifikatorning matnini chop etadi. Keyin u bola tugunlarga tashrif buyurish uchun `ts.forEachChild`ni rekursiv ravishda chaqiradi.
- Visitor yaratish: `IdentifierVisitor` nusxasini yaratadi.
- Harakatlanishni boshlash: Harakatlanishni boshlash uchun `SourceFile` ustida `visit` metodini chaqiradi.
Ushbu misol AST'dagi barcha identifikatorlarni qanday topishni ko'rsatadi. Siz ushbu naqshni boshqa tugun turlarini topish va turli xil amallarni bajarish uchun moslashtirishingiz mumkin.
AST'ni transformatsiya qilish
Compiler API'ning haqiqiy kuchi uning AST'ni o'zgartirish qobiliyatidadir. Kodingizning tuzilishi va xatti-harakatini o'zgartirish uchun AST'ni o'zgartirishingiz mumkin. Bu kodni refaktoring qilish vositalari, kod generatorlari va boshqa ilg'or vositalar uchun asosdir.
AST'ni transformatsiya qilish uchun siz `ts.transform` funksiyasidan foydalanishingiz kerak bo'ladi. Bu funksiya `SourceFile` va `TransformerFactory` funksiyalari ro'yxatini qabul qiladi. `TransformerFactory` bu `TransformationContext`ni qabul qiluvchi va `Transformer` funksiyasini qaytaruvchi funksiyadir. `Transformer` funksiyasi AST'dagi tugunlarga tashrif buyurish va ularni o'zgartirish uchun mas'uldir.
Mana bir TypeScript faylining boshiga izoh qo'shishni ko'rsatuvchi oddiy misol:
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);
Tushuntirish:
- TransformerFactory: `Transformer` funksiyasini qaytaradigan `TransformerFactory` funksiyasini belgilaydi.
- Transformer: `Transformer` funksiyasi joriy tugunning `SourceFile` ekanligini tekshiradi. Agar shunday bo'lsa, u `ts.addSyntheticLeadingComment` yordamida tugunga boshlang'ich izoh qo'shadi.
- ts.transform: Transformatsiyani `SourceFile`ga qo'llash uchun `ts.transform`ni chaqiradi.
- Printer: O'zgartirilgan AST'dan kod generatsiya qilish uchun `Printer` ob'ektini yaratadi.
- Chop etish va yozish: O'zgartirilgan kodni chop etadi va uni `example.transformed.ts` nomli yangi faylga yozadi.
Ushbu misol oddiy transformatsiyani ko'rsatadi, ammo siz xuddi shu naqshdan kodni refaktoring qilish, log yozuvlarini qo'shish yoki hujjatlarni generatsiya qilish kabi murakkabroq transformatsiyalarni bajarish uchun foydalanishingiz mumkin.
Ilg'or Transformatsiya Texnikalari
Compiler API bilan ishlatishingiz mumkin bo'lgan ba'zi ilg'or transformatsiya texnikalari:
- Yangi tugunlar yaratish: Yangi AST tugunlarini yaratish uchun `ts.createXXX` funksiyalaridan foydalaning. Masalan, `ts.createVariableDeclaration` yangi o'zgaruvchi e'lon qilish tugunini yaratadi.
- Tugunlarni almashtirish: `ts.visitEachChild` funksiyasidan foydalanib, mavjud tugunlarni yangilari bilan almashtiring.
- Tugunlarni qo'shish: `ts.updateXXX` funksiyalaridan foydalanib, AST'ga yangi tugunlar qo'shing. Masalan, `ts.updateBlock` blok operatorini yangi operatorlar bilan yangilaydi.
- Tugunlarni o'chirish: Transformer funksiyasidan `undefined` qaytarish orqali AST'dan tugunlarni olib tashlang.
Kod Generatsiyasi
AST'ni transformatsiya qilgandan so'ng, undan kod generatsiya qilishingiz kerak bo'ladi. Compiler API bu maqsad uchun `Printer` ob'ektini taqdim etadi. `Printer` AST'ni qabul qiladi va kodning satrli ko'rinishini generatsiya qiladi.
`ts.createPrinter` funksiyasi `Printer` ob'ektini yaratadi. Siz printerni turli xil variantlar bilan sozlashingiz mumkin, masalan, ishlatiladigan yangi qator belgisi va izohlarni chiqarish yoki chiqarmaslik.
`printer.printFile` metodi `SourceFile`ni qabul qiladi va kodning satrli ko'rinishini qaytaradi. Keyin siz bu satrni faylga yozishingiz mumkin.
Compiler API'ning Amaliy Qo'llanilishi
TypeScript Compiler API dasturiy ta'minotni ishlab chiqishda ko'plab amaliy qo'llanilishlarga ega. Mana bir nechta misollar:
- Linterlar: Kodlash standartlarini joriy qilish va kodingizdagi potentsial muammolarni aniqlash uchun maxsus linterlar yarating.
- Kod formatlovchilar: Kodingizni ma'lum bir uslub qo'llanmasiga muvofiq avtomatik formatlash uchun kod formatlovchilarni yarating.
- Statik analizatorlar: Kodingizdagi xatolar, xavfsizlik zaifliklari va unumdorlik muammolarini aniqlash uchun statik analizatorlarni ishlab chiqing.
- Kod generatorlari: Andozalar yoki boshqa kiritilgan ma'lumotlardan kod generatsiya qiling, takrorlanuvchi vazifalarni avtomatlashtiring va ortiqcha kodni kamaytiring. Masalan, tavsif faylidan API klientlari yoki ma'lumotlar bazasi sxemalarini generatsiya qilish.
- Refaktoring vositalari: O'zgaruvchilarni avtomatik ravishda qayta nomlash, funksiyalarni ajratib olish yoki kodni fayllar o'rtasida ko'chirish uchun refaktoring vositalarini yarating.
- Xalqarolashtirish (i18n) avtomatizatsiyasi: TypeScript kodingizdan tarjima qilinadigan satrlarni avtomatik ravishda ajratib oling va turli tillar uchun mahalliylashtirish fayllarini generatsiya qiling. Masalan, bir vosita `translate()` funksiyasiga uzatilgan satrlar uchun kodni skanerlashi va ularni avtomatik ravishda tarjima resurs fayliga qo'shishi mumkin.
Misol: Oddiy Linter Yaratish
Keling, TypeScript kodida ishlatilmaydigan o'zgaruvchilarni tekshiradigan oddiy linter yaratamiz. Ushbu linter e'lon qilingan, ammo hech qachon ishlatilmagan o'zgaruvchilarni aniqlaydi.
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.");
}
Tushuntirish:
- findUnusedVariables funksiyasi: Kirish sifatida `SourceFile`ni qabul qiladigan `findUnusedVariables` funksiyasini belgilaydi.
- usedVariables Set'i: Ishlatilgan o'zgaruvchilarning nomlarini saqlash uchun `Set` yaratadi.
- visit funksiyasi: AST bo'ylab harakatlanadigan va barcha identifikatorlarning nomlarini `usedVariables` to'plamiga qo'shadigan rekursiv `visit` funksiyasini belgilaydi.
- checkVariableDeclaration funksiyasi: O'zgaruvchi e'lonining ishlatilmaganligini tekshiradigan rekursiv `checkVariableDeclaration` funksiyasini belgilaydi. Agar shunday bo'lsa, u o'zgaruvchi nomini `unusedVariables` massiviga qo'shadi.
- unusedVariables'ni qaytarish: Har qanday ishlatilmagan o'zgaruvchilarning nomlarini o'z ichiga olgan massivni qaytaradi.
- Natija: Ishlatilmagan o'zgaruvchilarni konsolga chiqaradi.
Ushbu misol oddiy linterni ko'rsatadi. Siz uni boshqa kodlash standartlarini tekshirish va kodingizdagi boshqa potentsial muammolarni aniqlash uchun kengaytirishingiz mumkin. Masalan, siz ishlatilmagan importlar, haddan tashqari murakkab funksiyalar yoki potentsial xavfsizlik zaifliklarini tekshirishingiz mumkin. Asosiy narsa - AST bo'ylab qanday harakatlanishni va sizni qiziqtirgan ma'lum tugun turlarini qanday aniqlashni tushunishdir.
Eng Yaxshi Amaliyotlar va Mulohazalar
- AST'ni tushunish: AST tuzilishini tushunishga vaqt ajrating. Kodingizning AST'sini vizualizatsiya qilish uchun AST explorer kabi vositalardan foydalaning.
- Tur qo'riqchilaridan foydalanish: To'g'ri tugun turlari bilan ishlayotganingizga ishonch hosil qilish uchun tur qo'riqchilaridan (`ts.isXXX`) foydalaning.
- Unumdorlikni hisobga olish: AST transformatsiyalari hisoblash jihatidan qimmat bo'lishi mumkin. Siz tashrif buyuradigan va o'zgartiradigan tugunlar sonini minimallashtirish uchun kodingizni optimallashtiring.
- Xatolarni boshqarish: Xatolarni ehtiyotkorlik bilan boshqaring. Agar siz AST'da noto'g'ri amallarni bajarishga urinsangiz, Compiler API istisno chiqarishi mumkin.
- Puxta sinovdan o'tkazish: Transformatsiyalaringiz kerakli natijalarni berishiga va yangi xatoliklarni keltirib chiqarmasligiga ishonch hosil qilish uchun ularni puxta sinovdan o'tkazing.
- Mavjud kutubxonalardan foydalanish: Compiler API ustida yuqori darajadagi abstraktsiyalarni taqdim etadigan mavjud kutubxonalardan foydalanishni ko'rib chiqing. Ushbu kutubxonalar umumiy vazifalarni soddalashtirishi va siz yozishingiz kerak bo'lgan kod miqdorini kamaytirishi mumkin. Misollar: `ts-morph` va `typescript-eslint`.
Xulosa
TypeScript Compiler API ilg'or dasturlash vositalarini yaratish uchun kuchli vositadir. AST bilan qanday ishlashni tushunib, siz kod sifatini yaxshilaydigan, takrorlanuvchi vazifalarni avtomatlashtiradigan va dasturchi unumdorligini oshiradigan linterlar, kod formatlovchilar, statik analizatorlar va boshqa vositalarni yaratishingiz mumkin. API murakkab bo'lishi mumkin bo'lsa-da, uni o'zlashtirishning afzalliklari juda katta. Ushbu keng qamrovli qo'llanma loyihalaringizda Compiler API'ni samarali o'rganish va undan foydalanish uchun asos yaratadi. AST Explorer kabi vositalardan foydalanishni, tugun turlarini ehtiyotkorlik bilan boshqarishni va transformatsiyalaringizni puxta sinovdan o'tkazishni unutmang. Amaliyot va g'ayrat bilan siz TypeScript Compiler API'ning to'liq salohiyatini ochishingiz va dasturiy ta'minotni ishlab chiqish landshafti uchun innovatsion yechimlarni yaratishingiz mumkin.
Qo'shimcha o'rganish uchun:
- TypeScript Compiler API Hujjatlari: [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 kutubxonasi: [https://ts-morph.com/](https://ts-morph.com/)
- typescript-eslint: [https://typescript-eslint.io/](https://typescript-eslint.io/)