Skip to content

Commit b274b50

Browse files
James Quigleyphated
authored andcommitted
Fix: Match glob handling to vinyl-fs (fixes gulpjs/gulp#2192)
1 parent 10d7de9 commit b274b50

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

index.js

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ var chokidar = require('chokidar');
44
var debounce = require('just-debounce');
55
var asyncDone = require('async-done');
66
var defaults = require('object.defaults/immutable');
7+
var isNegatedGlob = require('is-negated-glob');
8+
var anymatch = require('anymatch');
79

810
var defaultOpts = {
911
delay: 200,
@@ -24,6 +26,10 @@ function hasErrorListener(ee) {
2426
return listenerCount(ee, 'error') !== 0;
2527
}
2628

29+
function exists(val) {
30+
return val != null;
31+
}
32+
2733
function watch(glob, options, cb) {
2834
if (typeof options === 'function') {
2935
cb = options;
@@ -36,10 +42,51 @@ function watch(glob, options, cb) {
3642
opt.events = [opt.events];
3743
}
3844

45+
if (Array.isArray(glob)) {
46+
// We slice so we don't mutate the passed globs array
47+
glob = glob.slice();
48+
} else {
49+
glob = [glob];
50+
}
51+
3952
var queued = false;
4053
var running = false;
4154

42-
var watcher = chokidar.watch(glob, opt);
55+
// These use sparse arrays to keep track of the index in the
56+
// original globs array
57+
var positives = new Array(glob.length);
58+
var negatives = new Array(glob.length);
59+
60+
// Reverse the glob here so we don't end up with a positive
61+
// and negative glob in position 0 after a reverse
62+
glob.reverse().forEach(sortGlobs);
63+
64+
function sortGlobs(globString, index) {
65+
var result = isNegatedGlob(globString);
66+
if (result.negated) {
67+
negatives[index] = result.pattern;
68+
} else {
69+
positives[index] = result.pattern;
70+
}
71+
}
72+
73+
function shouldBeIgnored(path) {
74+
var positiveMatch = anymatch(positives, path, true);
75+
var negativeMatch = anymatch(negatives, path, true);
76+
// If negativeMatch is -1, that means it was never negated
77+
if (negativeMatch === -1) {
78+
return false;
79+
}
80+
81+
// If the negative is "less than" the positive, that means
82+
// it came later in the glob array before we reversed them
83+
return negativeMatch < positiveMatch;
84+
}
85+
86+
var toWatch = positives.filter(exists);
87+
88+
opt.ignored = shouldBeIgnored;
89+
var watcher = chokidar.watch(toWatch, opt);
4390

4491
function runComplete(err) {
4592
running = false;

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
"coveralls": "npm run cover && istanbul-coveralls"
2222
},
2323
"dependencies": {
24+
"anymatch": "^2.0.0",
2425
"async-done": "^1.2.0",
2526
"chokidar": "^2.0.0",
27+
"is-negated-glob": "^1.0.0",
2628
"just-debounce": "^1.0.0",
2729
"object.defaults": "^1.1.0"
2830
},

test/index.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ describe('glob-watcher', function() {
2222
var outFile2 = path.join(outDir, 'added.js');
2323
var globPattern = '**/*.js';
2424
var outGlob = normalizePath(path.join(outDir, globPattern));
25+
var singleAdd = normalizePath(path.join(outDir, 'changed.js'));
26+
var ignoreGlob = '!' + singleAdd;
2527

2628
function changeFile() {
2729
fs.writeFileSync(outFile1, 'hello changed');
@@ -279,4 +281,42 @@ describe('glob-watcher', function() {
279281
setTimeout(done, 500);
280282
});
281283
});
284+
285+
it('can ignore a glob after it has been added', function(done) {
286+
watcher = watch([outGlob, ignoreGlob]);
287+
288+
watcher.once('change', function(filepath) {
289+
// It should never reach here
290+
expect(filepath).toNotExist();
291+
done();
292+
});
293+
294+
// We default `ignoreInitial` to true, so always wait for `on('ready')`
295+
watcher.on('ready', changeFile);
296+
297+
setTimeout(done, 1500);
298+
});
299+
300+
it('can re-add a glob after it has been negated', function(done) {
301+
watcher = watch([outGlob, ignoreGlob, singleAdd]);
302+
303+
watcher.once('change', function(filepath) {
304+
expect(filepath).toEqual(singleAdd);
305+
done();
306+
});
307+
308+
// We default `ignoreInitial` to true, so always wait for `on('ready')`
309+
watcher.on('ready', changeFile);
310+
});
311+
312+
it('does not mutate the globs array', function(done) {
313+
var globs = [outGlob, ignoreGlob, singleAdd];
314+
watcher = watch(globs);
315+
316+
expect(globs[0]).toEqual(outGlob);
317+
expect(globs[1]).toEqual(ignoreGlob);
318+
expect(globs[2]).toEqual(singleAdd);
319+
320+
done();
321+
});
282322
});

0 commit comments

Comments
 (0)