如何向 js 文件中插入一段代码?

前端开发过程当中,总会有一些机械重复的工作要做,比如:新建文件夹,新建一个 ts 文件,新建一个样式文件,向已有的菜单文件中增加新建的文件,初始化等等。这些繁复的工作虽然不复杂,但仍需要占用大量的时间,有没有除了手动添加外其它的方法呢?

答案是有的。

初始化

对于创建文件之类的,我们可以使用 fs-extra 来解决,初始化的文件我们只需要传入一段已写好的 js 就好。

fs-extra: 文件操作相关工具库,是系统 fs 模块的扩展,提供了更多便利的 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

追加文件

我们想要追加文件的话大概需要如何几个步骤:

  • 读取文件

  • 解析文件

  • 遍历修改

  • 生成代码

  • 写回文件

读取文件

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

fs.readFileSync(dir, format)

dir: 读取文件的路径,format: 编码格式

解析文件

使用 @babel/parser 解析文件

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

parse 将提供的代码作为一个完整的 ECMAScript 程序进行解析,而parseExpression 则尝试解析单个表达式并考虑性能。code: 刚刚读取的代码,options: 配置信息

options 选项如下:

  • allowImportExportEverywhere: 默认情况下,importexport声明只能出现在程序的顶层。如果将此选项设置为true,则允许在任何允许使用语句的地方使用它们。

  • allowAwaitOutsideFunction: 默认情况下,只允许在异步函数内部使用 await,或者在启用 topLevelAwait 插件时,允许在模块的顶层作用域中使用await。将其设置为true,以便在脚本的顶级范围中也接受它。

  • allowReturnOutsideFunction: 默认情况下,顶层的 return 语句会引发错误。将此设置为 true 以接受此类代码。

  • allowSuperOutsideMethod: 默认情况下,在类和对象方法之外不允许 super 使用。将此设置为 true 以接受此类代码。

  • allowUndeclaredExports: 默认情况下,导出未在当前模块范围中声明的标识符将引发错误。虽然 ECMAScript 模块规范要求这种行为,但是 Babel 的解析器无法预测插件管道中稍后可能插入适当声明的转换,因此,有时必须将此选项设置为 true,以防止解析器过早地抱怨稍后将添加的未声明的导出。

  • createParenthesizedExpressions: 默认情况下,解析器设置extra.parenthesized 在表达式节点上。当此选项设置为 true 时,将创建ParenthesizedExpression(圆括号) AST节点。

  • errorRecovery: 默认情况下,Babel总是在发现一些无效代码时抛出一个错误。当此选项设置为 true 时,它将存储解析错误并尝试继续解析无效的输入文件。生成的 AST 将有一个errors属性,表示所有解析错误的数组。请注意,即使启用了此选项,@babel/parser也可能抛出不可恢复的错误。

  • plugins: 包含要启用的插件的数组。

  • sourceType: 指示分析代码的模式。可以是"script", "module""unambiguous" 之一。默认为 "script""unambiguous"将使 @babel/parser尝试根据存在的 ES6 导入或导出语句进行猜测。带有 ES6 importexport的文件被视为"module",否则是"script"

  • sourceFilename: 将输出 AST 节点与其源文件名关联。从多个输入文件的AST 生成代码和源映射时非常有用。

  • startLine: 默认情况下,被解析的第一行代码被视为第1行。您可以提供一个行号作为替代。有助于与其他源工具集成。

  • strictMode: 默认情况下,只有在存在“use strict”;指令或解析的文件是ECMAScript模块时,ECMAScript 代码才会被解析为 strict。将此选项设置为true可始终以严格模式解析文件。

  • ranges: 向每个节点添加 range属性:[node.start, node.end]

  • tokens: 将所有解析的令牌添加到File 节点上的 tokens属性

遍历修改

使用 @babel/traverse 遍历解析过的代码,并在此基础上进行修改。

traverse(ast, visitors)

ast: parser 解析的代码,visitors: 特定节点操作

traverse(ast, {
  CallExpression(p) {
    // 对语法树中特定的节点进行操作 参考@babel/types (特定节点类型)
    // CallExpression 特定节点
  },
  FunctionDeclaration: function(path) {
    // 对语法树中特定的节点进行操作 参考@babel/types (特定节点类型)
    // FunctionDeclaration 特定节点
  }
})

生成代码

使用 @babel/generator 将修改过的 ast 重新生成代码。

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

ast: 修改过的抽象语法树,options: 配置信息,code: 原始代码,用于映射

opts 选项如下:

  • auxiliaryCommentBefore 作为块注释添加到输出文件开头的可选字符串

  • auxiliaryCommentAfter 在文件的结尾添加可选的字符串作为注释

  • shouldPrintComment 类型:function,默认值:opts.comments。该函数接受注释(作为字符串)并在输出中包含注释时返回 true。默认情况下,注释是被包含的。如果 opts.comments:true 或者opts.minified:false,并且注释包含@preserve 或 @license

  • retainLines 类型:boolean,默认值:false。尝试在输出代码中使用与源代码相同的行号(有助于保留堆栈跟踪)

  • retainFunctionParens 类型:boolean,默认值:false。保留函数表达式周围的 parens(可用于更改引擎解析行为)

  • comments 类型:boolean,默认值:true。输出中是否应包含注释

  • compact 类型:boolean or 'auto',默认值:opts.minified。设置为 true 以避免为格式添加空白

  • minified 类型:boolean,默认值:false。确定是否缩小输出

  • concise 类型:boolean,默认值:false。设置为true以减少空白(但不如选择紧凑型)

  • filename 用于警告消息

  • jsonCompatibleStrings 类型:boolean,默认值:false。设置为 true 以使用“json”运行 jsesc:true 将打印“\u00A9”。

  • sourceMaps 类型:boolean,默认值:false。启用生成源映射

  • sourceRoot 源映射中所有相对URL的根

  • sourceFileName 源代码的文件名(即code参数中的代码)。仅当代码是字符串时才使用此选项。

写回文件

使用 fs-extra 将重新生成的代码写回文件。

fs.writeFileSync(dir, code);

dir: 文件目录,code: 代码

总结

通过 fs-extrababel 相关插件我们可以很愉快地进行文件的创建,代码的修改,当然也不止适用于这一种情况,还有很多无限的可能等着我们去实现。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容