如何自定義babel插件

babel是如何工作的

babel主要處理步驟分爲三個:解析、轉換、生成

在這裏插入圖片描述

解析

解析步驟接受代碼輸出AST,該步驟分爲兩個階段:詞法分析、語法分析。

詞法分析主要是對源代碼進行分詞,產生一個叫做token的數組,分割的單位是運算符、括號、數字、字符串、標點符號等可以處理的最小單元。

然後語法分析再將所有的tokens組合成一個整體,分析它們的語法和關係,最後輸出AST(源代碼的抽象語法樹)。

分成兩個階段後,更容易的對解析步驟作優化,因爲解析步驟大部分的時間都在詞法分析過程中,同時也能提高可移植性。

轉換

AST進行遍歷,遇到需要處理的節點就操作,包括添加、移除和更新等。這個也是插件介入的主要工作內容。(babel也提供了自定義解析步驟的插件功能

生成

最終再將AST轉換回字符串形式的代碼。

自定義插件開發

插件的格式

function customPlugin(babel) {
    return {
        visitor: {
            // 定義多個訪問者
        }
    }
}

如上所示,一個插件就是一個普通的函數,函數接受一個babel對象(包含babel所有的api),最後返回一個包含visitor屬性的對象,visitor屬性中每個key都是一個ast節點的類型,值就是訪問這個節點的函數。

每個訪問者函數都會接受兩個參數:pathstate。path對象表示兩個節點之間連接的對象,例如:

{
  "parent": {
    "type": "FunctionDeclaration",
    "id": {...},
    ....
  },
  "node": {
    "type": "Identifier",
    "name": "square"
  }
}

state對象包含一些額外的狀態信息,例如可以從state.opts中取出爲插件配置的特定選項,甚至可以取出path對象,具體內容可以自己打印看看。

下面開發一個小插件,爲代碼中的console.log添加調用位置的信息。

module.exports = function (babel) {
    const t = babel.types
    
    return {
        name: 'custom-babel-plugin-demo',
        visitor: {
            CallExpression(path) {
                const obj = path.node.callee.object
                const prop = path.node.callee.property
                const arguments = path.node.arguments

                if (t.isIdentifier(obj) && t.isIdentifier(prop) && obj.name === 'console' && prop.name === 'log') {
                    const location = `---trace: line ${path.node.loc.start.line}, column ${path.node.loc.start.column}---`;
                    arguments.push(t.stringLiteral(location))
                }
            }
        }
    }
}

首先你需要知道你要訪問節點的類型,如果不清楚,可以到這個網站查看。babel.types則提供了類似lodash的工具庫功能(api)。

最後再測試下插件功能是否正常:

const { transform } = require('@babel/core')
const options = {
    plugins: [ ['./src/index.js', {
        option1: true,
        options2: false
    }] ]
}

const code = `
    const str1 = 'hello'
    console.log(str1)
    const str2 = 'babel'
    console.log(str2)
    const str3 = 'plugin'
    console.log(str3)
`

transform(code, options, function(err, result) {
    console.log(result.code)
})

得到輸出:

const str1 = 'hello';
console.log(str1, "---trace: line 3, column 4---");
const str2 = 'babel';
console.log(str2, "---trace: line 5, column 4---");
const str3 = 'plugin';
console.log(str3, "---trace: line 7, column 4---");

如果你要編寫良好的測試用例,可以藉助babel-plugin-tester庫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章