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 原理

相關文章
相關標籤/搜索