此篇為webpack處理文件內(nèi)容——編寫postcss8.0插件姐妹篇
本文目標
探究編寫一個babel插件顺献,將代碼里的http:
轉(zhuǎn)成https:
前言
webpack的babel原理好多博客都有講,Babel 插件手冊里面說
Babel 的三個主要處理步驟分別是:解析(parse),轉(zhuǎn)換(transform)的畴,生成(generate)
解析步驟接收代碼并輸出 AST。 這個步驟分為兩個階段:詞法分析(Lexical Analysis)和 語法分析(Syntactic Analysis)宗挥。
轉(zhuǎn)換步驟接收 AST 并對其進行遍歷厢呵,在此過程中對節(jié)點進行添加、更新及移除等操作俺夕。
代碼生成步驟把最終(經(jīng)過一系列轉(zhuǎn)換之后)的 AST 轉(zhuǎn)換成字符串形式的代碼,同時還會創(chuàng)建源碼映射(source maps)喷橙。
其實就是code->AST->對AST操作->生成code
那么問題來了啥么,如何對AST進行操作?
AST
用這個網(wǎng)站可以將代碼轉(zhuǎn)成AST:https://astexplorer.net/
- 代碼
有鏈接的情況一種是定義了一個變量然后給他賦值贰逾,一種是直接跳轉(zhuǎn)悬荣。
const AURL = 'http://XXXXX';
const BURL = 'http://XX.XXX';
function jump(path) {
window.location.href = path;
}
jump(AURL);
window.location.href = 'http://XXXXX';
-
效果
還挺好使的 - -
-
簡單說
這段代碼里聲明并賦值了一個值為鏈接的變量,然后從這個AST里我知道了這個語句的type為VariableDeclaration疙剑,babel的插件可以針對type為這個的節(jié)點做處理
開始寫插件
Visitors訪問者
寫一個自己的插件之前氯迂,還記得怎么給babel使用插件嗎?來復(fù)習一下吧言缤。假設(shè)我們的插件即將放在根目錄的plugins文件夾里嚼蚀,叫tran-http-plugin
- 在webpack.config.js里
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
}
}
]
}
- 在.bablerc或者是babel.config.json里有plugins屬性
"plugins": ["plugins/tran-http-plugin"]
- 然后我們寫一下插件內(nèi)容,我喜歡邊調(diào)試邊寫管挟,插件書寫規(guī)則大致如下
a. 導(dǎo)出一個函數(shù)轿曙,函數(shù)返回體是一個對象
b. visitor是訪問者,可以在這個對象里面定義訪問節(jié)點的方法
c. 想處理什么類型的節(jié)點,就找出他們的名字导帝,作為屬性將方法寫在visitor
里守谓。
舉個例子:
比如說Identifier
就是一種節(jié)點的類型,它翻譯過來就是識別碼您单。
代碼里有一個聲明語句斋荞,聲明了變量AURL
并對其賦值。
那么這個語句的語法樹里會有Identifier
這個節(jié)點虐秦,并且node.name="AURL"
平酿。我們可以在這里對變量名做一些修改操作。
module.exports = function ({ types: t }) {
return {
visitor: {
Identifier(path) {
console.log('Visiting: ' + path.node.name);
},
VariableDeclaration(path, state) {
console.log('VariableDeclaration');
},
},
};
};
- 調(diào)試
調(diào)試這個腳本悦陋,類似于調(diào)試node一樣蜈彼,很簡單。 不會的可以參考這篇 叨恨。
觀察控制臺
-
觀察節(jié)點
直觀感受柳刮,就是訪問到聲明語句的時候才會進VariableDeclaration里。
再展開痒钝,要改的部分其實是里面的StringLiteral秉颗,那么我們就可以在這里面對這個節(jié)點進行操作。
因為這是個語法樹的關(guān)系送矩,所以我們要用操作語法樹的方法去操作節(jié)點蚕甥。
改變節(jié)點
在上一個環(huán)節(jié)我們找到了想改的內(nèi)容
思路
建一個一樣的節(jié)點,然后替換他栋荸!
構(gòu)建節(jié)點
這里我們用到的是@babel/types 插件菇怀,各種節(jié)點的說明文檔在此 。
這個文檔反正挺惜字如金的
以這個賦值語句為例分析
const AURL = 'http://XXXXX';
- 如何快速定位晌块?變量聲明節(jié)點的構(gòu)成是什么爱沟?
如果打開文檔,就會發(fā)現(xiàn)節(jié)點多得數(shù)不清匆背,完全不曉得我們要從哪里下手去構(gòu)建這個聲明語句的node呼伸。
在寫代碼之前,我們先了解一下賦值語句的語法節(jié)點構(gòu)成吧钝尸。
對于賦值語句括享,可以從斷點的那個path看出關(guān)鍵詞。搜一下declarations
這個單詞珍促,有且只有兩個相關(guān)節(jié)點铃辖。
variableDeclaration:
t.variableDeclaration(kind, declarations)
variableDeclarator:t.variableDeclarator(id, init)
得出推論
a. 一個變量聲明語句節(jié)點為variableDeclaration,kind為這個變量的類型猪叙,var娇斩、const這種仁卷,declarations是一個數(shù)組(Array<VariableDeclarator>
)。
b. 每一個VariableDeclarator擁有自己的id和init屬性成洗。從調(diào)試結(jié)果來看五督,在這個賦值語句中,id就是Identifier節(jié)點瓶殃,init是StringLiteral節(jié)點。
綜上副签,一個賦值語句的語法樹會出現(xiàn)variableDeclaration遥椿、VariableDeclarator、Identifier淆储、StringLiteral這四種節(jié)點冠场。
————————
賦值語句節(jié)點偽代碼如下
variableDeclaration(kind,[
VariableDeclarator(Identifier,StringLiteral)
])
- 怎么寫
我們要操作的其實是stringLiteral
,這里是等號右邊那個值本砰。把這個字符串替換掉碴裙。
在上面的截圖里也有體現(xiàn)了,完整代碼如下
// tran-http-plugin.js
module.exports = function ({ types: t }) {
return {
visitor: {
Identifier(path) {
console.log('Visiting: ' + path.node.name);
},
StringLiteral(path) {
var node = path.node;
var oldVal = node.value;
var matchRes = oldVal.match(/http:\/\/([\w.]+\/?)\S*/);
var newVal = '';
if (!matchRes) return;
newVal = oldVal.replace(/http:\/\//g, 'https://');
path.replaceWith(t.StringLiteral(newVal));
},
},
};
};
- 打包結(jié)果
- 源文件
const AURL = 'http://XXXXX';
const BURL = 'http://XX.XXX';
function jump(path) {
window.location.href = path;
}
jump(AURL);
window.location.href = 'http://XXXXX';
-
轉(zhuǎn)換后
參考資料:
babel腳本文檔
https://zhuanlan.zhihu.com/p/84799735
http://www.reibang.com/p/7c8c5ae1e4be
https://www.csdn.net/gather_2b/MtTacg0sMzQwNzYtYmxvZwO0O0OO0O0O.html
http://www.reibang.com/p/44c0075fd043