Panduan komprehensif tentang API Kompilator TypeScript, mencakup Abstract Syntax Tree (AST), analisis, transformasi, dan pembuatan kode untuk pengembang internasional.
API Kompilator TypeScript: Menguasai Manipulasi AST dan Transformasi Kode
API Kompilator TypeScript menyediakan antarmuka yang kuat untuk menganalisis, memanipulasi, dan menghasilkan kode TypeScript dan JavaScript. Intinya terletak pada Abstract Syntax Tree (AST), sebuah representasi terstruktur dari kode sumber Anda. Memahami cara bekerja dengan AST membuka kemampuan untuk membangun perkakas canggih, seperti linter, pemformat kode, penganalisis statis, dan pembuat kode kustom.
Apa itu API Kompilator TypeScript?
API Kompilator TypeScript adalah sekumpulan antarmuka dan fungsi TypeScript yang mengekspos cara kerja internal kompilator TypeScript. Ini memungkinkan pengembang untuk berinteraksi secara terprogram dengan proses kompilasi, lebih dari sekadar mengompilasi kode. Anda dapat menggunakannya untuk:
- Menganalisis Kode: Memeriksa struktur kode, mengidentifikasi potensi masalah, dan mengekstrak informasi semantik.
- Mengubah Kode: Memodifikasi kode yang ada, menambahkan fitur baru, atau melakukan refaktor kode secara otomatis.
- Menghasilkan Kode: Membuat kode baru dari awal berdasarkan templat atau input lainnya.
API ini penting untuk membangun alat pengembangan canggih yang meningkatkan kualitas kode, mengotomatiskan tugas berulang, dan meningkatkan produktivitas pengembang.
Memahami Abstract Syntax Tree (AST)
AST adalah representasi seperti pohon dari struktur kode Anda. Setiap simpul (node) di pohon mewakili konstruksi sintaksis, seperti deklarasi variabel, pemanggilan fungsi, atau pernyataan alur kontrol. API Kompilator TypeScript menyediakan alat untuk menelusuri AST, memeriksa simpul-simpulnya, dan memodifikasinya.
Perhatikan kode TypeScript sederhana ini:
function greet(name: string): string {
return `Hello, ${name}!`;
}
console.log(greet("World"));
AST untuk kode ini akan merepresentasikan deklarasi fungsi, pernyataan return, literal templat, pemanggilan console.log, dan elemen kode lainnya. Memvisualisasikan AST bisa jadi menantang, tetapi alat seperti AST explorer (astexplorer.net) dapat membantu. Alat-alat ini memungkinkan Anda memasukkan kode dan melihat AST yang sesuai dalam format yang mudah digunakan. Menggunakan AST Explorer akan membantu Anda memahami jenis struktur kode yang akan Anda manipulasi.
Jenis Simpul AST Utama
API Kompilator TypeScript mendefinisikan berbagai jenis simpul AST, masing-masing mewakili konstruksi sintaksis yang berbeda. Berikut adalah beberapa jenis simpul yang umum:
- SourceFile: Merepresentasikan seluruh file TypeScript.
- FunctionDeclaration: Merepresentasikan definisi fungsi.
- VariableDeclaration: Merepresentasikan deklarasi variabel.
- Identifier: Merepresentasikan pengidentifikasi (mis., nama variabel, nama fungsi).
- StringLiteral: Merepresentasikan literal string.
- CallExpression: Merepresentasikan pemanggilan fungsi.
- ReturnStatement: Merepresentasikan pernyataan return.
Setiap jenis simpul memiliki properti yang memberikan informasi tentang elemen kode yang sesuai. Misalnya, simpul `FunctionDeclaration` mungkin memiliki properti untuk nama, parameter, tipe kembalian, dan badannya.
Memulai dengan API Kompilator
Untuk mulai menggunakan API Kompilator, Anda perlu menginstal TypeScript dan memiliki pemahaman dasar tentang sintaksis TypeScript. Berikut adalah contoh sederhana yang menunjukkan cara membaca file TypeScript dan mencetak AST-nya:
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 versi ECMAScript
true // SetParentNodes: true untuk mempertahankan referensi induk di 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);
Penjelasan:
- Impor Modul: Mengimpor modul `typescript` dan modul `fs` untuk operasi sistem file.
- Baca File Sumber: Membaca konten file TypeScript bernama `example.ts`. Anda perlu membuat file `example.ts` agar ini berfungsi.
- Buat SourceFile: Membuat objek `SourceFile`, yang merepresentasikan akar dari AST. Fungsi `ts.createSourceFile` mengurai kode sumber dan menghasilkan AST.
- Cetak AST: Mendefinisikan fungsi rekursif `printAST` yang menelusuri AST dan mencetak jenis dari setiap simpul.
- Panggil printAST: Memanggil `printAST` untuk mulai mencetak AST dari simpul `SourceFile` akar.
Untuk menjalankan kode ini, simpan sebagai file `.ts` (mis., `ast-example.ts`), buat file `example.ts` dengan beberapa kode TypeScript, lalu kompilasi dan jalankan kodenya:
tsc ast-example.ts
node ast-example.js
Ini akan mencetak AST dari file `example.ts` Anda ke konsol. Outputnya akan menunjukkan hierarki simpul dan jenisnya. Misalnya, ini mungkin menunjukkan `FunctionDeclaration`, `Identifier`, `Block`, dan jenis simpul lainnya.
Menelusuri AST
API Kompilator menyediakan beberapa cara untuk menelusuri AST. Yang paling sederhana adalah menggunakan metode `forEachChild`, seperti yang ditunjukkan pada contoh sebelumnya. Metode ini mengunjungi setiap simpul anak dari simpul yang diberikan.
Untuk skenario penelusuran yang lebih kompleks, Anda dapat menggunakan pola `Visitor`. Visitor adalah objek yang mendefinisikan metode untuk dipanggil untuk jenis simpul tertentu. Ini memungkinkan Anda untuk menyesuaikan proses penelusuran dan melakukan tindakan berdasarkan jenis simpul.
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);
Penjelasan:
- Kelas IdentifierVisitor: Mendefinisikan kelas `IdentifierVisitor` dengan metode `visit`.
- Metode Visit: Metode `visit` memeriksa apakah simpul saat ini adalah `Identifier`. Jika ya, ia mencetak teks pengidentifikasi. Kemudian secara rekursif memanggil `ts.forEachChild` untuk mengunjungi simpul anak.
- Buat Visitor: Membuat instance dari `IdentifierVisitor`.
- Mulai Penelusuran: Memanggil metode `visit` pada `SourceFile` untuk memulai penelusuran.
Contoh ini menunjukkan cara menemukan semua pengidentifikasi di AST. Anda dapat mengadaptasi pola ini untuk menemukan jenis simpul lain dan melakukan tindakan yang berbeda.
Mengubah AST
Kekuatan sebenarnya dari API Kompilator terletak pada kemampuannya untuk mengubah AST. Anda dapat memodifikasi AST untuk mengubah struktur dan perilaku kode Anda. Ini adalah dasar untuk alat refaktor kode, pembuat kode, dan perkakas canggih lainnya.
Untuk mengubah AST, Anda perlu menggunakan fungsi `ts.transform`. Fungsi ini menerima `SourceFile` dan daftar fungsi `TransformerFactory`. `TransformerFactory` adalah fungsi yang menerima `TransformationContext` dan mengembalikan fungsi `Transformer`. Fungsi `Transformer` bertanggung jawab untuk mengunjungi dan mengubah simpul di AST.
Berikut adalah contoh sederhana yang menunjukkan cara menambahkan komentar di awal file 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)) {
// Buat komentar pembuka
const comment = ts.addSyntheticLeadingComment(
node,
ts.SyntaxKind.MultiLineCommentTrivia,
" File ini diubah secara otomatis ",
true // memiliki baris baru di akhir
);
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);
Penjelasan:
- TransformerFactory: Mendefinisikan fungsi `TransformerFactory` yang mengembalikan fungsi `Transformer`.
- Transformer: Fungsi `Transformer` memeriksa apakah simpul saat ini adalah `SourceFile`. Jika ya, ia menambahkan komentar pembuka ke simpul menggunakan `ts.addSyntheticLeadingComment`.
- ts.transform: Memanggil `ts.transform` untuk menerapkan transformasi ke `SourceFile`.
- Printer: Membuat objek `Printer` untuk menghasilkan kode dari AST yang telah diubah.
- Cetak dan Tulis: Mencetak kode yang telah diubah dan menuliskannya ke file baru bernama `example.transformed.ts`.
Contoh ini menunjukkan transformasi sederhana, tetapi Anda dapat menggunakan pola yang sama untuk melakukan transformasi yang lebih kompleks, seperti melakukan refaktor kode, menambahkan pernyataan logging, atau menghasilkan dokumentasi.
Teknik Transformasi Lanjutan
Berikut adalah beberapa teknik transformasi lanjutan yang dapat Anda gunakan dengan API Kompilator:
- Membuat Simpul Baru: Gunakan fungsi `ts.createXXX` untuk membuat simpul AST baru. Misalnya, `ts.createVariableDeclaration` membuat simpul deklarasi variabel baru.
- Mengganti Simpul: Ganti simpul yang ada dengan simpul baru menggunakan fungsi `ts.visitEachChild`.
- Menambahkan Simpul: Tambahkan simpul baru ke AST menggunakan fungsi `ts.updateXXX`. Misalnya, `ts.updateBlock` memperbarui pernyataan blok dengan pernyataan baru.
- Menghapus Simpul: Hapus simpul dari AST dengan mengembalikan `undefined` dari fungsi transformer.
Pembuatan Kode
Setelah mengubah AST, Anda perlu menghasilkan kode darinya. API Kompilator menyediakan objek `Printer` untuk tujuan ini. `Printer` menerima AST dan menghasilkan representasi string dari kode tersebut.
Fungsi `ts.createPrinter` membuat objek `Printer`. Anda dapat mengonfigurasi printer dengan berbagai opsi, seperti karakter baris baru yang akan digunakan dan apakah akan menyertakan komentar.
Metode `printer.printFile` menerima `SourceFile` dan mengembalikan representasi string dari kode. Anda kemudian dapat menulis string ini ke file.
Aplikasi Praktis dari API Kompilator
API Kompilator TypeScript memiliki banyak aplikasi praktis dalam pengembangan perangkat lunak. Berikut beberapa contohnya:
- Linter: Membangun linter kustom untuk menegakkan standar pengkodean dan mengidentifikasi potensi masalah dalam kode Anda.
- Pemformat Kode: Membuat pemformat kode untuk memformat kode Anda secara otomatis sesuai dengan panduan gaya tertentu.
- Penganalisis Statis: Mengembangkan penganalisis statis untuk mendeteksi bug, kerentanan keamanan, dan hambatan kinerja dalam kode Anda.
- Pembuat Kode: Menghasilkan kode dari templat atau input lain, mengotomatiskan tugas berulang dan mengurangi kode boilerplate. Misalnya, menghasilkan klien API atau skema basis data dari file deskripsi.
- Alat Refactoring: Membangun alat refactoring untuk secara otomatis mengganti nama variabel, mengekstrak fungsi, atau memindahkan kode antar file.
- Otomatisasi Internasionalisasi (i18n): Secara otomatis mengekstrak string yang dapat diterjemahkan dari kode TypeScript Anda dan menghasilkan file lokalisasi untuk berbagai bahasa. Misalnya, sebuah alat dapat memindai kode untuk string yang dilewatkan ke fungsi `translate()` dan secara otomatis menambahkannya ke file sumber daya terjemahan.
Contoh: Membangun Linter Sederhana
Mari kita buat linter sederhana yang memeriksa variabel yang tidak digunakan dalam kode TypeScript. Linter ini akan mengidentifikasi variabel yang dideklarasikan tetapi tidak pernah digunakan.
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.");
}
Penjelasan:
- Fungsi findUnusedVariables: Mendefinisikan fungsi `findUnusedVariables` yang menerima `SourceFile` sebagai input.
- Set usedVariables: Membuat `Set` untuk menyimpan nama-nama variabel yang digunakan.
- Fungsi visit: Mendefinisikan fungsi rekursif `visit` yang menelusuri AST dan menambahkan nama semua pengidentifikasi ke set `usedVariables`.
- Fungsi checkVariableDeclaration: Mendefinisikan fungsi rekursif `checkVariableDeclaration` yang memeriksa apakah deklarasi variabel tidak digunakan. Jika ya, ia menambahkan nama variabel ke array `unusedVariables`.
- Kembalikan unusedVariables: Mengembalikan array yang berisi nama-nama variabel yang tidak digunakan.
- Output: Mencetak variabel yang tidak digunakan ke konsol.
Contoh ini menunjukkan linter sederhana. Anda dapat mengembangkannya untuk memeriksa standar pengkodean lain dan mengidentifikasi potensi masalah lain dalam kode Anda. Misalnya, Anda dapat memeriksa impor yang tidak digunakan, fungsi yang terlalu kompleks, atau potensi kerentanan keamanan. Kuncinya adalah memahami cara menelusuri AST dan mengidentifikasi jenis simpul spesifik yang Anda minati.
Praktik Terbaik dan Pertimbangan
- Pahami AST: Investasikan waktu untuk memahami struktur AST. Gunakan alat seperti AST explorer untuk memvisualisasikan AST dari kode Anda.
- Gunakan Type Guard: Gunakan type guard (`ts.isXXX`) untuk memastikan Anda bekerja dengan jenis simpul yang benar.
- Pertimbangkan Kinerja: Transformasi AST bisa jadi mahal secara komputasi. Optimalkan kode Anda untuk meminimalkan jumlah simpul yang Anda kunjungi dan ubah.
- Tangani Kesalahan: Tangani kesalahan dengan baik. API Kompilator dapat melempar pengecualian jika Anda mencoba melakukan operasi yang tidak valid pada AST.
- Uji Secara Menyeluruh: Uji transformasi Anda secara menyeluruh untuk memastikan bahwa mereka menghasilkan hasil yang diinginkan dan tidak menimbulkan bug baru.
- Gunakan Pustaka yang Ada: Pertimbangkan untuk menggunakan pustaka yang ada yang menyediakan abstraksi tingkat lebih tinggi di atas API Kompilator. Pustaka ini dapat menyederhanakan tugas-tugas umum dan mengurangi jumlah kode yang perlu Anda tulis. Contohnya termasuk `ts-morph` dan `typescript-eslint`.
Kesimpulan
API Kompilator TypeScript adalah alat yang ampuh untuk membangun alat pengembangan canggih. Dengan memahami cara bekerja dengan AST, Anda dapat membuat linter, pemformat kode, penganalisis statis, dan alat lain yang meningkatkan kualitas kode, mengotomatiskan tugas berulang, dan meningkatkan produktivitas pengembang. Meskipun API ini bisa jadi kompleks, manfaat dari menguasainya sangatlah signifikan. Panduan komprehensif ini memberikan dasar untuk menjelajahi dan memanfaatkan API Kompilator secara efektif dalam proyek Anda. Ingatlah untuk memanfaatkan alat seperti AST Explorer, menangani jenis simpul dengan hati-hati, dan menguji transformasi Anda secara menyeluruh. Dengan latihan dan dedikasi, Anda dapat membuka potensi penuh dari API Kompilator TypeScript dan membangun solusi inovatif untuk lanskap pengembangan perangkat lunak.
Eksplorasi Lebih Lanjut:
- Dokumentasi API Kompilator TypeScript: [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/)
- Pustaka ts-morph: [https://ts-morph.com/](https://ts-morph.com/)
- typescript-eslint: [https://typescript-eslint.io/](https://typescript-eslint.io/)