awatch
Version:
Async file watcher
74 lines (67 loc) • 2.21 kB
JavaScript
const fs = require('fs')
const path = require('path')
const aglob = require('aglob')
const debug = require('debug')('a:watch')
/**
* @function watchFiles to change
* @param {string} pattern - Filename patterns
* @param {function} handler - Change handler
* @param {Object} [options={}] - Optional settings
* @param {string} [options.cwd] - Current working directory
* @param {string} [options.ignore] - Glob patterns to ignore
* @param {number} [options.interval=100] - Watch fire interval
* @param {function} [options.onError=(err) => console.error(err)] - Error handler
* @returns {Promise}
*/
async function awatch (pattern, handler, options = {}) {
const {
cwd = process.cwd(),
ignore = [],
interval = 10,
onError = (err) => console.error(err)
} = options
const filenames = await aglob(pattern, { cwd, ignore })
const changed = {}
const watchers = {}
const watch = (watched) => fs.watch(watched, (event, filename) => {
fs.stat(watched, (err, state) => {
const isDir = !err && state && state.isDirectory()
const detected = path.resolve(isDir ? path.resolve(watched, filename) : watched)
const name = [event, detected].join(':')
changed[name] = { event, filename, detected, watched }
})
})
const timer = setInterval(() => {
for (const name of Object.keys(changed)) {
if (!changed[name]) {
continue
}
const { event, watched, detected } = changed[name]
delete changed[name]
void Promise.resolve(handler(event, detected)).catch(onError)
const oldWatcher = watchers[watched]
try {
watchers[watched] = watch(watched)
} catch (err) {
console.log(err)
}
if (oldWatcher) {
oldWatcher.close()
}
}
}, interval).unref()
debug('Filenames', filenames)
for (const filename of filenames) {
watchers[filename] = watch(path.resolve(cwd, filename))
}
async function close () {
clearInterval(timer)
for (const filename of Object.keys(watchers)) {
const watcher = watchers[filename]
watcher.close()
}
}
return Object.assign(close, { close, filenames, timer })
}
module.exports = awatch