123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- 'use strict';
- var chokidar = require('chokidar');
- var debounce = require('just-debounce');
- var asyncDone = require('async-done');
- var defaults = require('object.defaults/immutable');
- 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) {
- if (typeof ee.listenerCount === 'function') {
- return ee.listenerCount(evtName);
- }
- return ee.listeners(evtName).length;
- }
- function hasErrorListener(ee) {
- return listenerCount(ee, 'error') !== 0;
- }
- function exists(val) {
- return val != null;
- }
- function watch(glob, options, cb) {
- if (typeof options === 'function') {
- cb = options;
- options = {};
- }
- var opt = defaults(options, defaultOpts);
- 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];
- }
- 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)) {
- watcher.emit('error', err);
- }
- // If we have a run queued, start onChange again
- if (queued) {
- queued = false;
- onChange();
- }
- }
- function onChange() {
- if (running) {
- if (opt.queue) {
- queued = true;
- }
- return;
- }
- running = true;
- 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;
- }
- module.exports = watch;
|