Pendahuluan
Setiap hari ketika bekerja pada kode, dalam perjalanan untuk mengimplementasikan fungsional yang bermanfaat bagi pengguna, perubahan dipaksa (tidak terhindarkan, atau hanya diinginkan) menjadi kode. Ini bisa menjadi refactoring, memperbarui perpustakaan atau kerangka kerja ke versi utama baru, memperbarui sintaksis JavaScript (yang tidak jarang baru-baru ini). Sekalipun perpustakaan adalah bagian dari proyek kerja, perubahan tidak dapat dihindari. Sebagian besar perubahan ini bersifat rutin. Tidak ada yang menarik bagi pengembang di dalamnya, di satu sisi, di sisi lain, itu tidak membawa apa-apa ke bisnis, dan di ketiga, selama proses pembaruan, Anda harus sangat berhati-hati untuk tidak merusak kayu bakar dan tidak merusak fungsionalitas. Dengan demikian, kita sampai pada kesimpulan bahwa lebih baik untuk menggeser rutinitas seperti itu ke pundak program sehingga mereka akan melakukan semuanya sendiri, dan orang itu, pada gilirannya, akan mengontrol apakah semuanya dilakukan dengan benar. Inilah yang akan dibahas dalam artikel hari ini.
AST
Untuk pemrosesan kode program, perlu menerjemahkannya ke dalam representasi khusus, yang akan memudahkan program untuk bekerja. Representasi seperti itu ada, itu disebut Pohon Sintaksis Abstrak (AST).
Untuk mendapatkannya, gunakan parser. AST yang dihasilkan dapat diubah sesuai keinginan Anda, dan untuk menyimpan hasilnya Anda memerlukan pembuat kode. Mari kita perhatikan secara lebih rinci setiap langkah. Mari kita mulai dengan parser.
Parser
Dan kita punya kodenya:
a + b
Parser biasanya dibagi menjadi dua bagian:
Memecah kode menjadi token, yang masing-masing menggambarkan bagian dari kode:
[{ "type": "Identifier", "value": "a" }, { "type": "Punctuator", "value": "+", }, { "type": "Identifier", "value": "b" }]
Membangun pohon sintaksis dari token:
{ "type": "BinaryExpression", "left": { "type": "Identifier", "name": "a" }, "operator": "+", "right": { "type": "Identifier", "name": "b" } }
Dan sekarang kita sudah memiliki gagasan itu, yang dengannya Anda dapat bekerja secara terprogram. Perlu diklarifikasi bahwa ada sejumlah besar parser JavaScript
, berikut adalah beberapa di antaranya:
- babel-parser - parser yang menggunakan
babel
; - espree - parser yang menggunakan
eslint
; - acorn - parser yang menjadi dasar dua sebelumnya;
- esprima - parser populer yang mendukung JavaScript hingga EcmaScript 2017;
- cherow adalah pemain baru di antara parser JavaScript yang mengklaim sebagai yang tercepat;
Ada parser JavaScript standar, ini disebut ESTree dan mendefinisikan node mana yang harus diuraikan.
Untuk analisis yang lebih rinci tentang proses penerapan parser (serta trafo dan generator), Anda dapat membaca kompiler super-kecil .
Untuk mengubah pohon AST, Anda dapat menggunakan pola Pengunjung , misalnya, menggunakan perpustakaan @ babel / traverse . Kode berikut akan menampilkan nama semua pengidentifikasi kode JavaScript dari variabel code
.
import * as parser from "@babel/parser"; import traverse from "@babel/traverse"; const code = `function square(n) { return n * n; }`; const ast = parser.parse(code); traverse(ast, { Identifier(path) { console.log(path.node.name); } });
Generator
Anda dapat menghasilkan kode, misalnya, menggunakan @ babel / generator , dengan cara ini:
import {parse} from '@babel/parser'; import generate from '@babel/generator'; const code = 'class Example {}'; const ast = parse(code); const output = generate(ast, code);
Jadi, pada tahap ini, pembaca seharusnya mendapat ide dasar tentang apa yang diperlukan untuk mengubah kode JavaScript, dan dengan alat apa yang diterapkan.
Ini juga layak ditambahkan alat online seperti astexplorer , itu menggabungkan sejumlah besar parser, transformer, dan generator.
Putout
Putout adalah transformator kode yang diaktifkan plugin. Bahkan, itu adalah persilangan antara eslint dan babel , menggabungkan keunggulan dari kedua alat.
Bagaimana eslint
putout
menunjukkan area masalah dalam kode, tetapi tidak seperti eslint
putout
mengubah perilaku kode, yaitu, ia dapat memperbaiki semua kesalahan yang dapat ditemukan.
Seperti babel
putout
mengonversi kode, tetapi mencoba mengubahnya secara minimal, sehingga dapat digunakan untuk bekerja dengan kode yang disimpan dalam repositori.
Prettier juga layak disebut, itu adalah alat pemformatan, dan berbeda secara radikal.
Jscodeshift terletak tidak jauh dari putout
, tetapi tidak mendukung plugins, tidak menampilkan pesan kesalahan, dan juga menggunakan tipe ast, bukan @ babel / tipe .
Kisah penampilan
Dalam prosesnya, eslint
membantu saya dengan tips saya. Tapi kadang-kadang aku ingin lebih banyak darinya. Misalnya, untuk menghapus debugger , perbaiki test.only , dan juga hapus variabel yang tidak digunakan. Poin terakhir membentuk dasar putout
, selama proses pengembangan, menjadi jelas bahwa itu sangat sulit dan banyak transformasi lainnya lebih mudah untuk diimplementasikan. Dengan demikian, putout
lancar tumbuh dari satu fungsi ke sistem plugin. Menghapus variabel yang tidak terpakai sekarang merupakan proses yang paling sulit, tetapi ini tidak menghalangi kita untuk mengembangkan dan mendukung banyak transformasi lain yang sama bermanfaatnya.
Cara Kerja Putout Di Dalam
Pekerjaan putout
dapat dibagi menjadi dua bagian: engine dan plugins. Arsitektur ini memungkinkan Anda untuk tidak terganggu oleh transformasi saat bekerja dengan mesin, dan ketika bekerja pada plug-in, Anda akan fokus sebanyak mungkin pada tujuannya.
Plugin bawaan
putout
dibangun di atas sistem plugin. Setiap plugin mewakili satu aturan. Menggunakan aturan bawaan, Anda dapat melakukan hal berikut:
- Menggabungkan sifat-sifat perusakan:
Setiap plugin dibangun sesuai dengan Unix Philosophy , yaitu, mereka sesederhana mungkin, masing-masing melakukan satu tindakan, membuat mereka mudah untuk digabungkan, karena mereka pada dasarnya adalah filter.
Misalnya, memiliki kode berikut:
const name = user.name; const password = user.password;
Pertama kali dikonversi dengan menggunakan apply-destructure ke:
const {name} = user; const {password} = user;
Kemudian, menggunakan properti gabungan-penghancuran, itu dikonversi menjadi:
const { name, password } = user;
Dengan demikian, plugin dapat bekerja secara terpisah dan bersamaan. Saat membuat plug-in Anda sendiri, disarankan untuk mematuhi aturan ini, dan mengimplementasikan plug-in dengan fungsionalitas minimal yang hanya melakukan apa yang Anda butuhkan, dan plug-in bawaan dan kustom akan menangani sisanya.
Contoh penggunaan
Setelah membiasakan diri dengan aturan putout
, kami dapat mempertimbangkan contoh menggunakan putout
.
Buat file example.js
dengan konten berikut:
const x = 1, y = 2; const name = user.name; const password = user.password; console.log(name, password);
Sekarang jalankan putout
dengan mengirimkan example.js
sebagai argumen:
coderaiser@cloudcmd:~/example$ putout example.js /home/coderaiser/example/example.js 1:6 error "x" is defined but never used remove-unused-variables 1:13 error "y" is defined but never used remove-unused-variables 6:0 error Unexpected "console" call remove-console 1:0 error variables should be declared separately split-variable-declarations 3:6 error Object destructuring should be used apply-destructuring 4:6 error Object destructuring should be used apply-destructuring 6 errors in 1 files fixable with the `--fix` option
Kami akan menerima informasi yang mengandung 6 kesalahan, dipertimbangkan secara lebih rinci di atas, sekarang kami akan memperbaikinya, dan melihat apa yang terjadi:
coderaiser@cloudcmd:~/example$ putout example.js --fix coderaiser@cloudcmd:~/example$ cat example.js const { name, password } = user;
Sebagai hasil dari koreksi, variabel yang tidak digunakan dan panggilan console.log
dihapus, dan perusakan juga diterapkan.
Pengaturan
Pengaturan default mungkin tidak selalu dan tidak untuk semua orang, jadi putout
mendukung file konfigurasi .putout.json
, terdiri dari bagian-bagian berikut:
Aturan
Bagian rules
berisi sistem aturan. Aturan, secara default, ditetapkan sebagai berikut:
{ "rules": { "remove-unused-variables": true, "remove-debugger": true, "remove-only": true, "remove-skip": true, "remove-process-exit": false, "remove-console": true, "split-variable-declarations": true, "remove-empty": true, "remove-empty-pattern": true, "convert-esm-to-commonjs": false, "apply-destructuring": true, "merge-destructuring-properties": true } }
Untuk mengaktifkan remove-process-exit
cukup setel ke true
dalam file .putout.json
:
{ "rules": { "remove-process-exit": true } }
Ini akan cukup untuk melaporkan semua panggilan process.exit
ditemukan dalam kode dan menghapusnya jika opsi --fix
.
Abaikan
Jika Anda perlu menambahkan beberapa folder ke daftar pengecualian, cukup tambahkan bagian ignore
:
{ "ignore": [ "test/fixture" ] }
Cocok
Jika Anda membutuhkan sistem aturan bercabang, misalnya, aktifkan process.exit
untuk direktori bin
, cukup gunakan bagian match
:
{ "match": { "bin": { "remove-process-exit": true, } } }
Plugin
Jika Anda menggunakan plugin yang tidak terintegrasi dan memiliki awalan putout-plugin-
, Anda harus memasukkannya di bagian plugins
sebelum mengaktifkannya di bagian rules
. Misalnya, untuk menghubungkan putout-plugin-add-hello-world
dan aktifkan aturan add-hello-world
, cukup sebutkan:
{ "rules": { "add-hello-world": true }, "plugins": [ "add-hello-world" ] }
Mesin Putout
Mesin putout
adalah alat baris perintah yang membaca pengaturan, mem-parsing file, memuat dan menjalankan plugin, dan kemudian menulis hasil dari plugin.
Dia menggunakan perpustakaan menyusun kembali , yang membantu untuk melakukan tugas yang sangat penting: setelah parsing dan transformasi, kumpulkan kode dalam keadaan yang semirip mungkin dengan yang sebelumnya.
Untuk parsing, parser yang kompatibel dengan ESTree
digunakan ( babel
saat ini dengan plugin estree
, tetapi perubahan mungkin dilakukan di masa depan), dan alat babel
digunakan untuk transformasi. Kenapa persis babel
? Semuanya sederhana. Faktanya adalah bahwa ini adalah produk yang sangat populer, jauh lebih populer daripada alat serupa lainnya, dan ini berkembang jauh lebih cepat. Setiap proposal baru dalam standar EcmaScript tidak lengkap tanpa plugin babel . Babel juga memiliki buku, Babel Handbook , yang menggambarkan dengan sangat baik semua fitur dan alat untuk melintasi dan mengubah pohon AST.
Plugin khusus untuk Putout
Sistem plugin putout
cukup sederhana, dan sangat mirip dengan eslint plugins , serta babel plugins . Benar, alih-alih satu fungsi, plugin putout
harus mengekspor 3. Ini dilakukan untuk meningkatkan penggunaan kembali kode, karena fungsi duplikasi dalam 3 fungsi tidak terlalu nyaman, jauh lebih mudah untuk memasukkannya ke dalam fungsi yang terpisah dan cukup memanggilnya di tempat yang tepat.
Struktur plugin
Jadi plugin Putout
terdiri dari 3 fungsi:
report
- mengembalikan pesan;find
- mencari tempat dengan kesalahan dan mengembalikannya;fix
- memperbaiki tempat-tempat ini;
Poin utama yang perlu diingat ketika membuat plugin untuk putout
adalah namanya, itu harus dimulai dengan putout-plugin-
. Berikutnya mungkin nama operasi yang dilakukan plugin, misalnya, plugin remove-wrong
harus dipanggil seperti ini: putout-plugin-remove-wrong
.
Anda juga harus menambahkan kata-kata: putout
dan putout-plugin
ke bagian package.json
di bagian keywords
, dan tentukan "putout": ">=3.10"
di peerDependencies
"putout": ">=3.10"
, atau versi yang akan menjadi yang terakhir pada saat menulis plugin.
Contoh plugin untuk Putout
Mari kita menulis contoh plugin yang akan menghapus kata debugger
dari kode. Plugin semacam itu sudah ada, itu adalah @ putout / plugin-remove-debugger dan cukup sederhana untuk mempertimbangkannya sekarang.
Ini terlihat seperti ini:
Jika aturan remove-debugger
disertakan dalam .putout.json
, @putout/plugin-remove-debugger
akan dimuat. Pertama, fungsi find
disebut, dengan menggunakan fungsi traverse
, akan memotong node dari pohon AST dan menyimpan semua tempat yang diperlukan.
Langkah selanjutnya putout
akan berubah menjadi report
untuk mendapatkan pesan yang diinginkan.
Jika flag --fix
digunakan, fungsi fix
plugin akan dipanggil dan transformasi akan dilakukan, dalam hal ini, node akan dihapus.
Contoh Uji Plugin
Untuk mempermudah pengujian plugin, alat @ putout / test ditulis. Pada intinya, itu tidak lebih dari pembungkus pita , dengan beberapa metode untuk kenyamanan dan penyederhanaan pengujian.
Tes untuk plugin remove-debugger
mungkin terlihat seperti ini:
const removeDebugger = require('..'); const test = require('@putout/test')(__dirname, { 'remove-debugger': removeDebugger, });
Kodem
Tidak setiap transformasi perlu digunakan setiap hari, untuk transformasi satu kali itu cukup untuk melakukan hal yang sama, tetapi alih-alih menerbitkan ke npm
letakkan di ~/.putout
. Saat startup, putout
akan mencari di folder ini, mengambil dan memulai transformasi.
Berikut adalah contoh transformasi yang menggantikan tape
dan koneksi coba-ke-pita dengan panggilan supertape : convert-tape-to-supertape .
eslint-plugin-putout
Pada akhirnya, ada baiknya menambahkan satu poin: putout
mencoba mengubah kode secara minimal, tetapi jika itu terjadi pada teman yang melanggar beberapa aturan pemformatan, eslint --fix selalu siap eslint --fix
, dan untuk tujuan ini ada plugin eslint-plugin-putout khusus . Ini dapat mencerahkan banyak kesalahan pemformatan, dan tentu saja itu dapat disesuaikan sesuai dengan preferensi pengembang pada proyek tertentu. Menghubungkannya mudah:
{ "extends": [ "plugin:putout/recommended", ], "plugins": [ "putout" ] }
Sejauh ini, hanya ada satu aturan di dalamnya: one-line-destructuring
, ia melakukan yang berikut:
Ada banyak lagi aturan eslint
yang bisa Anda gunakan untuk membiasakan diri dengan lebih detail .
Kesimpulan
Saya ingin mengucapkan terima kasih kepada pembaca atas perhatian yang diberikan pada teks ini. Saya sangat berharap bahwa topik transformasi AST akan menjadi lebih populer, dan artikel tentang proses yang menarik ini akan muncul lebih sering. Saya akan sangat berterima kasih atas komentar dan saran yang terkait dengan pengembangan putout
lebih lanjut. Buat masalah , kirim kumpulan permintaan , uji, tulis aturan apa yang ingin Anda lihat, dan bagaimana memprogram kode Anda secara terprogram, kami akan bekerja sama untuk meningkatkan alat transformasi AST.