如何向 js 文件中插入一段代碼猎唁?

前端開(kāi)發(fā)過(guò)程當(dāng)中,總會(huì)有一些機(jī)械重復(fù)的工作要做顷蟆,比如:新建文件夾诫隅,新建一個(gè) ts 文件,新建一個(gè)樣式文件帐偎,向已有的菜單文件中增加新建的文件逐纬,初始化等等。這些繁復(fù)的工作雖然不復(fù)雜削樊,但仍需要占用大量的時(shí)間豁生,有沒(méi)有除了手動(dòng)添加外其它的方法呢?

答案是有的漫贞。

初始化

對(duì)于創(chuàng)建文件之類的甸箱,我們可以使用 fs-extra 來(lái)解決,初始化的文件我們只需要傳入一段已寫(xiě)好的 js 就好迅脐。

fs-extra: 文件操作相關(guān)工具庫(kù)摇肌,是系統(tǒng) fs 模塊的擴(kuò)展,提供了更多便利的 API仪际,并繼承了 fs 模塊的 API围小。

安裝

npm install fs-extra

使用示例:

const fs = require('fs-extra')

// Async with promises:
fs.copy('/tmp/myfile', '/tmp/mynewfile')
  .then(() => console.log('success!'))
  .catch(err => console.error(err))

// Async with callbacks:
fs.copy('/tmp/myfile', '/tmp/mynewfile', err => {
  if (err) return console.error(err)
  console.log('success!')
})

// Sync:
try {
  fs.copySync('/tmp/myfile', '/tmp/mynewfile')
  console.log('success!')
} catch (err) {
  console.error(err)
}

// Async/Await:
async function copyFiles () {
  try {
    await fs.copy('/tmp/myfile', '/tmp/mynewfile')
    console.log('success!')
  } catch (err) {
    console.error(err)
  }
}

copyFiles()

使用方法:

Async

Sync

那么如何向已有文件追加呢?

答:我們可以考慮使用 babel树碱。

追加文件

我們想要追加文件的話大概需要如何幾個(gè)步驟:

  • 讀取文件

  • 解析文件

  • 遍歷修改

  • 生成代碼

  • 寫(xiě)回文件

讀取文件

使用上面的 fs-extra 讀取文件肯适,方法如下:

fs.readFileSync(dir, format)

dir: 讀取文件的路徑,format: 編碼格式

解析文件

使用 @babel/parser 解析文件

babelParser.parse(code, [options])
babelParser.parseExpression(code, [options])

parse 將提供的代碼作為一個(gè)完整的 ECMAScript 程序進(jìn)行解析成榜,而parseExpression 則嘗試解析單個(gè)表達(dá)式并考慮性能框舔。code: 剛剛讀取的代碼,options: 配置信息

options 選項(xiàng)如下:

  • allowImportExportEverywhere: 默認(rèn)情況下,importexport聲明只能出現(xiàn)在程序的頂層刘绣。如果將此選項(xiàng)設(shè)置為true樱溉,則允許在任何允許使用語(yǔ)句的地方使用它們。

  • allowAwaitOutsideFunction: 默認(rèn)情況下纬凤,只允許在異步函數(shù)內(nèi)部使用 await福贞,或者在啟用 topLevelAwait 插件時(shí),允許在模塊的頂層作用域中使用await停士。將其設(shè)置為true挖帘,以便在腳本的頂級(jí)范圍中也接受它。

  • allowReturnOutsideFunction: 默認(rèn)情況下恋技,頂層的 return 語(yǔ)句會(huì)引發(fā)錯(cuò)誤拇舀。將此設(shè)置為 true 以接受此類代碼。

  • allowSuperOutsideMethod: 默認(rèn)情況下蜻底,在類和對(duì)象方法之外不允許 super 使用骄崩。將此設(shè)置為 true 以接受此類代碼。

  • allowUndeclaredExports: 默認(rèn)情況下薄辅,導(dǎo)出未在當(dāng)前模塊范圍中聲明的標(biāo)識(shí)符將引發(fā)錯(cuò)誤刁赖。雖然 ECMAScript 模塊規(guī)范要求這種行為,但是 Babel 的解析器無(wú)法預(yù)測(cè)插件管道中稍后可能插入適當(dāng)聲明的轉(zhuǎn)換长搀,因此,有時(shí)必須將此選項(xiàng)設(shè)置為 true鸡典,以防止解析器過(guò)早地抱怨稍后將添加的未聲明的導(dǎo)出源请。

  • createParenthesizedExpressions: 默認(rèn)情況下,解析器設(shè)置extra.parenthesized 在表達(dá)式節(jié)點(diǎn)上彻况。當(dāng)此選項(xiàng)設(shè)置為 true 時(shí)谁尸,將創(chuàng)建ParenthesizedExpression(圓括號(hào)) AST節(jié)點(diǎn)。

  • errorRecovery: 默認(rèn)情況下纽甘,Babel總是在發(fā)現(xiàn)一些無(wú)效代碼時(shí)拋出一個(gè)錯(cuò)誤良蛮。當(dāng)此選項(xiàng)設(shè)置為 true 時(shí),它將存儲(chǔ)解析錯(cuò)誤并嘗試?yán)^續(xù)解析無(wú)效的輸入文件悍赢。生成的 AST 將有一個(gè)errors屬性决瞳,表示所有解析錯(cuò)誤的數(shù)組。請(qǐng)注意左权,即使啟用了此選項(xiàng)皮胡,@babel/parser也可能拋出不可恢復(fù)的錯(cuò)誤。

  • plugins: 包含要啟用的插件的數(shù)組赏迟。

  • sourceType: 指示分析代碼的模式屡贺。可以是"script", "module""unambiguous" 之一。默認(rèn)為 "script"甩栈。 "unambiguous"將使 @babel/parser嘗試根據(jù)存在的 ES6 導(dǎo)入或?qū)С稣Z(yǔ)句進(jìn)行猜測(cè)泻仙。帶有 ES6 importexport的文件被視為"module",否則是"script"量没。

  • sourceFilename: 將輸出 AST 節(jié)點(diǎn)與其源文件名關(guān)聯(lián)玉转。從多個(gè)輸入文件的AST 生成代碼和源映射時(shí)非常有用。

  • startLine: 默認(rèn)情況下允蜈,被解析的第一行代碼被視為第1行冤吨。您可以提供一個(gè)行號(hào)作為替代。有助于與其他源工具集成饶套。

  • strictMode: 默認(rèn)情況下漩蟆,只有在存在“use strict”;指令或解析的文件是ECMAScript模塊時(shí)妓蛮,ECMAScript 代碼才會(huì)被解析為 strict怠李。將此選項(xiàng)設(shè)置為true可始終以嚴(yán)格模式解析文件。

  • ranges: 向每個(gè)節(jié)點(diǎn)添加 range屬性:[node.start, node.end]

  • tokens: 將所有解析的令牌添加到File 節(jié)點(diǎn)上的 tokens屬性

遍歷修改

使用 @babel/traverse 遍歷解析過(guò)的代碼蛤克,并在此基礎(chǔ)上進(jìn)行修改捺癞。

traverse(ast, visitors)

ast: parser 解析的代碼,visitors: 特定節(jié)點(diǎn)操作

traverse(ast, {
  CallExpression(p) {
    // 對(duì)語(yǔ)法樹(shù)中特定的節(jié)點(diǎn)進(jìn)行操作 參考@babel/types (特定節(jié)點(diǎn)類型)
    // CallExpression 特定節(jié)點(diǎn)
  },
  FunctionDeclaration: function(path) {
    // 對(duì)語(yǔ)法樹(shù)中特定的節(jié)點(diǎn)進(jìn)行操作 參考@babel/types (特定節(jié)點(diǎn)類型)
    // FunctionDeclaration 特定節(jié)點(diǎn)
  }
})

生成代碼

使用 @babel/generator 將修改過(guò)的 ast 重新生成代碼构挤。

generate(ast, [opts], [code]);

ast: 修改過(guò)的抽象語(yǔ)法樹(shù)髓介,options: 配置信息,code: 原始代碼筋现,用于映射

opts 選項(xiàng)如下:

  • auxiliaryCommentBefore 作為塊注釋添加到輸出文件開(kāi)頭的可選字符串

  • auxiliaryCommentAfter 在文件的結(jié)尾添加可選的字符串作為注釋

  • shouldPrintComment 類型:function唐础,默認(rèn)值:opts.comments。該函數(shù)接受注釋(作為字符串)并在輸出中包含注釋時(shí)返回 true矾飞。默認(rèn)情況下一膨,注釋是被包含的。如果 opts.comments:true 或者opts.minified:false洒沦,并且注釋包含@preserve 或 @license

  • retainLines 類型:boolean豹绪,默認(rèn)值:false。嘗試在輸出代碼中使用與源代碼相同的行號(hào)(有助于保留堆棧跟蹤)

  • retainFunctionParens 類型:boolean申眼,默認(rèn)值:false瞒津。保留函數(shù)表達(dá)式周圍的 parens(可用于更改引擎解析行為)

  • comments 類型:boolean,默認(rèn)值:true括尸。輸出中是否應(yīng)包含注釋

  • compact 類型:boolean or 'auto'仲智,默認(rèn)值:opts.minified。設(shè)置為 true 以避免為格式添加空白

  • minified 類型:boolean姻氨,默認(rèn)值:false钓辆。確定是否縮小輸出

  • concise 類型:boolean,默認(rèn)值:false。設(shè)置為true以減少空白(但不如選擇緊湊型)

  • filename 用于警告消息

  • jsonCompatibleStrings 類型:boolean前联,默認(rèn)值:false功戚。設(shè)置為 true 以使用“json”運(yùn)行 jsesc:true 將打印“\u00A9”。

  • sourceMaps 類型:boolean似嗤,默認(rèn)值:false啸臀。啟用生成源映射

  • sourceRoot 源映射中所有相對(duì)URL的根

  • sourceFileName 源代碼的文件名(即code參數(shù)中的代碼)。僅當(dāng)代碼是字符串時(shí)才使用此選項(xiàng)烁落。

寫(xiě)回文件

使用 fs-extra 將重新生成的代碼寫(xiě)回文件乘粒。

fs.writeFileSync(dir, code);

dir: 文件目錄,code: 代碼

總結(jié)

通過(guò) fs-extrababel 相關(guān)插件我們可以很愉快地進(jìn)行文件的創(chuàng)建伤塌,代碼的修改灯萍,當(dāng)然也不止適用于這一種情況,還有很多無(wú)限的可能等著我們?nèi)?shí)現(xiàn)每聪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末旦棉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子药薯,更是在濱河造成了極大的恐慌绑洛,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,110評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件童本,死亡現(xiàn)場(chǎng)離奇詭異真屯,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)穷娱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)绑蔫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鄙煤,你說(shuō)我怎么就攤上這事〔杼唬” “怎么了梯刚?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,474評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)薪寓。 經(jīng)常有香客問(wèn)我亡资,道長(zhǎng),這世上最難降的妖魔是什么向叉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,881評(píng)論 1 295
  • 正文 為了忘掉前任锥腻,我火速辦了婚禮,結(jié)果婚禮上母谎,老公的妹妹穿的比我還像新娘瘦黑。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,902評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布幸斥。 她就那樣靜靜地躺著匹摇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪甲葬。 梳的紋絲不亂的頭發(fā)上廊勃,一...
    開(kāi)封第一講書(shū)人閱讀 51,698評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音经窖,去河邊找鬼坡垫。 笑死,一個(gè)胖子當(dāng)著我的面吹牛画侣,可吹牛的內(nèi)容都是我干的冰悠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,418評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼棉钧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼屿脐!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起宪卿,我...
    開(kāi)封第一講書(shū)人閱讀 39,332評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤的诵,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后佑钾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體西疤,經(jīng)...
    沈念sama閱讀 45,796評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,968評(píng)論 3 337
  • 正文 我和宋清朗相戀三年休溶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了代赁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,110評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡兽掰,死狀恐怖芭碍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情孽尽,我是刑警寧澤窖壕,帶...
    沈念sama閱讀 35,792評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站杉女,受9級(jí)特大地震影響瞻讽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜熏挎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,455評(píng)論 3 331
  • 文/蒙蒙 一速勇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坎拐,春花似錦烦磁、人聲如沸养匈。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,003評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)乖寒。三九已至,卻和暖如春院溺,著一層夾襖步出監(jiān)牢的瞬間楣嘁,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,130評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工珍逸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逐虚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,348評(píng)論 3 373
  • 正文 我出身青樓谆膳,卻偏偏與公主長(zhǎng)得像叭爱,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子漱病,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,047評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容