什麼是 babel
Babel 是一個工具鏈,主要用於將 ECMAScript 2015+ 版本的程式碼轉換為向後相容的 JavaScript 語法,以便能夠執行在當前和舊版本的瀏覽器或其他環境中。
什麼是抽象語法樹(AST)
在電腦科學中,抽象語法樹(Abstract Syntax Tree,AST),或簡稱語法樹(Syntax tree),是原始碼語法結構的一種抽象表示。 它以樹狀的形式表現程式語言的語法結構,樹上的每個節點都表示原始碼中的一種結構。 之所以說語法是“抽象”的,是因為這裡的語法並不會表示出真實語法中出現的每個細節。
對於AST
的相關介紹:
推薦的介紹連結:
Leveling Up One’s Parsing Game With ASTs
中文翻譯: https://segmentfault.com/a/1190000038511186
維基百科裡的介紹: https://en.wikipedia.org/wiki/Abstract_syntax_tree
babel 的簡單使用
相關 api 可以參考文件: https://babeljs.io/docs/en/babel-core#parse
使用 babel 的 API 將程式碼解析成 ast:
var babel = require("@babel/core");
const code = `const a = 1 + 2;`
// code 解析成 ast
const result = babel.transformSync(code, {ast: true});
console.log(result.ast)
當然 ast 也可以轉換成程式碼:
const { code } = babel.transformFromAstSync(result.ast, { presets: ["minify"], babelrc: false, configFile: false,});
console.log(code)
在這個線上網站,你可以更加直接地看到 code 和 ast 的比較:
https://lihautan.com/babel-ast-explorer
const n = 1
的 ast:
-program:Program{
sourceType:"module" -body:[ -VariableDeclaration { -declarations:[ -VariableDeclarator{ -id:Identifier{ name:"n" } -init:NumericLiteral{ -extra:{ rawValue:1 raw:"1" } value:1 } } ] kind:"const" } ] directives:[]}
babel 外掛:
var babel = require("@babel/core");
const code = 'const n = 1';
const output = babel.transformSync(code, {
plugins: [ function myCustomPlugin() {
return {
visitor: {
Identifier(path) {
// 在這個例子裡我們將所有變數 `n` 變為 `x` if (path.isIdentifier({ name: 'n' })) {
path.node.name = 'x'; }
},
},
};
},
],});
console.log(output.code);
// const x = 1;
通過 babel 的外掛我們可以對程式碼進行隨心所以的修改
關於 visitor
使用的是訪問者模式, 在遍歷階段,babel會先進行深度優先遍歷來訪問AST的每一個節點。你可以為訪問指定一個回撥函式,然後每當訪問某個節點的時候,babel會呼叫這個函式,並給函式傳入當前訪問的節點。
現在我們新增另一個函式: NumericLiteral
, 在剛剛的 ast 中我們可以看到 const n = 1
是有 NumericLiteral
此節點的
function myCustomPlugin() {
return {
visitor: {
Identifier(path) {
console.log('identifier');
},
NumericLiteral(path) {
console.log('NumericLiteral');
},
},
};
}
執行 plugin.js
, 列印結果:
Identifier
NumericLiteral
const x = 1;
即在碰到此節點的時候 就會觸發外掛中對應節點的回撥, 關於回撥函式的 path 可以在此文件中檢視: https://github.com/jamiebuilds/babel-handbook/blob/master/translations/en/plugin-handbook.md#paths
修改表示式
在外掛方法 NumericLiteral
上新增操作:
visitor: {
// 省略其他方法 NumericLiteral(path) {
console.log('NumericLiteral');
const newNode = babel.types.binaryExpression('+', babel.types.NumericLiteral(path.node.value), babel.types.NumericLiteral(10));
path.replaceWith(newNode);
path.skip(); // 因為我們新增加了一個 NumericLiteral, 所以外掛會檢測到, 並且又會觸發此回撥,造成無限迴圈 // skip 的作用就是跳過對當前路徑子節點的訪問 }
}
這裡我們新建了一個 binaryExpression, 將 const x = 1
轉換為 const x = 1 + 10
這是關於 babel.types
的檔案: https://www.babeljs.cn/docs/babel-types
本文程式碼記錄: https://github.com/Grewer/JsDemo/tree/master/babel2AST
參考資料
https://lihautan.com/step-by-step-guide-for-writing-a-babel-transformation/