自动 Import 工具,前端打字员的自我救赎

新闻 前端
作为一个前端打字员,一个经常遇到的场景就是在 路由文件中引入模块 ,总是做这么机械的事情无异于消耗我这个 前端打字员 的寿命,不能忍,遂写个工具。

自动 import 工具

作为一个前端打字员,一个经常遇到的场景就是在 路由文件中引入模块 ,比如这样

在 router/index.js 中写入

import Vue from 'vue'
import Router from 'vue-router'

const About = () => import('../pages/About.vue')
const Home = () => import('../pages/Home.vue')

Vue.use(Router)
...

如果修改了模块的名字,增加了模块或者删除了模块,就需要重新修改这个路由文件

总是做这么机械的事情无异于消耗我这个 前端打字员 的寿命

不能忍,遂写个工具

整理思路如下

其中,监视目录下文件的变动依靠的是 node API 中 fs.watch(filename[, options][, listener])

替换目标文件中引入模块的部分,则是通过正则来实现

在这里五星推荐一个验证正则是否正确的网站, regexr

代码实现

监视包含模块的目录

fs.watch(dir, {
  recursive: true // 目录下子目录也被监视
}, (event, filename) => { 
// event 是文件变动的类型,添加文件、删除文件和修改文件名都是'rename' 事件
// filename 是变化的文件或目录
  if(event === 'rename'){ // 判断文件变动类型
    
  }
})

当发生 rename 事件后,需要重新获得目录下( from )所有的模块,包括模块名 moduleName,模块文件相对于引用模块文件( to )的相对路径 modulePath ,将它们存入变量 modules 中

实际项目中,模块通常都是 .vue 文件,或者 .jsx 文件,因此只将这些作为模块,在路由文件中引用

另外有些模块文件因为各种原因,希望人工引入,而不被 watch ,这样的文件存入 excludeArr 中

const _ = require('lodash')
let excludeArr = [...]
let modules = []
let extname = '.vue'
let from = './src/pages'
let to = './src/router/index.js"'

const mapDir = d => {
    // 获得当前文件夹下的所有的文件夹和文件
    const [dirs, files] = _(fs.readdirSync(d)).partition(p =>
        fs.statSync(path.join(d, p)).isDirectory()
    )

    // 映射文件夹
    dirs.forEach(dir => {
        modules.concat(mapDir(path.join(d, dir)))
    })

    // 映射文件
    files.forEach(file => {
        // 文件后缀名
        let filename = path.join(d, file)
        if (path.extname(file) === extname) {
            if (!excludeArr.includes(path.resolve(__dirname, filename))) {
                let moduleName = path.basename(file, extname)
                // 若存在 -
                if (moduleName.match('-')) {
                    moduleName = moduleName.replace(
                        /(-)(.{1})/,
                        (match, p1, p2, offset, string) => p2.toUpperCase()
                    )
                }
                modules.push({
                    moduleName,
                    modulePath: path.relative(path.dirname(to), filename)
                })
            }
        }
    })
}

生成好新的待引入的模块后,接下来就是在路由文件中,将对应的内容替换掉

所以需要读写文件以及正则替换

const regex = /\/\*\sautoImport(.*\n)*\/\*\sautoImport\s\*\//g
                let importStr = ''
                modules.forEach((m, index) => {
                    importStr =
                        importStr +
                        fillTemplate(template, m.moduleName, m.modulePath) +
                        (cache.length - 1 === index ? '' : '\n')
                })
                fs.readFile(to, 'utf8', (err, data) => {
                    if (err) return console.log(err)
                    let result = ''
                    if (data.match(regex)) {
                        result = data.replace(
                            regex,
                            `/* autoImport */
${importStr}
/* autoImport */`
                        )
                    } else {
                        /* ***插入在文件***的import插入 */
                        result = data.replace(
                            /(.*import.*)(\n)([^(import)])/,
                            (match, p1, p2, p3, offset, string) => {
                                return `${p1}
/* autoImport */
${importStr}
/* autoImport */
${p3}`
                            }
                        )
                    }

                    fs.writeFile(to, result, 'utf8', err => {
                        if (err) return console.log(err)
                    })
                })

其中 /\/\*\sautoImport(.*\n)*\/\*\sautoImport\s\*\//g 是用于匹配两段注释 /* autoImport */ 及其中间的内容

import Vue from 'vue'
import Router from 'vue-router'
/* autoImport */
const About = () => import('../pages/About.vue')
const Home = () => import('../pages/Home.vue')
/* autoImport */

Vue.use(Router)

当***次使用,没有 /* autoImport */ 时,就需要在***一个 import 后面,插入引入的模块

data.replace( 
                            /(.*import.*)(\n)([^(import)])/, 
                            (match, p1, p2, p3, offset, string) => { 
                                return `${p1} 
/* autoImport */ 
${importStr} 
/* autoImport */ 
${p3}` 

在这里还可以自定义了引入模块的方式,例如懒加载, "const moduleName = () => import(modulePath)"

const template = "const moduleName = () => import(modulePath)"
const fillTemplate = (template, moduleName, modulePath) =>
    template
        .replace('moduleName', moduleName)
        .replace('modulePath', `'${modulePath}'`)

为了工具的灵活性,把可配置项写成json文件的形式

{
    "extname": ".vue",
    "from": "src/pages",
    "to": "src/router/index.js",
    "template": "const moduleName = () => import(modulePath)",
    "exclude": [
        "./src/pages/login.vue",
        "./src/pages/404.vue",
        "./src/pages/overall/**",
        "./src/pages/account-result/**"
    ]
}

然后通过以下的方式来获得

const config = fs.readFileSync('./autoImport.json')
const { extname, from, to, template, exclude } = JSON.parse(config)

后记

下一步准备把这个工具写成webpack的插件,名字我都起好了, AutoImportPlugin ,先在github上占了个坑, 顺手给颗星,不用改Bug

同时准备用更加成熟的模块 chokidar 来代替原生的 watch

工具有问题提issue啊

责任编辑:张燕妮 来源: 推酷
相关推荐

2019-11-13 14:16:09

Python开发工具

2019-11-14 18:47:06

Python程序员人生第一份工作

2013-10-21 09:53:50

Windows 8.1微软

2020-07-24 09:44:03

安全驻场工程师网络安全IT安全

2012-03-07 17:34:28

TransPhone

2019-04-18 08:56:33

互联网996裁员

2009-06-17 10:39:01

互联网

2019-10-21 09:20:36

安卓系统Android

2015-07-17 08:27:19

EMMBYOD

2015-07-20 09:11:19

企业移动管理EMMBYOD安全

2011-12-27 16:31:27

程序员

2018-06-29 15:21:12

中兴程序员区块链

2017-10-20 17:21:16

华为

2016-09-06 14:48:36

存储

2013-07-04 13:50:14

2019-04-16 08:35:05

Selenium ID功能测试自动化

2024-03-08 13:11:05

前端自动化工具

2013-11-11 09:26:50

编程思考

2018-05-23 00:06:25

无人货架智能货架供应链

2012-04-05 13:42:02

点赞
收藏

51CTO技术栈公众号