node 写一个自动监听文件并读写配置的脚本

前言

个人 github/blog,给个小星星咯~javascript

最近由于工做,须要写一个脚原本自动读取文件夹下的某个文件,把其中的内容写到另外一个新生成的文件中。由于这种场景仍是挺常见的,网络上也搜不到好的(手把手教学的)解决方案,这对于还没学过 node.js 的前端小白来讲,很不友好啊~前端

因而这篇文章就手把手教你写一个这样的脚本,都会尽可能解释清楚,保证你看了就会!java

场景举例

假若有这么一个项目,其文件目录以下:node

|-- app1
    |-- config.json
    |-- index.js
|-- app2
    |-- config.json
    |-- index.js
|-- app3
    |-- config.json
    |-- index.js
|-- app4
    |-- config.json
    |-- index.js
|-- config.all.js
|-- package.json
复制代码

index.js 中的内容是啥与本文无关,只是作个样子,可是在每一个 app 文件夹中都有一个 config.json 文件,这就是咱们须要读的配置文件,如今咱们要作的就是写一个 node 脚本去监听当前这个目录的文件变更,而且实时地去读各个 app 下的配置文件,并写入 config.all.js 文件中。webpack

如今假设配置文件 config.json 内容大概以下:nginx

{
  "name""vortesnail",
  "github""github.com/vortesnail",
  "age""24",
  "address""earth",
  "hobby": ["sing""dance""rap""code"]
}
复制代码

各个 app 文件下的 config.json 内容可不一致,比较符合咱们的实际项目场景。git

脚本编写

安装 chokidar

由于用原生的 fs.watch 会有不少问题,而且有很大的局限,咱们采用第三方模块 chokidar 来进行文件的监听。github

npm install chokidar
复制代码

建立脚本文件

如今在根目录下建立咱们的脚本文件: auto-config.js ,固然,名字随你。
先引用咱们的第三方模块 chokidar ,以及 node 核心模块 fspath 以及 processweb

const chokidar = require('chokidar')
const fs = require('fs')
const path = require('path')
const process = require('process')
const PROJECT_PATH = process.cwd()
复制代码

PROJECT_PATH 表示当前目录路径。npm

使用 chokidar.watch

这里须要注意咱们是 chokidar.watch('.', {}).on(). 表明当前跟路径,使用 PROJECT_PATH 会有问题,有知道的大佬能够评论交流一下!

const chokidar = require('chokidar')
const fs = require('fs')
const path = require('path')
const process = require('process')
const PROJECT_PATH = process.cwd()

chokidar.watch('.', {
  persistenttrue,
  ignored/(^|[\/\\])\..|auto-config.js|config.all.js|node_modules/,
  depth1
}).on('all', (event, pathname) => {
  console.log(event, pathname)
  // do something later...
})
复制代码
  • persistent: 与原生 fs.watch 同样,表示是否保护进程不退出持久监听,默认值为true。
  • ignored: 所要忽略监听的文件或文件夹。
  • depth: 只监听当前目录以及下一级子目录。

使用 fs.readdirSync

使用 fs.readdirSync(PROJECT_PATH) 可读取当前目录的文件列表,是数组形式,数组内容为每个文件或文件夹的名字。更新咱们的代码:

chokidar.watch('.', {
  persistent: true,
  ignored: /(^|[\/\\])\..|auto-config.js|config.all.js|node_modules/,
  depth: 0
}).on('all', (event, pathname) => {
  console.log(event, pathname)
- // do something later...
+ const rootFilenames = fs.readdirSync(PROJECT_PATH)
+ console.log(rootFilenames)
})
复制代码

如今已经能够在当前目录执行 node auto-config.js 来查看当前控制台的打印了,会发现循环打印了当前目录下的文件名字数组:

[
  'app1',
  'app2',
  'app3',
  'app4',
  'auto-config.js',
  'config.all.js',
  'node_modules',
  'package-lock.json',
  'package.json'
]
复制代码

循环的缘由是 chokidar 第一次会监听当前目录全部文件的 add 事件,都有哪些事件详情可看这: event

循环遍历每一个文件夹并获取子目录文件列表

得到了当前目录的文件,咱们须要先筛选出文件夹,再对该文件夹(好比咱们的 app1app2 文件夹)使用上面使用过的 fs.readdirSync([路径]) 来获取配置文件所在目录的文件列表, [路径] 可经过字符串拼接获得。

chokidar.watch('.', {
    // ...
}).on('all', (event, pathname) => {
  console.log(event, pathname)
  const rootFilenames = fs.readdirSync(PROJECT_PATH)
- console.log(rootFilenames)
+ rootFilenames.forEach(function(file) {
+   const newPath = path.join(PROJECT_PATH, `/${file}/`)
+   const subFilenanme = fs.readdirSync(newPath)
+   console.log(subFilenanme)
+ })
})
复制代码

可是如今会报错,由于对于 fs.readdirSync 来讲,若读取的当前路径为一个文件而不是一个文件夹,就会发生错误并终止程序的运行。故咱们须要对其作一个判断。

读取文件状态 fs.stat

使用 fs.stat(path,callback) ,而不是 fs.statSync ,咱们能够处理错误发生后的一些操做。

  • callback 有两个参数: (err,stats),stats 是一个 fs.Stats 对象。
  • stats.isDirectory() 可判断是不是文件夹。

更新代码以下:

chokidar.watch('.', {
    // ...
}).on('all', (event, pathname) => {
  console.log(event, pathname)
  const rootFilenames = fs.readdirSync(PROJECT_PATH)
  rootFilenames.forEach(function(file{
    const newPath = path.join(PROJECT_PATH, `/${file}/`)
    fs.stat(newPath, function(err, stats{
      if(err){
        console.log(file + 'is not a directory...')
      } else {
        const isDir = stats.isDirectory() //是文件夹
        if (isDir) {
          const subFilenanmes = fs.readdirSync(newPath)
          console.log(subFilenanmes)
        }
      }
    })
  })
})
复制代码

如今已经能够获取到子目录的文件列表了,接下来能够判断是否找到咱们须要读取的文件,而且读文件了。

使用 fs.readFileSync 与 fs.writeFileSync

咱们须要一个变量来存储读取到的值,这里咱们使用

let content = ''
复制代码

这里我只是简单的读取 .json 文件,并将其内容后添加一个 , 并所有写入到新生成的 config.all.js 文件中。

添加代码以下:

chokidar.watch('.', {
  persistent: true,
  ignored: /(^|[\/\\])\..|auto-config.js|config.all.js|node_modules/,
  depth: 0
}).on('all', (event, pathname) => {
  console.log(event, pathname)
+ let content = ''
  const rootFilenames = fs.readdirSync(PROJECT_PATH)
  rootFilenames.forEach(function(file) {
    const newPath = path.join(PROJECT_PATH, `/${file}/`)
    fs.stat(newPath, function(err, stats) {
      if(err){
        console.log(file + 'is not a directory...')
      } else {
        const isDir = stats.isDirectory() //是文件夹
        if (isDir) {
          const subFilenanmes = fs.readdirSync(newPath)
-         console.log(subFilenanmes)
+         subFilenanmes.forEach(function(file) {
+           if (file === 'config.json') {
+             const data = fs.readFileSync(path.join(newPath, file), 'utf-8') //读取文件内容
+             content += data + ',' + '\n'
+           }
+           fs.writeFileSync(path.join(PROJECT_PATH, 'config.all.js'), `module.exports={data: [${content}]}`)
+         })
        }
      }
    })
  })
+ console.log(`配置表 config.all.js 已自动生成...`)
})
复制代码

到目前为止,这个读写脚本就算完成了,你不信你执行 node auto-config.js ,再打开根目录下 config.all.js 文件看看,是否是把全部 app 目录下的 config.json 中的文件写入到里面了,并且你任意修改一下当前目录以及子目录的任一文件内容,都会从新生成配置表。

处理瑕疵

最后的的打印由于第一次监听会生成不少不少。。。这看起来太丑了,能够加一个防抖,只让它输出一次。
另外,还能够在适合的地方加一些提示,现放出完整代码:

const chokidar = require('chokidar')
const fs = require('fs')
const path = require('path')
const process = require('process')
const PROJECT_PATH = process.cwd()

chokidar.watch('.', {
  persistenttrue,
  ignored/(^|[\/\\])\..|auto-config.js|config.all.js|node_modules/,
  depth0
}).on('all', (event, pathname) => {
  console.log(event, pathname)
  let content = ''
  const rootFilenames = fs.readdirSync(PROJECT_PATH)
  rootFilenames.forEach(function(file{
    const newPath = path.join(PROJECT_PATH, `/${file}/`)
    fs.stat(newPath, function(err, stats{
      if(err){
        console.log(file + 'is not a directory...')
      } else {
        const isDir = stats.isDirectory() //是文件夹
        if (isDir) {
          const subFilenanmes = fs.readdirSync(newPath)
          subFilenanmes.forEach(function(file{
            if (file === 'config.json') {
              const data = fs.readFileSync(path.join(newPath, file), 'utf-8'//读取文件内容
              content += data + ',' + '\n'
            }
            fs.writeFileSync(path.join(PROJECT_PATH, 'config.all.js'), `module.exports={data: [${content}]}`)
          })
        }
      }
    })
  })

  success()
})

function debounce(func, wait{
  var timeout;
  return function ({
    var context = this;
    var args = arguments;
    clearTimeout(timeout)
    timeout = setTimeout(function(){
      func.apply(context, args)
    }, wait);
  }
}

const success = debounce(() => {
  console.log('配置表 config.all.js 已自动生成...')
}, 500)
复制代码

如今你再试试 node auto-config.js ,看看效果吧~

webpack打包配置

有的时候,咱们不只仅是只在项目中使用而已,咱们须要打包出一个脚本文件,丢到 nginx 环境中去,在那个根目录打开咱们的脚本,自动时时刻刻监听文件的变更,生成配置表,完全解放双手!

打包配置很简单,不要慌!

安装必要插件

无非就是一些 webpack 打包须要的依赖而已,比较重要的是 node-loader 这个包。

npm install -D webpack webpack-cli node-loader
复制代码

webpack配置

根目录建立 webpack.auto.js

const path = require('path');

module.exports = {
  target"node",
  entry: {
    script: path.resolve(__dirname, "auto-config.js"),
  },
  output: {
    publicPath'',
    filename'[name].js',
    path: path.resolve(__dirname, "build"),
  },
  module: {
    rules: [
      {
        test/\.node$/,
        use'node-loader'
      }
    ],
  },
  node: {
    fs'empty',
    child_process'empty',
    tls'empty',
    net'empty'
  },
};
复制代码

比较重要的地方就是 target: node ,以及入口文件要写对,由于 fsevents 中有 .node 文件,咱们须要对其处理,须要一个 node-loader 作识别转译。

修改 package.json

"scripts": {
  "test": "echo \"Error: no test specified\" && exit 1",
+ "build:auto": "webpack -p --progress --config webpack.auto.js"
},
复制代码

打包

如今,你在控制台执行 npm run build:auto ,一个能够监听并读写的小脚本就这样完成了!尽情去使用吧。把打出来的包丢到任意目录,执行:

node auto-config.js
// 没权限的话须要要加 sudo
sudo node auto-config.js
复制代码

完美!

结语

自我认为该写法还有很大改进地方,奈何本身水平有限,若是有大佬有更好的意见,很是但愿您能在评论区说出来,让更多像我这样“求知若渴”的同窗获得成长,感谢!🙏

推荐阅读:
这一次,完全理解 https 原理

相关文章
相关标签/搜索