通過babel操作AST精準(zhǔn)插入配置代碼全流程
babel修改js配置文件實(shí)現(xiàn)原理
像那些js配置文件,里面可能有很多的非配置代碼,而且一次可能要修改好幾個(gè)文件
比如我們?cè)谇岸隧?xiàng)目,要插入一個(gè)頁面,需要修改router、menus等配置文件,還要手動(dòng)拷貝頁面模板等等
這些高重復(fù)機(jī)械化操作,人工修改非常容易出錯(cuò)
我們可以直接用babel來操作AST抽象語法樹,通過工程化去精準(zhǔn)修改。讓babel去幫我們找到指定位置,并正確插入配置代碼。我們?cè)谧龉こ袒_發(fā)的時(shí)候,經(jīng)常會(huì)用到babel去操作AST。
首先我們了解一下什么是AST
AST,抽象語法樹(Abstract Syntax Tree)它是源代碼語法結(jié)構(gòu)的一種抽象表示。它以樹狀的形式表現(xiàn)編程語言的語法結(jié)構(gòu)。
我們使用babel來轉(zhuǎn)化和操作AST,主要分為三個(gè)步驟:解析(parser)、轉(zhuǎn)換(traverse)、生成(generator)
操作AST三大階段
如下圖,如果我們想通過babel,在配置文件里面插入一段配置代碼,應(yīng)該怎么實(shí)現(xiàn)呢
解析(parser)
第一步:讀取配置文件代碼,并生成AST抽象語法樹
let configJsData = fs.readFileSync(configJsPath, "utf8");
然后將配置文件代碼生成AST抽象語法樹
const parser = require("@babel/parser"); ? let configJsTree = parser.parse(`${configJsData}`,{ ? ? sourceType: "module", ? ? plugins: [ ? ? ? "jsx", ? ? ? "flow", ? ? ], ? });
configJsTree就是我們的AST了
加上sourceType: "module"
這個(gè)配置屬性,是為了讓babel支持解析export和import
轉(zhuǎn)換(traverse)
轉(zhuǎn)換(traverse)階段,就是要遍歷整個(gè)AST抽象語法樹,找到指定的位置,然后插入對(duì)應(yīng)的配置代碼。
代碼如下:
const traverse = require("@babel/traverse").default; traverse(configJsTree, { ObjectProperty(path) { // 插入配置文件代碼 }, });
我們使用@babel/traverse
的traverse方法進(jìn)行遍歷整個(gè)AST
其中ObjectProperty
的作用是在遍歷AST過程中,識(shí)別出所有的Object對(duì)象,因?yàn)槲覀兪且獙⑴渲么a插入一個(gè)Object對(duì)象,所以使用的是ObjectProperty
。如果要將配置插入數(shù)組中,就使用ArrayExpression
。
然后我們開始進(jìn)行配置代碼的插入,將代碼
{ key: "testPath", icon: HomeOutlined, exact: true, }
插入如下的位置
我們需要在traverse
的ObjectProperty
進(jìn)行位置的查找和代碼插入
首先我們要找到key: 'home'
的位置
代碼如下:
traverse(configJsTree, { ObjectProperty(path) { if ( path.node.key.name === "key" && path.node.value.value === "home" ) { // 這就是 key: 'home'的位置 } }, });
通過path.node.key.name
和path.node.value.value
找到對(duì)象屬性為key
并且對(duì)象值為home
的Object對(duì)象
找到位置后開始插入代碼
traverse(configJsTree, { ObjectProperty(path) { // 搜索并識(shí)別出配置文件里key: "home" 這個(gè)object對(duì)象位置 if ( path.node.key.name === "key" && path.node.value.value === "home" ) { path.parent.properties.forEach(e=>{ if ( e.key.name === "children" ) { // 找到children屬性 } }) } }, });
通過path.parent.properties
找到對(duì)象的父級(jí),然后遍歷父級(jí)下的所有屬性,找到children
這個(gè)屬性。這就是我們要插入的位置。
接下來我們要構(gòu)造要插入的數(shù)據(jù)
{ key: "testPath", icon: HomeOutlined, exact: true, }
構(gòu)造數(shù)據(jù)的代碼如下:
const t = require("@babel/types"); const newObj = t.objectExpression([ t.objectProperty( t.identifier("key"), t.stringLiteral("testPath") ), t.objectProperty( t.identifier("icon"), t.identifier("HomeOutlined") ), t.objectProperty( t.identifier("exact"), t.booleanLiteral(true) ), ]);
可以看到用dentifier
來標(biāo)識(shí)對(duì)象的屬性,用stringLiteral
標(biāo)識(shí)字符串?dāng)?shù)據(jù),booleanLiteral
標(biāo)識(shí)boolean值,這些類型都可以在@babel/types
查詢到。
最后一步,將構(gòu)造好的數(shù)據(jù)插入:
e.value.elements.push(newObj)
完成~!
將所有 traverse 階段代碼匯總起來如下:
const traverse = require("@babel/traverse").default; const t = require("@babel/types"); traverse(configJsTree, { ObjectProperty(path) { // 搜索并識(shí)別出配置文件里key: "home" 這個(gè)object對(duì)象位置 if ( path.node.key.name === "key" && path.node.value.value === "home" ) { path.parent.properties.forEach(e=>{ if ( e.key.name === "children" ) { const newObj = t.objectExpression([ t.objectProperty( t.identifier("key"), t.stringLiteral("testPath") ), t.objectProperty( t.identifier("icon"), t.identifier("HomeOutlined") ), t.objectProperty( t.identifier("exact"), t.booleanLiteral(true) ), ]); e.value.elements.push(newObj) } }) } }, });
生成(generator)
這個(gè)階段就是把AST抽象語法樹反解,生成我們常規(guī)的代碼
const generate = require("@babel/generator").default; const result = generate(configJsTree, { jsescOption: { minimal: true } }, "").code; fs.writeFileSync(resultPath, result);
通過@babel/generator
將AST抽象代碼語法樹反解回原代碼,jsescOption: { minimal: true }
配置是為了解決中文為unicode亂碼的問題。
至此咱們就完成了對(duì)js配置文件插入代碼,最終結(jié)果如下:
以上就是通過babel操作AST,然后精準(zhǔn)插入配置代碼的全流程。
更多關(guān)于babel操作AST插入配置的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
小程序?qū)崿F(xiàn)列表倒計(jì)時(shí)功能
這篇文章主要為大家詳細(xì)介紹了小程序?qū)崿F(xiàn)列表倒計(jì)時(shí)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01javascript 樹形導(dǎo)航菜單實(shí)例代碼
本文章來給大家提供一款適用于網(wǎng)站后臺(tái)的使用的javascript 樹形導(dǎo)航菜單特效代碼,有需要了解的同學(xué)可以參考一下2013-08-08javascript實(shí)現(xiàn)漂亮的拖動(dòng)層,窗口拖拽特效
一個(gè)可關(guān)閉、可隨意拖動(dòng)位置的網(wǎng)頁彈出層代碼,美化的相當(dāng)漂亮,簡潔實(shí)用,還可拖動(dòng)改變大小,通過八個(gè)方向改變大小,學(xué)習(xí)這類特效編寫的網(wǎng)頁設(shè)計(jì)者可參閱一下2015-04-04JS操作XML實(shí)例總結(jié)(加載與解析XML文件、字符串)
這篇文章主要介紹了JS操作XML的方法,結(jié)合實(shí)例形式總結(jié)分析了JavaScript加載與解析XML文件及字符串的相關(guān)技巧,需要的朋友可以參考下2015-12-12微信小程序?qū)崿F(xiàn)下滑到底部自動(dòng)翻頁功能
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)下滑到底部自動(dòng)翻頁功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-03-03bootstrap table列和表頭對(duì)不齊的解決方法
這篇文章主要為大家詳細(xì)介紹了bootstrap table列和表頭對(duì)不齊的解決方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07詳解小程序中h5頁面onShow實(shí)現(xiàn)及跨頁面通信方案
這篇文章主要介紹了小程序中h5頁面onShow實(shí)現(xiàn)及跨頁面通信方案,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-05-05基于js實(shí)現(xiàn)二級(jí)下拉聯(lián)動(dòng)
這篇文章主要為大家詳細(xì)介紹了基于js實(shí)現(xiàn)二級(jí)下拉聯(lián)動(dòng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12