Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
vortesnail opened this issue Feb 20, 2020 · 0 comments
Open

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

vortesnail opened this issue Feb 20, 2020 · 0 comments
Labels

Comments

@vortesnail
Copy link
Owner

前言

我的 github/blog,给个小星星咯~

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

于是这篇文章就手把手教你写一个这样的脚本,都会尽量解释清楚,保证你看了就会!

场景举例

假如有这么一个项目,其文件目录如下:

|-- 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  文件中。

现在假设配置文件 config.json  内容大概如下:

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

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

脚本编写

安装  chokidar

因为用原生的 fs.watch  会有很多问题,并且有很大的局限,我们采用第三方模块 chokidar  来进行文件的监听。

npm install chokidar

创建脚本文件

现在在根目录下创建我们的脚本文件: auto-config.js ,当然,名字随你。
先引用我们的第三方模块 chokidar ,以及 node 核心模块 fs 、 path  以及 process 。

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

PROJECT_PATH  表示当前目录路径。

使用 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('.', {
  persistent: true,
  ignored: /(^|[\/\\])\..|auto-config.js|config.all.js|node_modules/,
  depth: 1
}).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

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

获得了当前目录的文件,我们需要先筛选出文件夹,再对该文件夹(比如我们的 app1 、 app2  文件夹)使用上面使用过的 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('.', {
  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)
          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: {
    'auto-config': 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 原理

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant