fix: catch and warn if remove watcher is called but no watcher was started.
master
Kebler Network System Administrator 2021-06-29 07:38:59 -07:00
parent 3d036ac7a0
commit f903ef7071
3 changed files with 78 additions and 76 deletions

View File

@ -1,29 +1,29 @@
import Watcher from '../src/watcher.js' import Watcher from '../src/watcher.js'
import onDeath from 'ondeath' import onDeath from 'ondeath'
const USE_CUSTOM_HANDLER=true const USE_CUSTOM_HANDLER = true
const DEBOUNCE=0 const DEBOUNCE = 0
const READY_TIMEOUT=null const READY_TIMEOUT = null
; ;
(async () => { (async () => {
let options = { let options = {
source:'./example/repo/**', source: './example/repo/**',
ignored:['**/dontwatch.js'], ignored: ['**/dontwatch.js'],
ignoreList:['./example/repo/.gitignore'], ignoreList: ['./example/repo/.gitignore'],
debounce: DEBOUNCE, debounce: DEBOUNCE,
readyTimeout:READY_TIMEOUT readyTimeout: READY_TIMEOUT
} }
let watcher = new Watcher(options) let watcher = new Watcher(options)
watcher.on('ready', (state,opts) =>{ watcher.on('ready', (state, opts) => {
console.log('watched files indexed and ready??',state) console.log('watched files indexed and ready??', state)
if (opts) console.dir(opts) if (opts) console.dir(opts)
}) })
watcher.on('watching', (state, opts) =>{ watcher.on('watching', (state, opts) => {
console.log('watcher is active and listening for changes?',state) console.log('watcher is active and listening for changes?', state)
if (opts) console.dir(opts) if (opts) console.dir(opts)
}) })
@ -31,10 +31,10 @@ const READY_TIMEOUT=null
await watcher.start( await watcher.start(
// {ignored:'**/another'} // {ignored:'**/another'}
) )
if (USE_CUSTOM_HANDLER) { if (USE_CUSTOM_HANDLER) {
watcher.registerHandler( watcher.registerHandler(
function handler (type, f) { function handler(type, f) {
this.emit('custom', f, type) this.emit('custom', f, type)
} // end handler } // end handler
) )
@ -43,20 +43,20 @@ const READY_TIMEOUT=null
console.log(`custom handler ======= file ${file} was ${type} ==========`) console.log(`custom handler ======= file ${file} was ${type} ==========`)
}) })
} }
// default handler event is `changed` // default handler event is `changed`
else watcher.on('changed', else watcher.on('changed',
(file,type) => { (file, type) => {
console.log(`======= file ${file} was ${type} ==========`) console.log(`======= file ${file} was ${type} ==========`)
}) })
onDeath( () => { onDeath(() => {
console.log('\nHe\'s dead Jim') console.log('\nHe\'s dead Jim')
watcher.remove() watcher.remove()
}) })
})().catch(err => { })().catch(err => {
console.error('FATAL: UNABLE TO START SYSTEM!\n',err) console.error('FATAL: UNABLE TO START SYSTEM!\n', err)
}) })

View File

@ -1,6 +1,6 @@
{ {
"name": "@uci-utils/watcher", "name": "@uci-utils/watcher",
"version": "0.6.0", "version": "0.6.1",
"description": "File System Watcher Class that emits events", "description": "File System Watcher Class that emits events",
"main": "src/watcher.js", "main": "src/watcher.js",
"type": "module", "type": "module",

View File

@ -13,42 +13,42 @@ let log = {}
const READY_TIMEOUT = 10000 //default const READY_TIMEOUT = 10000 //default
class Watcher extends Emitter { class Watcher extends Emitter {
constructor(opts={}) { constructor(opts = {}) {
super() super()
log = logger({ package:'@uci-utils/watcher', class:'Watcher', file:'src/watcher.js'}) log = logger({ package: '@uci-utils/watcher', class: 'Watcher', file: 'src/watcher.js' })
opts.unlinkDir = 'unlinkDir' in opts ? opts.unlinkDir : true // delete file even on by default opts.unlinkDir = 'unlinkDir' in opts ? opts.unlinkDir : true // delete file even on by default
this.opts = opts this.opts = opts
this.timeout = process.env.READY_TIMEOUT || opts.readyTimeout || READY_TIMEOUT this.timeout = process.env.READY_TIMEOUT || opts.readyTimeout || READY_TIMEOUT
this._ignored = opts.ignored ? (Array.isArray(opts.ignored) ? opts.ignored : opts.ignored.split(',')) : [] this._ignored = opts.ignored ? (Array.isArray(opts.ignored) ? opts.ignored : opts.ignored.split(',')) : []
this.handler = opts.handler || _handler this.handler = opts.handler || _handler
this._ready=false this._ready = false
this._watching=false this._watching = false
return this return this
} }
get watching () { get watching() {
return this._watching return this._watching
} }
get ready () { get ready() {
return this._ready return this._ready
} }
registerHandler(func,opts) { registerHandler(func, opts) {
opts = Object.assign({},this.opts,opts) opts = Object.assign({}, this.opts, opts)
if (!this._watcher) return 'failed: watcher not initialized' if (!this._watcher) return 'failed: watcher not initialized'
if (typeof func ==='function') this.handler = func if (typeof func === 'function') this.handler = func
else if (func) opts = func else if (func) opts = func
opts = Object.assign({},this.opts,opts) opts = Object.assign({}, this.opts, opts)
let handler let handler
if (opts.debounce) if (opts.debounce)
handler = debounce((type, file) => { handler = debounce((type, file) => {
log.debug(`waited : ${opts.debounce}ms before calling handler`) log.debug(`waited : ${opts.debounce}ms before calling handler`)
log.info('debounced handler, only last event is emitted') log.info('debounced handler, only last event is emitted')
this.handler.call(this,type,file) this.handler.call(this, type, file)
},{wait:opts.debounce}) }, { wait: opts.debounce })
else else
handler= (type,file) => {this.handler.call(this,type,file)} handler = (type, file) => { this.handler.call(this, type, file) }
this._watcher.removeAllListeners() this._watcher.removeAllListeners()
@ -62,55 +62,57 @@ class Watcher extends Emitter {
// reset the error event since it got scrubbed // reset the error event since it got scrubbed
this._watcher.on('error', error => { this._watcher.on('error', error => {
const msg ='chokidar watcher error' const msg = 'chokidar watcher error'
log.error({error:error, msg:msg}) log.error({ error: error, msg: msg })
this.emit('error',msg,error) this.emit('error', msg, error)
}) })
} }
async start(opts={}) { async start(opts = {}) {
if(this._watching) { if (this._watching) {
log.warn(`watching aleady running for ${opts.source || this.opts.source}`) log.warn(`watching aleady running for ${opts.source || this.opts.source}`)
return false return false
} }
if (!this._watcher) opts = await this._init(opts) if (!this._watcher) opts = await this._init(opts)
if (this._ready) { if (this._ready) {
this.emit('ready', true, opts) this.emit('ready', true, opts)
log.trace({watching:this._watcher.getWatched(),msg:'initial files watched'}) log.trace({ watching: this._watcher.getWatched(), msg: 'initial files watched' })
log.info(`now watching ${opts.source || this.opts.source}`) log.info(`now watching ${opts.source || this.opts.source}`)
if (this.registerHandler(opts)) { if (this.registerHandler(opts)) {
log.fatal('watcher was not initialzed so could not register handler') log.fatal('watcher was not initialzed so could not register handler')
return new Error('watcher was not initialzed so could not register handler') return new Error('watcher was not initialzed so could not register handler')
} }
this._watching = true this._watching = true
this.emit('watching',true,opts) this.emit('watching', true, opts)
} else { } else {
const msg ='watcher is not ready to start, check options and try again' const msg = 'watcher is not ready to start, check options and try again'
log.fatal(msg) log.fatal(msg)
this.emit('error',msg) this.emit('error', msg)
return new Error('not ready to start check configuration options') return new Error('not ready to start check configuration options')
} }
} }
stop() { stop() {
if(this._watching) { if (this._watching) {
this._watching = false this._watching = false
this._watcher.close() this._watcher.close()
this.emit('watching',false) this.emit('watching', false)
} }
else log.warn('not watching, nothing to stop') else log.warn('not watching, nothing to stop')
} }
remove() { remove() {
this.stop() if (this._watcher) {
this._watcher.removeAllListeners() this.stop()
delete(this._watcher) this._watcher.removeAllListeners()
this._ready=false delete (this._watcher)
this.emit('ready',false) } else log.warn('no watcher started nothing to remove')
this._ready = false
this.emit('ready', false)
} }
async restart(opts,force) { async restart(opts, force) {
if (typeof opts ==='boolean') {force=opts,opts={}} if (typeof opts === 'boolean') { force = opts, opts = {} }
this.stop() this.stop()
await this.start(opts) await this.start(opts)
} }
@ -121,37 +123,37 @@ class Watcher extends Emitter {
// private methods // private methods
async _init(opts={}) { async _init(opts = {}) {
if (!opts.overwrite) { if (!opts.overwrite) {
await this._fetchIgnoreLists(this.opts.excludeFrom) await this._fetchIgnoreLists(this.opts.excludeFrom)
await this._fetchIgnoreLists(this.opts.ignoreList) await this._fetchIgnoreLists(this.opts.ignoreList)
} }
await this._fetchIgnoreLists(opts.excludeFrom,opts.overwrite) await this._fetchIgnoreLists(opts.excludeFrom, opts.overwrite)
await this._fetchIgnoreLists(opts.ignoreList,opts.overwrite) await this._fetchIgnoreLists(opts.ignoreList, opts.overwrite)
if (!opts.ignored) opts.ignored = [] if (!opts.ignored) opts.ignored = []
opts.ignored = Array.isArray(opts.ignored) ? opts.ignored : opts.ignored.split(',') opts.ignored = Array.isArray(opts.ignored) ? opts.ignored : opts.ignored.split(',')
opts.ignored = opts.overwrite ? opts.ignored : [...this._ignored,...opts.ignored] opts.ignored = opts.overwrite ? opts.ignored : [...this._ignored, ...opts.ignored]
opts = Object.assign({},this.opts,opts) // now that ingnore arrays are dealt with merge options opts = Object.assign({}, this.opts, opts) // now that ingnore arrays are dealt with merge options
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
log.debug({options:opts, msg:'intializing watch with options'}) log.debug({ options: opts, msg: 'intializing watch with options' })
if (opts.source) { if (opts.source) {
// create chokidar watcher // create chokidar watcher
this._watcher = watch(opts.source,opts) this._watcher = watch(opts.source, opts)
this._watcher.once('error', error => { this._watcher.once('error', error => {
log.error({error:error, msg:'Watcher error'}) log.error({ error: error, msg: 'Watcher error' })
reject({error:error, msg:'Watcher error'}) reject({ error: error, msg: 'Watcher error' })
}) })
this._watcher.once('ready', () => { this._watcher.once('ready', () => {
clearTimeout(readyTimeout) clearTimeout(readyTimeout)
log.info('initial scan sucessful, ready to start') log.info('initial scan sucessful, ready to start')
this._ready=true this._ready = true
resolve(opts) resolve(opts)
}) })
log.debug(`initial scanning, timeout in ${this.timeout}ms`) log.debug(`initial scanning, timeout in ${this.timeout}ms`)
let readyTimeout = setTimeout(() =>{ let readyTimeout = setTimeout(() => {
log.fatal({options:opts, timeout:this.timeout, msg:'Timeout: unable to complete initial scan'}) log.fatal({ options: opts, timeout: this.timeout, msg: 'Timeout: unable to complete initial scan' })
reject('timeout during initial scan, maybe increase ready timeout') reject('timeout during initial scan, maybe increase ready timeout')
},this.timeout) }, this.timeout)
} }
else { else {
log.fatal('MUST provide a source directory(s) option to watch') log.fatal('MUST provide a source directory(s) option to watch')
@ -160,11 +162,11 @@ class Watcher extends Emitter {
}) })
} }
async _fetchIgnoreLists(lists,overwrite) { async _fetchIgnoreLists(lists, overwrite) {
if (typeof lists === 'string') lists=[lists] if (typeof lists === 'string') lists = [lists]
if (!Array.isArray(lists)) return // no lists if (!Array.isArray(lists)) return // no lists
let ignored = await readIgnoreLists(lists) let ignored = await readIgnoreLists(lists)
this._ignored = overwrite ? ignored : [...this._ignored,...ignored] this._ignored = overwrite ? ignored : [...this._ignored, ...ignored]
} }
@ -172,21 +174,21 @@ class Watcher extends Emitter {
} //end class } //end class
// default handler // default handler
function _handler (type, f) { function _handler(type, f) {
log.debug(`file ${f} was ${type}`) log.debug(`file ${f} was ${type}`)
const fname = path.basename(f) const fname = path.basename(f)
if ( fname.toLowerCase() === 'package.json' && path.dirname(f)=== this.opts.source.replace(/\/$/, '')) if (fname.toLowerCase() === 'package.json' && path.dirname(f) === this.opts.source.replace(/\/$/, ''))
if (type !=='modified') { if (type !== 'modified') {
const msg = `a package.json in root of ${this.opts.source} was added or removed` const msg = `a package.json in root of ${this.opts.source} was added or removed`
log.warning(msg) log.warning(msg)
this.emit('warning',f,msg) this.emit('warning', f, msg)
return return
} else{ } else {
this.emit('install', f) this.emit('install', f)
} }
// user might want to run debounce on the listener for this event // user might want to run debounce on the listener for this event
log.debug({file:f, type:type, msg:'file system changed, emitting'}) log.debug({ file: f, type: type, msg: 'file system changed, emitting' })
this.emit('changed', f,type) this.emit('changed', f, type)
} // end handler } // end handler
export default Watcher export default Watcher