Skip to content

Commit

Permalink
dev
Browse files Browse the repository at this point in the history
  • Loading branch information
sttk committed Aug 6, 2022
1 parent e4dd8c5 commit f376686
Show file tree
Hide file tree
Showing 7 changed files with 261 additions and 119 deletions.
128 changes: 18 additions & 110 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,114 +1,32 @@
'use strict';

var chokidar = require('chokidar');
var debounce = require('just-debounce');
var asyncDone = require('async-done');
var isNegatedGlob = require('is-negated-glob');
var anymatch = require('anymatch');
var normalize = require('normalize-path');

var defaultOpts = {
delay: 200,
events: ['add', 'change', 'unlink'],
ignored: [],
ignoreInitial: true,
queue: true,
};

function listenerCount(ee, evtName) {
/* istanbul ignore else */
if (typeof ee.listenerCount === 'function') {
return ee.listenerCount(evtName);
}

/* istanbul ignore next */
return ee.listeners(evtName).length;
}
var chokidar = require('chokidar');
var normalizeArgs = require('./lib/normalize-args');
var debounce = require('./lib/debounce');

function hasErrorListener(ee) {
return listenerCount(ee, 'error') !== 0;
function watch(glob, options, cb) {
return normalizeArgs(glob, options, cb, watchProc);
}

function exists(val) {
return val != null;
function watchProc(glob, options, cb) {
var watcher = chokidar.watch(glob, options);
registerWatchEvent(watcher, options, cb);
return watcher;
}

function watch(glob, options, cb) {
if (typeof options === 'function') {
cb = options;
options = {};
}

var opt = Object.assign({}, defaultOpts, options);

if (!Array.isArray(opt.events)) {
opt.events = [opt.events];
}

if (Array.isArray(glob)) {
// We slice so we don't mutate the passed globs array
glob = glob.slice();
} else {
glob = [glob];
function registerWatchEvent(watcher, opts, cb) {
if (typeof cb !== 'function') {
return;
}

var queued = false;
var running = false;

// These use sparse arrays to keep track of the index in the
// original globs array
var positives = new Array(glob.length);
var negatives = new Array(glob.length);

// Reverse the glob here so we don't end up with a positive
// and negative glob in position 0 after a reverse
glob.reverse().forEach(sortGlobs);

function sortGlobs(globString, index) {
var result = isNegatedGlob(globString);
if (result.negated) {
negatives[index] = result.pattern;
} else {
positives[index] = result.pattern;
}
}

var toWatch = positives.filter(exists);

function joinCwd(glob) {
if (glob && opt.cwd) {
return normalize(opt.cwd + '/' + glob);
}

return glob;
}

// We only do add our custom `ignored` if there are some negative globs
// TODO: I'm not sure how to test this
if (negatives.some(exists)) {
var normalizedPositives = positives.map(joinCwd);
var normalizedNegatives = negatives.map(joinCwd);
var shouldBeIgnored = function(path) {
var positiveMatch = anymatch(normalizedPositives, path, true);
var negativeMatch = anymatch(normalizedNegatives, path, true);
// If negativeMatch is -1, that means it was never negated
if (negativeMatch === -1) {
return false;
}

// If the negative is "less than" the positive, that means
// it came later in the glob array before we reversed them
return negativeMatch < positiveMatch;
};

opt.ignored = [].concat(opt.ignored, shouldBeIgnored);
}
var watcher = chokidar.watch(toWatch, opt);

function runComplete(err) {
running = false;

if (err && hasErrorListener(watcher)) {
if (err && watcher.listenerCount('error') > 0) {
watcher.emit('error', err);
}

Expand All @@ -121,7 +39,7 @@ function watch(glob, options, cb) {

function onChange() {
if (running) {
if (opt.queue) {
if (opts.queue) {
queued = true;
}
return;
Expand All @@ -131,20 +49,10 @@ function watch(glob, options, cb) {
asyncDone(cb, runComplete);
}

var fn;
if (typeof cb === 'function') {
fn = debounce(onChange, opt.delay);
}

function watchEvent(eventName) {
watcher.on(eventName, fn);
}

if (fn) {
opt.events.forEach(watchEvent);
}

return watcher;
var debounced = debounce(onChange, opts.delay);
opts.events.forEach(function(eventName) {
watcher.on(eventName, debounced);
});
}

module.exports = watch;
26 changes: 26 additions & 0 deletions lib/debounce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';

function debounce(fn, delay) {
var timeout;
var args;
var self;

return function() {
self = this;
args = arguments;
clear();
timeout = setTimeout(run, delay);
};

function run() {
clear();
fn.apply(self, args);
}

function clear() {
clearTimeout(timeout);
timeout = null;
}
}

module.exports = debounce;
33 changes: 33 additions & 0 deletions lib/normalize-args.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
'use strict';

var defaultOpts = {
delay: 200,
events: ['add', 'change', 'unlink'],
ignored: [],
ignoreInitial: true,
queue: true,
};

function normalizeArgs(glob, options, cb, next) {
if (typeof options === 'function') {
cb = options;
options = {};
}

var opts = Object.assign({}, defaultOpts, options);

if (!Array.isArray(opts.events)) {
opts.events = [opts.events];
}

if (Array.isArray(glob)) {
// We slice so we don't mutate the passed globs array
glob = glob.slice();
} else {
glob = [glob];
}

return next(glob, opts, cb);
}

module.exports = normalizeArgs;
8 changes: 2 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,11 @@
"scripts": {
"lint": "eslint .",
"pretest": "npm run lint",
"test": "nyc mocha --async-only"
"test": "nyc mocha test test/lib --async-only"
},
"dependencies": {
"anymatch": "^3.1.2",
"async-done": "^2.0.0",
"chokidar": "^3.5.3",
"is-negated-glob": "^1.0.0",
"just-debounce": "^1.1.0",
"normalize-path": "^3.0.0"
"chokidar": "^3.5.3"
},
"devDependencies": {
"eslint": "^7.32.0",
Expand Down
8 changes: 5 additions & 3 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ var expect = require('expect');
var sinon = require('sinon');
var rimraf = require('rimraf');
var through = require('through2');
var normalizePath = require('normalize-path');
//var normalizePath = require('normalize-path');

var watch = require('../');

Expand All @@ -22,8 +22,10 @@ describe('glob-watcher', function() {
var outFile1 = path.join(outDir, 'changed.js');
var outFile2 = path.join(outDir, 'added.js');
var globPattern = '**/*.js';
var outGlob = normalizePath(path.join(outDir, globPattern));
var singleAdd = normalizePath(path.join(outDir, 'changed.js'));
//var outGlob = normalizePath(path.join(outDir, globPattern));
//var singleAdd = normalizePath(path.join(outDir, 'changed.js'));
var outGlob = path.join(outDir, globPattern);
var singleAdd = path.join(outDir, 'changed.js');
var ignoreGlob = '!' + singleAdd;

function changeFile() {
Expand Down
90 changes: 90 additions & 0 deletions test/lib/debounce.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
'use strict';

var expect = require('expect');
var sinon = require('sinon');
var debounce = require('../../lib/debounce');

describe('lib/debounce', function() {

it('should call an original function with specified delay', function(done) {
expect.assertions(1);

var executed = false;
var fn = debounce(function() {
executed = true;
}, 10);

fn();

expect(executed).toBeFalsy();

setTimeout(function() {
expect(executed).toBeTruthy();
done();
}, 11);
});

it('should extend delay against multiple calls', function(done) {
expect.assertions(1);

var fn = debounce(function(a) {
expect(a).toBe(3);
done();
}, 50);

fn(1);
fn(2);

setTimeout(function() {
fn(3);
}, 3);
});

it('should extends delay if a preceding call doesn\'t run yet', function(done) {
expect.assertions(1);

var fn = debounce(function(a) {
expect(a).toBe(3);
done();
}, 10);

fn(1);

setTimeout(function() {
fn(2);

setTimeout(function() {
fn(3);
}, 5);
}, 5);
});

it('should run if a preceding call already run', function(done) {
expect.assertions(2);

var spy = sinon.spy(function(a) {
switch (spy.callCount) {
case 1:
expect(a).toBe(2);
break;
case 2:
expect(a).toBe(3);
done();
break;
default:
throw new Error();
}
});
var fn = debounce(spy, 6);

fn(1);

setTimeout(function() {
fn(2);

setTimeout(function() {
fn(3);
}, 10);
}, 3);
});
});
Loading

0 comments on commit f376686

Please sign in to comment.