From 1900dc5ba2fab311620037a4209375e2c663f478 Mon Sep 17 00:00:00 2001 From: David Kebler Date: Sat, 16 Feb 2019 11:40:48 -0800 Subject: [PATCH] 0.2.2 setup for test working but needs to include listener for change --- .eslintrc.js | 37 ++++++++++++ .gitignore | 2 + .npmignore | 4 ++ package.json | 2 +- readme.md | 2 + src/watcher.js | 134 +++++++++++++++++++++++++++++++++++++++++ test/repo/.gitignore | 1 + test/repo/.npmignore | 5 ++ test/repo/dontwatch.js | 1 + test/watcher.test.js | 47 +++++++++++++++ 10 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 .eslintrc.js create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 readme.md create mode 100644 src/watcher.js create mode 100644 test/repo/.gitignore create mode 100644 test/repo/.npmignore create mode 100644 test/repo/dontwatch.js create mode 100644 test/watcher.test.js diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..49bac18 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,37 @@ +module.exports = { + "ecmaFeatures": { + "modules": true, + "spread" : true, + "restParams" : true + }, + // "plugins": [ + // "unicorn" + // ], + "env": { + "es6": true, + "node": true, + "mocha": true + }, + "parserOptions": { + "ecmaVersion": 2017, + "sourceType": "module" + }, + "extends": "eslint:recommended", + "rules": { + "indent": [ + "error", + 2 + ], + // "unicorn/no-array-instanceof": "error", + "no-console": 0, + "semi": ["error", "never"], + "linebreak-style": [ + "error", + "unix" + ], + "quotes": [ + "error", + "single" + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e61051f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/node_modules/ +/coverage/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..f16fc41 --- /dev/null +++ b/.npmignore @@ -0,0 +1,4 @@ +tests/ +test/ +*.test.js +testing/ diff --git a/package.json b/package.json index de01a73..b715036 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@uci-utils/watcher", - "version": "0.2.1", + "version": "0.2.2", "description": "File System Watcher Class that emits events", "main": "src/watcher.js", "scripts": { diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..19f188f --- /dev/null +++ b/readme.md @@ -0,0 +1,2 @@ +### File System Watcher Class +#### a uCOMmandIt Utiltiy Function diff --git a/src/watcher.js b/src/watcher.js new file mode 100644 index 0000000..0bd9253 --- /dev/null +++ b/src/watcher.js @@ -0,0 +1,134 @@ +// native imports +import { EventEmitter as Emitter } from 'events' +import path from 'path' +// third party imports +import { watch } from 'chokidar' +// UCI imports +import ignores from '@uci-utils/read-lines' +import logger from '@uci-utils/logger' + +let log = {} + +const READY_TIMEOUT = 2000 + +class Watcher extends Emitter { + constructor(opts={}) { + super() + log = logger({ package:'@uci/sync', class:'Watcher', file:'src/watcher.js'}) + opts.unlinkDir = Object.hasOwnProperty(opts.unlinkDir) ? opts.unlinkDir : true + this.opts = opts + this._ignored = [] + this._ready=false + this.watching=false + return this + } + + async init(opts) { + return new Promise(async (resolve, reject) => { + opts = opts || this.opts + log.debug({options:opts, msg:'intializing watch with options'}) + if (opts.excludeFrom) await this.getIgnoreLists(opts.excludeFrom) + if (opts.ignoreList) await this.getIgnoreLists(opts.ignoreList) + if (opts.source) { + opts.ignored = opts.ignored ? [...this._ignored,...opts.ignored] : this._ignored + log.debug({ignored:opts.ignored, msg:'all ignores'}) + this._watcher = watch(opts.source,opts) + this._watcher.on('error', error => { + log.error({error:error, msg:'Watcher error'}) + }) + this._watcher.on('ready', () => { + clearTimeout(readyTimeout) + log.info('initial scan sucessful, ready to start') + this._ready=true + this.opts = opts // save options + resolve() + }) + let readyTimeout = setTimeout(() =>{ + log.fatal({options:opts, timeout:READY_TIMEOUT, msg:'Timeout: unabled to complete initial scan'}) + reject('timeout during intial scan') + },READY_TIMEOUT) + } + else { + log.fatal('MUST provide a source directory(s) option to watch') + reject('no source provided') + } + }) + } + + + async start(opts) { + if(this.watching) { + log.warn(`watching aleady running for ${this.opts.source}`) + return false + } + if (!this._watcher) await this.init(opts) + if (this._ready) { + log.info(`now watching ${this.opts.source}`) + this._watcher.removeAllListeners() // just in case + this.watching = true + // define command listen handler + const handler = + (type, f) => { + log.debug(`file ${f} was ${type}`) + // convert this to a plugin/hook so it's not specific + const fname = path.basename(f) + if ( fname.toLowerCase() === 'package.json') + if (type !=='modified') { + this.emit('error',new Error('package.json was added or removed, ignoring sync and reinstall')) + return + } else{ + this.emit('install', f) + } + // user might want to run debounce on the listener for this event + this.emit('changed', {file:f, type:type}) + } // end handler + + this._watcher + .on('add', handler.bind(this, 'added')) + .on('change', handler.bind(this, 'modified')) + .on('unlink', handler.bind(this, 'removed')) + if(this.opts.unlinkDir) this._watcher.on('unlinkDir', handler.bind(this, 'dir-deleted')) + if(this.opts.addDir) this._watcher.on('addDir', handler.bind(this, 'dir-added')) + } else { + log.warn('watcher is not ready to start, check options and try again') + return new Error('not ready to start check configuration') + } + } + + stop() { + if(this.watching) { + this.watching = false + this._watcher.close() + } + else log.warn('not watching, nothing to close') + } + + remove() { + this.stop() + delete(this._watcher) + this._ready=false + } + + async restart(opts) { + this.remove() + await this.start(opts) + } + + async getIgnoreLists(lists) { + // console.log('ignore lists', lists) + if (typeof lists === 'string') lists=[lists] + let ignored = await ignores(lists) + // console.log('==ignores from lists', ignored) + this._ignored = [...this._ignored,...ignored] + // console.log(this._ignored) + } + + addIgnore(ignore) { + Array.isArray(ignore) ? this._ignored.push(...ignore) : this._ignored.push(ignore) + } + clearAllIgnore() { this._ignored = [] } + +} + +export default Watcher +export { Watcher } diff --git a/test/repo/.gitignore b/test/repo/.gitignore new file mode 100644 index 0000000..0979a6c --- /dev/null +++ b/test/repo/.gitignore @@ -0,0 +1 @@ +**/node_modules/** diff --git a/test/repo/.npmignore b/test/repo/.npmignore new file mode 100644 index 0000000..02078d5 --- /dev/null +++ b/test/repo/.npmignore @@ -0,0 +1,5 @@ +tests/ +test/ +*.test.js +testing/ +example/ diff --git a/test/repo/dontwatch.js b/test/repo/dontwatch.js new file mode 100644 index 0000000..bff63ba --- /dev/null +++ b/test/repo/dontwatch.js @@ -0,0 +1 @@ +this was modified some moreddddddd diff --git a/test/watcher.test.js b/test/watcher.test.js new file mode 100644 index 0000000..62e5ba1 --- /dev/null +++ b/test/watcher.test.js @@ -0,0 +1,47 @@ +import Watcher from '../src/watcher' +// import to from 'await-to-js' +import { expect } from 'chai' +import { it } from 'mocha' + +describe('Watcher Class Testing ',async ()=> { + + // let log = logger({}) + before(async () => { + // log = logger({ package:'@uci/sync', id: 'sync-test' }) + let options = {source:'./test/repo', ignored:['**/dontwatch.js'],ignoreList:['./test/repo/.gitignore']} + let watcher = new Watcher(options) + await watcher.start() + watcher.on('changed', + // debounce( + (change) => { + console.log(`======= file ${change.file} was ${change.type} ==========`) + } + ) + + }) + + + it('can watch for file delete' , async function () { + // let [err,res] = await to(remote.exec('cd /opt && pwd')) + // if (err) { + // log.info('error running command aborting test', err) + // return + // } + // log.info(`result of remote command ${res.command} => ${res.reply.toString().trim()}`) + expect('test', 'test failed').to.equal('test') + }) + +}) + +// function hooks(remote) { +// + +// // beforeEach(async() => { +// // await someasyncfunctiontodobeforeeachtest() +// // }) +// +// // after(async() => { +// // await someasyncfunctiontodoaftereeachtest() +// // }) +// +// }