<commit_before>/*
 * gaze
 * https://github.com/shama/gaze
 *
 * Copyright (c) 2014 Kyle Robinson Young
 * Licensed under the MIT license.
 */

'use strict';

// libs
var util = require('util');
var EE = require('events').EventEmitter;
var fs = require('fs');
var path = require('path');
var globule = require('globule');
var nextback = require('nextback');
var helper = require('./helper');
var platform = require('./platform');

// keep track of instances to call multiple times for backwards compatibility
var instances = [];

// `Gaze` EventEmitter object to return in the callback
function Gaze(patterns, opts, done) {
  var self = this;
  EE.call(self);

  // If second arg is the callback
  if (typeof opts === 'function') {
    done = opts;
    opts = {};
  }

  // Default options
  opts = opts || {};
  opts.mark = true;
  opts.interval = opts.interval || 500;
  opts.debounceDelay = opts.debounceDelay || 500;
  opts.cwd = opts.cwd || process.cwd();
  this.options = opts;

  // File watching mode to use when adding files to the platform
  this._mode = opts.mode || 'auto';

  // Default done callback
  done = done || function() {};

  // Remember our watched dir:files
  this._watched = Object.create(null);

  // Store watchers
  this._watchers = Object.create(null);

  // Store watchFile listeners
  this._pollers = Object.create(null);

  // Store patterns
  this._patterns = [];

  // Cached events for debouncing
  this._cached = Object.create(null);

  // Set maxListeners
  if (this.options.maxListeners) {
    this.setMaxListeners(this.options.maxListeners);
    Gaze.super_.prototype.setMaxListeners(this.options.maxListeners);
    delete this.options.maxListeners;
  }

  // Initialize the watch on files
  if (patterns) {
    this.add(patterns, done);
  }

  // keep the process alive
  this._keepalive = setInterval(platform.tick.bind(platform), opts.interval);

  // Keep track of all instances created
  this._instanceNum = instances.length;
  instances.push(this);

  // Keep track of safewriting and debounce timeouts
  this._safewriting = null;
  this._safewriteTimeout = null;
  this._timeoutId = null;

  return this;
}
util.inherits(Gaze, EE);

// Main entry point. Start watching and call done when setup
module.exports = function gaze(patterns, opts, done) {
  return new Gaze(patterns, opts, done);
};
module.exports.Gaze = Gaze;

// Override the emit function to emit `all` events
// and debounce on duplicate events per file
Gaze.prototype.emit = function() {
  var self = this;
  var args = arguments;

  var e = args[0];
  var filepath = args[1];

  // If not added/deleted/changed/renamed then just emit the event
  if (e.slice(-2) !== 'ed') {
    Gaze.super_.prototype.emit.apply(self, args);
    return this;
  }

  // Detect rename event, if added and previous deleted is in the cache
  if (e === 'added') {
    Object.keys(this._cached).forEach(function(oldFile) {
      if (self._cached[oldFile].indexOf('deleted') !== -1) {
        args[0] = e = 'renamed';
        [].push.call(args, oldFile);
        delete self._cached[oldFile];
        return false;
      }
    });
  }

  // Detect safewrite events, if file is deleted and then added/renamed, assume a safewrite happened
  if (e === 'deleted' && this._safewriting == null) {
    this._safewriting = filepath;
    this._safewriteTimeout = setTimeout(function() {
      // Just a normal delete, carry on
      Gaze.super_.prototype.emit.apply(self, args);
      Gaze.super_.prototype.emit.apply(self, ['all', e].concat([].slice.call(args, 1)));
      self._safewriting = null;
    }, this.options.debounceDelay);
    return this;
  } else if ((e === 'added' || e === 'renamed') && this._safewriting === filepath) {
    clearTimeout(this._safewriteTimeout);
    this._safewriteTimeout = setTimeout(function() {
      self._safewriting = null;
    }, this.options.debounceDelay);
    args[0] = e = 'changed';
  } else if (e === 'deleted' && this._safewriting === filepath) {
    return this;
  }

  // If cached doesnt exist, create a delay before running the next
  // then emit the event
  var cache = this._cached[filepath] || [];
  if (cache.indexOf(e) === -1) {
    helper.objectPush(self._cached, filepath, e);
    clearTimeout(this._timeoutId);
    this._timeoutId = setTimeout(function() {
      delete self._cached[filepath];
    }, this.options.debounceDelay);
    // Emit the event and `all` event
    Gaze.super_.prototype.emit.apply(self, args);
    Gaze.super_.prototype.emit.apply(self, ['all', e].concat([].slice.call(args, 1)));
  }

  // Detect if new folder added to trigger for matching files within folder
  if (e === 'added') {
    if (helper.isDir(filepath)) {
      fs.readdirSync(filepath).map(function(file) {
        return path.join(filepath, file);
      }).filter(function(file) {
        return globule.isMatch(self._patterns, file, self.options);
      }).forEach(function(file) {
        self.emit('added', file);
      });
    }
  }

  return this;
};

// Close watchers
Gaze.prototype.close = function(_reset) {
  instances.splice(this._instanceNum, 1);
  platform.closeAll();
  this.emit('end');
};

// Add file patterns to be watched
Gaze.prototype.add = function(files, done) {
  var self = this;
  if (typeof files === 'string') { files = [files]; }
  this._patterns = helper.unique.apply(null, [this._patterns, files]);
  files = globule.find(this._patterns, this.options);

  // If no matching files
  if (files.length < 1) {
    // Defer to emitting to give a chance to attach event handlers.
    nextback(function() {
      self.emit('ready', self);
      if (done) done.call(self, null, self);
      self.emit('nomatch');
    })();
    return;
  }

  // Set the platform mode before adding files
  platform.mode = self._mode;

  helper.forEachSeries(files, function(file, next) {
    self._platform(path.join(self.options.cwd, file));
    next();
  }, function() {
    // A little delay here for backwards compatibility, lol
    setTimeout(function() {
      self.emit('ready', self);
      if (done) done.call(self, null, self);
    }, 10);
  });
};

// Wrapper for adding files to the platform
Gaze.prototype._platform = function(file) {
  try {
    platform(file, this._trigger.bind(this));
  } catch (err) {
    this.emit('error', err);
  }
};

// Call when the platform has triggered
Gaze.prototype._trigger = function(error, event, filepath, newFile) {
  if (error) { return this.emit('error', error); }

  // Set the platform mode before adding files
  platform.mode = this._mode;

  if (event === 'change' && helper.isDir(filepath)) {
    this._wasAdded(filepath);
  } else if (event === 'change') {
    this._emitAll('changed', filepath);
  } else if (event === 'delete') {
    // Close out deleted filepaths (important to make safewrite detection work)
    platform.close(filepath);
    this._emitAll('deleted', filepath);
  } else if (event === 'rename') {
    // TODO: This occasionally throws, figure out why or use the old style rename detect
    // The handle(26) returned by watching [filename] is the same with an already watched path([filename])
    this._emitAll('renamed', newFile, filepath);
  }
};

// If a folder received a change event, investigate
Gaze.prototype._wasAdded = function(dir) {
  var self = this;
  var dirstat = fs.statSync(dir);
  fs.readdir(dir, function(err, current) {
    helper.forEachSeries(current, function(file, next) {
      var filepath = path.join(dir, file);
      if (!fs.existsSync(filepath)) return next();
      var stat = fs.lstatSync(filepath);
      if ((dirstat.mtime - stat.mtime) <= 0) {
        var relpath = path.relative(self.options.cwd, filepath);
        if (stat.isDirectory()) {
          // If it was a dir, watch the dir and emit that it was added
          self._platform(filepath);
          self._emitAll('added', filepath);
          self._wasAddedSub(filepath);
        } else if (globule.isMatch(self._patterns, relpath, self.options)) {
          // Otherwise if the file matches, emit added
          self._platform(filepath);
          self._emitAll('added', filepath);
        }
      }
      next();
    });
  });
};

// If a sub folder was added, investigate further
// Such as with grunt.file.write('new_dir/tmp.js', '') as it will create the folder and file simultaneously
Gaze.prototype._wasAddedSub = function(dir) {
  var self = this;
  fs.readdir(dir, function(err, current) {
    helper.forEachSeries(current, function(file, next) {
      var filepath = path.join(dir, file);
      var relpath = path.relative(self.options.cwd, filepath);
      try {
        if (fs.lstatSync(filepath).isDirectory()) {
          self._wasAdded(filepath);
        } else if (globule.isMatch(self._patterns, relpath, self.options)) {
          // Make sure to watch the newly added sub file
          self._platform(filepath);
          self._emitAll('added', filepath);
        }
      } catch (err) { /* ignore error if ENOENT */ }
      next();
    });
  });
};

// Wrapper for emit to ensure we emit on all instances
Gaze.prototype._emitAll = function() {
  var args = Array.prototype.slice.call(arguments);
  for (var i = 0; i < instances.length; i++) {
    instances[i].emit.apply(instances[i], args);
  }
};

// Remove file/dir from `watched`
Gaze.prototype.remove = function(file) {
  platform.close(file);
  return this;
};

// Return watched files
Gaze.prototype.watched = function(done) {
  done = nextback(done || function() {});
  helper.flatToTree(platform.getWatchedPaths(), this.options.cwd, false, done);
  return this;
};

// Returns `watched` files with relative paths to cwd
Gaze.prototype.relative = function(dir, unixify, done) {
  if (typeof dir === 'function') {
    done = dir;
    dir = null;
    unixify = false;
  }
  if (typeof unixify === 'function') {
    done = unixify;
    unixify = false;
  }
  done = nextback(done || function() {});
  helper.flatToTree(platform.getWatchedPaths(), this.options.cwd, true, unixify, function(err, relative) {
    if (dir) {
      if (unixify) {
        dir = helper.unixifyPathSep(dir);
      }
      // Better guess what to return for backwards compatibility
      return done(null, relative[dir] || relative[dir + (unixify ? '/' : path.sep)] || []);
    }
    return done(null, relative);
  });
  return this;
};
<commit_msg>Dont completely ignore the error<commit_after>/*
 * gaze
 * https://github.com/shama/gaze
 *
 * Copyright (c) 2014 Kyle Robinson Young
 * Licensed under the MIT license.
 */

'use strict';

// libs
var util = require('util');
var EE = require('events').EventEmitter;
var fs = require('fs');
var path = require('path');
var globule = require('globule');
var nextback = require('nextback');
var helper = require('./helper');
var platform = require('./platform');

// keep track of instances to call multiple times for backwards compatibility
var instances = [];

// `Gaze` EventEmitter object to return in the callback
function Gaze(patterns, opts, done) {
  var self = this;
  EE.call(self);

  // If second arg is the callback
  if (typeof opts === 'function') {
    done = opts;
    opts = {};
  }

  // Default options
  opts = opts || {};
  opts.mark = true;
  opts.interval = opts.interval || 500;
  opts.debounceDelay = opts.debounceDelay || 500;
  opts.cwd = opts.cwd || process.cwd();
  this.options = opts;

  // File watching mode to use when adding files to the platform
  this._mode = opts.mode || 'auto';

  // Default done callback
  done = done || function() {};

  // Remember our watched dir:files
  this._watched = Object.create(null);

  // Store watchers
  this._watchers = Object.create(null);

  // Store watchFile listeners
  this._pollers = Object.create(null);

  // Store patterns
  this._patterns = [];

  // Cached events for debouncing
  this._cached = Object.create(null);

  // Set maxListeners
  if (this.options.maxListeners) {
    this.setMaxListeners(this.options.maxListeners);
    Gaze.super_.prototype.setMaxListeners(this.options.maxListeners);
    delete this.options.maxListeners;
  }

  // Initialize the watch on files
  if (patterns) {
    this.add(patterns, done);
  }

  // keep the process alive
  this._keepalive = setInterval(platform.tick.bind(platform), opts.interval);

  // Keep track of all instances created
  this._instanceNum = instances.length;
  instances.push(this);

  // Keep track of safewriting and debounce timeouts
  this._safewriting = null;
  this._safewriteTimeout = null;
  this._timeoutId = null;

  return this;
}
util.inherits(Gaze, EE);

// Main entry point. Start watching and call done when setup
module.exports = function gaze(patterns, opts, done) {
  return new Gaze(patterns, opts, done);
};
module.exports.Gaze = Gaze;

// Override the emit function to emit `all` events
// and debounce on duplicate events per file
Gaze.prototype.emit = function() {
  var self = this;
  var args = arguments;

  var e = args[0];
  var filepath = args[1];

  // If not added/deleted/changed/renamed then just emit the event
  if (e.slice(-2) !== 'ed') {
    Gaze.super_.prototype.emit.apply(self, args);
    return this;
  }

  // Detect rename event, if added and previous deleted is in the cache
  if (e === 'added') {
    Object.keys(this._cached).forEach(function(oldFile) {
      if (self._cached[oldFile].indexOf('deleted') !== -1) {
        args[0] = e = 'renamed';
        [].push.call(args, oldFile);
        delete self._cached[oldFile];
        return false;
      }
    });
  }

  // Detect safewrite events, if file is deleted and then added/renamed, assume a safewrite happened
  if (e === 'deleted' && this._safewriting == null) {
    this._safewriting = filepath;
    this._safewriteTimeout = setTimeout(function() {
      // Just a normal delete, carry on
      Gaze.super_.prototype.emit.apply(self, args);
      Gaze.super_.prototype.emit.apply(self, ['all', e].concat([].slice.call(args, 1)));
      self._safewriting = null;
    }, this.options.debounceDelay);
    return this;
  } else if ((e === 'added' || e === 'renamed') && this._safewriting === filepath) {
    clearTimeout(this._safewriteTimeout);
    this._safewriteTimeout = setTimeout(function() {
      self._safewriting = null;
    }, this.options.debounceDelay);
    args[0] = e = 'changed';
  } else if (e === 'deleted' && this._safewriting === filepath) {
    return this;
  }

  // If cached doesnt exist, create a delay before running the next
  // then emit the event
  var cache = this._cached[filepath] || [];
  if (cache.indexOf(e) === -1) {
    helper.objectPush(self._cached, filepath, e);
    clearTimeout(this._timeoutId);
    this._timeoutId = setTimeout(function() {
      delete self._cached[filepath];
    }, this.options.debounceDelay);
    // Emit the event and `all` event
    Gaze.super_.prototype.emit.apply(self, args);
    Gaze.super_.prototype.emit.apply(self, ['all', e].concat([].slice.call(args, 1)));
  }

  // Detect if new folder added to trigger for matching files within folder
  if (e === 'added') {
    if (helper.isDir(filepath)) {
      fs.readdirSync(filepath).map(function(file) {
        return path.join(filepath, file);
      }).filter(function(file) {
        return globule.isMatch(self._patterns, file, self.options);
      }).forEach(function(file) {
        self.emit('added', file);
      });
    }
  }

  return this;
};

// Close watchers
Gaze.prototype.close = function(_reset) {
  instances.splice(this._instanceNum, 1);
  platform.closeAll();
  this.emit('end');
};

// Add file patterns to be watched
Gaze.prototype.add = function(files, done) {
  var self = this;
  if (typeof files === 'string') { files = [files]; }
  this._patterns = helper.unique.apply(null, [this._patterns, files]);
  files = globule.find(this._patterns, this.options);

  // If no matching files
  if (files.length < 1) {
    // Defer to emitting to give a chance to attach event handlers.
    nextback(function() {
      self.emit('ready', self);
      if (done) done.call(self, null, self);
      self.emit('nomatch');
    })();
    return;
  }

  // Set the platform mode before adding files
  platform.mode = self._mode;

  helper.forEachSeries(files, function(file, next) {
    self._platform(path.join(self.options.cwd, file));
    next();
  }, function() {
    // A little delay here for backwards compatibility, lol
    setTimeout(function() {
      self.emit('ready', self);
      if (done) done.call(self, null, self);
    }, 10);
  });
};

// Wrapper for adding files to the platform
Gaze.prototype._platform = function(file) {
  try {
    platform(file, this._trigger.bind(this));
  } catch (err) {
    this.emit('error', err);
  }
};

// Call when the platform has triggered
Gaze.prototype._trigger = function(error, event, filepath, newFile) {
  if (error) { return this.emit('error', error); }

  // Set the platform mode before adding files
  platform.mode = this._mode;

  if (event === 'change' && helper.isDir(filepath)) {
    this._wasAdded(filepath);
  } else if (event === 'change') {
    this._emitAll('changed', filepath);
  } else if (event === 'delete') {
    // Close out deleted filepaths (important to make safewrite detection work)
    platform.close(filepath);
    this._emitAll('deleted', filepath);
  } else if (event === 'rename') {
    // TODO: This occasionally throws, figure out why or use the old style rename detect
    // The handle(26) returned by watching [filename] is the same with an already watched path([filename])
    this._emitAll('renamed', newFile, filepath);
  }
};

// If a folder received a change event, investigate
Gaze.prototype._wasAdded = function(dir) {
  var self = this;
  var dirstat = fs.statSync(dir);
  fs.readdir(dir, function(err, current) {
    helper.forEachSeries(current, function(file, next) {
      var filepath = path.join(dir, file);
      if (!fs.existsSync(filepath)) return next();
      var stat = fs.lstatSync(filepath);
      if ((dirstat.mtime - stat.mtime) <= 0) {
        var relpath = path.relative(self.options.cwd, filepath);
        if (stat.isDirectory()) {
          // If it was a dir, watch the dir and emit that it was added
          self._platform(filepath);
          self._emitAll('added', filepath);
          self._wasAddedSub(filepath);
        } else if (globule.isMatch(self._patterns, relpath, self.options)) {
          // Otherwise if the file matches, emit added
          self._platform(filepath);
          self._emitAll('added', filepath);
        }
      }
      next();
    });
  });
};

// If a sub folder was added, investigate further
// Such as with grunt.file.write('new_dir/tmp.js', '') as it will create the folder and file simultaneously
Gaze.prototype._wasAddedSub = function(dir) {
  var self = this;
  fs.readdir(dir, function(err, current) {
    helper.forEachSeries(current, function(file, next) {
      var filepath = path.join(dir, file);
      var relpath = path.relative(self.options.cwd, filepath);
      try {
        if (fs.lstatSync(filepath).isDirectory()) {
          self._wasAdded(filepath);
        } else if (globule.isMatch(self._patterns, relpath, self.options)) {
          // Make sure to watch the newly added sub file
          self._platform(filepath);
          self._emitAll('added', filepath);
        }
      } catch (err) {
        self.emit('error', err);
      }
      next();
    });
  });
};

// Wrapper for emit to ensure we emit on all instances
Gaze.prototype._emitAll = function() {
  var args = Array.prototype.slice.call(arguments);
  for (var i = 0; i < instances.length; i++) {
    instances[i].emit.apply(instances[i], args);
  }
};

// Remove file/dir from `watched`
Gaze.prototype.remove = function(file) {
  platform.close(file);
  return this;
};

// Return watched files
Gaze.prototype.watched = function(done) {
  done = nextback(done || function() {});
  helper.flatToTree(platform.getWatchedPaths(), this.options.cwd, false, done);
  return this;
};

// Returns `watched` files with relative paths to cwd
Gaze.prototype.relative = function(dir, unixify, done) {
  if (typeof dir === 'function') {
    done = dir;
    dir = null;
    unixify = false;
  }
  if (typeof unixify === 'function') {
    done = unixify;
    unixify = false;
  }
  done = nextback(done || function() {});
  helper.flatToTree(platform.getWatchedPaths(), this.options.cwd, true, unixify, function(err, relative) {
    if (dir) {
      if (unixify) {
        dir = helper.unixifyPathSep(dir);
      }
      // Better guess what to return for backwards compatibility
      return done(null, relative[dir] || relative[dir + (unixify ? '/' : path.sep)] || []);
    }
    return done(null, relative);
  });
  return this;
};
<|endoftext|>


<commit_before>/*
 * gaze
 * https://github.com/shama/gaze
 *
 * Copyright (c) 2013 Kyle Robinson Young
 * Licensed under the MIT license.
 */

'use strict';

// libs
var util = require('util');
var EE = require('events').EventEmitter;
var fs = require('fs');
var path = require('path');
var globule = require('globule');
var helper = require('./helper');

// globals
var delay = 10;

// `Gaze` EventEmitter object to return in the callback
function Gaze(patterns, opts, done) {
  var self = this;
  EE.call(self);

  // If second arg is the callback
  if (typeof opts === 'function') {
    done = opts;
    opts = {};
  }

  // Default options
  opts = opts || {};
  opts.mark = true;
  opts.interval = opts.interval || 100;
  opts.debounceDelay = opts.debounceDelay || 500;
  opts.cwd = opts.cwd || process.cwd();
  this.options = opts;

  // Default done callback
  done = done || function() {};

  // Remember our watched dir:files
  this._watched = Object.create(null);

  // Store watchers
  this._watchers = Object.create(null);

  // Store watchFile listeners
  this._pollers = Object.create(null);

  // Store patterns
  this._patterns = [];

  // Cached events for debouncing
  this._cached = Object.create(null);

  // Set maxListeners
  if (this.options.maxListeners) {
    this.setMaxListeners(this.options.maxListeners);
    Gaze.super_.prototype.setMaxListeners(this.options.maxListeners);
    delete this.options.maxListeners;
  }

  // Initialize the watch on files
  if (patterns) {
    this.add(patterns, done);
  }

  return this;
}
util.inherits(Gaze, EE);

// Main entry point. Start watching and call done when setup
module.exports = function gaze(patterns, opts, done) {
  return new Gaze(patterns, opts, done);
};
module.exports.Gaze = Gaze;

// Override the emit function to emit `all` events
// and debounce on duplicate events per file
Gaze.prototype.emit = function() {
  var self = this;
  var args = arguments;

  var e = args[0];
  var filepath = args[1];
  var timeoutId;

  // If not added/deleted/changed/renamed then just emit the event
  if (e.slice(-2) !== 'ed') {
    Gaze.super_.prototype.emit.apply(self, args);
    return this;
  }

  // Detect rename event, if added and previous deleted is in the cache
  if (e === 'added') {
    Object.keys(this._cached).forEach(function(oldFile) {
      if (self._cached[oldFile].indexOf('deleted') !== -1) {
        args[0] = e = 'renamed';
        [].push.call(args, oldFile);
        delete self._cached[oldFile];
        return false;
      }
    });
  }

  // If cached doesnt exist, create a delay before running the next
  // then emit the event
  var cache = this._cached[filepath] || [];
  if (cache.indexOf(e) === -1) {
    helper.objectPush(self._cached, filepath, e);
    clearTimeout(timeoutId);
    timeoutId = setTimeout(function() {
      delete self._cached[filepath];
    }, this.options.debounceDelay);
    // Emit the event and `all` event
    Gaze.super_.prototype.emit.apply(self, args);
    Gaze.super_.prototype.emit.apply(self, ['all', e].concat([].slice.call(args, 1)));
  }

  return this;
};

// Close watchers
Gaze.prototype.close = function(_reset) {
  var self = this;
  _reset = _reset === false ? false : true;
  Object.keys(self._watchers).forEach(function(file) {
    self._watchers[file].close();
  });
  self._watchers = Object.create(null);
  Object.keys(this._watched).forEach(function(dir) {
    fs.unwatchFile(dir);
    self._watched[dir].forEach(function(uFile) {
      fs.unwatchFile(uFile);
    });
  });
  if (_reset) {
    self._watched = Object.create(null);
    setTimeout(function() {
      self.emit('end');
     self.removeAllListeners();
   }, delay + 100);
  }
  return self;
};

// Add file patterns to be watched
Gaze.prototype.add = function(files, done) {
  if (typeof files === 'string') { files = [files]; }
  this._patterns = helper.unique.apply(null, [this._patterns, files]);
  files = globule.find(this._patterns, this.options);
  this._addToWatched(files);
  this.close(false);
  this._initWatched(done);
};

// Dont increment patterns and dont call done if nothing added
Gaze.prototype._internalAdd = function(file, done) {
  var files = [];
  if (helper.isDir(file)) {
    files = globule.find(this._patterns, this.options);
  } else {
    if (globule.isMatch(this._patterns, file, this.options)) {
      files = [file];
    }
  }
  if (files.length > 0) {
    this._addToWatched(files);
    this.close(false);
    this._initWatched(done);
  }
};

// Remove file/dir from `watched`
Gaze.prototype.remove = function(file) {
  var self = this;
  if (this._watched[file]) {
    // is dir, remove all files
    this._unpollDir(file);
    delete this._watched[file];
  } else {
    // is a file, find and remove
    Object.keys(this._watched).forEach(function(dir) {
      var index = self._watched[dir].indexOf(file);
      if (index !== -1) {
        fs.unwatchFile(file);
        self._watched[dir].splice(index, 1);
        return false;
      }
    });
  }
  if (this._watchers[file]) {
    this._watchers[file].close();
  }
  return this;
};

// Return watched files
Gaze.prototype.watched = function() {
  return this._watched;
};

// Returns `watched` files with relative paths to process.cwd()
Gaze.prototype.relative = function(dir, unixify) {
  var self = this;
  var relative = Object.create(null);
  var relDir, relFile, unixRelDir;
  var cwd = this.options.cwd || process.cwd();
  if (dir === '') { dir = '.'; }
  dir = helper.markDir(dir);
  unixify = unixify || false;
  Object.keys(this._watched).forEach(function(dir) {
    relDir = path.relative(cwd, dir) + path.sep;
    if (relDir === path.sep) { relDir = '.'; }
    unixRelDir = unixify ? helper.unixifyPathSep(relDir) : relDir;
    relative[unixRelDir] = self._watched[dir].map(function(file) {
      relFile = path.relative(path.join(cwd, relDir) || '', file || '');
      if (helper.isDir(file)) {
        relFile = helper.markDir(relFile);
      }
      if (unixify) {
        relFile = helper.unixifyPathSep(relFile);
      }
      return relFile;
    });
  });
  if (dir && unixify) {
    dir = helper.unixifyPathSep(dir);
  }
  return dir ? relative[dir] || [] : relative;
};

// Adds files and dirs to watched
Gaze.prototype._addToWatched = function(files) {
  for (var i = 0; i < files.length; i++) {
    var file = files[i];
    var filepath = path.resolve(this.options.cwd, file);

    var dirname = (helper.isDir(file)) ? filepath : path.dirname(filepath);
    dirname = helper.markDir(dirname);

    if (file.slice(-1) === '/') { filepath += path.sep; }
    helper.objectPush(this._watched, path.dirname(filepath) + path.sep, filepath);

    // add folders into the mix
    var readdir = fs.readdirSync(dirname);
    for (var j = 0; j < readdir.length; j++) {
      var dirfile = path.join(dirname, readdir[j]);
      if (fs.statSync(dirfile).isDirectory()) {
        helper.objectPush(this._watched, dirname, dirfile + path.sep);
      }
    }
  }
  return this;
};

Gaze.prototype._watchDir = function(dir, done) {
  try {
    this._watchers[dir] = fs.watch(dir, function(event) {
      // race condition. Let's give the fs a little time to settle down. so we
      // don't fire events on non existent files.
      setTimeout(function() {
        if (fs.existsSync(dir)) {
          done(null, dir);
        }
      }, delay + 100);
    });
  } catch (err) {
    return this._handleError(err);
  }
  return this;
};

Gaze.prototype._unpollFile = function(file) {
  if (this._pollers[file]) {
    fs.unwatchFile(file, this._pollers[file] );
    delete this._pollers[file];
  }
  return this;
};

Gaze.prototype._unpollDir = function(dir) {
  this._unpollFile(dir);
  for (var i = 0; i < this._watched[dir].length; i++) {
    this._unpollFile(this._watched[dir][i]);
  }
};

Gaze.prototype._pollFile = function(file, done) {
  var opts = { persistent: true, interval: this.options.interval };
  if (!this._pollers[file]) {
    this._pollers[file] = function(curr, prev) {
      done(null, file);
    };
    try {
      fs.watchFile(file, opts, this._pollers[file]);
    } catch (err) {
      return this._handleError(err);
    }
  }
  return this;
};

// Initialize the actual watch on `watched` files
Gaze.prototype._initWatched = function(done) {
  var self = this;
  var cwd = this.options.cwd || process.cwd();
  var curWatched = Object.keys(self._watched);
  helper.forEachSeries(curWatched, function(dir, next) {
    var files = self._watched[dir];
    // Triggered when a watched dir has an event
    self._watchDir(dir, function(event, dirpath) {
      var relDir = cwd === dir ? '.' : path.relative(cwd, dir);

      fs.readdir(dirpath, function(err, current) {
        if (err) { return self.emit('error', err); }
        if (!current) { return; }

        try {
          // append path.sep to directories so they match previous.
          current = current.map(function(curPath) {
            if (fs.existsSync(path.join(dir, curPath)) && fs.statSync(path.join(dir, curPath)).isDirectory()) {
              return curPath + path.sep;
            } else {
              return curPath;
            }
          });
        } catch (err) {
          // race condition-- sometimes the file no longer exists
        }

        // Get watched files for this dir
        var previous = self.relative(relDir);

        // If file was deleted
        previous.filter(function(file) {
          return current.indexOf(file) < 0;
        }).forEach(function(file) {
          if (!helper.isDir(file)) {
            var filepath = path.join(dir, file);
            self.remove(filepath);
            self.emit('deleted', filepath);
          }
        });

        // If file was added
        current.filter(function(file) {
          return previous.indexOf(file) < 0;
        }).forEach(function(file) {
          // Is it a matching pattern?
          var relFile = path.join(relDir, file);
          // Add to watch then emit event
          self._internalAdd(relFile, function() {
            self.emit('added', path.join(dir, file));
          });
        });

      });
    });

    // Watch for change/rename events on files
    files.forEach(function(file) {
      if (helper.isDir(file)) { return; }
      self._pollFile(file, function(err, filepath) {
        // Only emit changed if the file still exists
        // Prevents changed/deleted duplicate events
        if (fs.existsSync(filepath)) {
          self.emit('changed', filepath);
        }
      });
    });

    next();
  }, function() {

    // Return this instance of Gaze
    // delay before ready solves a lot of issues
    setTimeout(function() {
      self.emit('ready', self);
      done.call(self, null, self);
    }, delay + 100);

  });
};

// If an error, handle it here
Gaze.prototype._handleError = function(err) {
  if (err.code === 'EMFILE') {
    return this.emit('error', new Error('EMFILE: Too many opened files.'));
  }
  return this.emit('error', err);
};
<commit_msg>Patch bad rebase<commit_after>/*
 * gaze
 * https://github.com/shama/gaze
 *
 * Copyright (c) 2013 Kyle Robinson Young
 * Licensed under the MIT license.
 */

'use strict';

// libs
var util = require('util');
var EE = require('events').EventEmitter;
var fs = require('fs');
var path = require('path');
var globule = require('globule');
var helper = require('./helper');

// globals
var delay = 10;

// `Gaze` EventEmitter object to return in the callback
function Gaze(patterns, opts, done) {
  var self = this;
  EE.call(self);

  // If second arg is the callback
  if (typeof opts === 'function') {
    done = opts;
    opts = {};
  }

  // Default options
  opts = opts || {};
  opts.mark = true;
  opts.interval = opts.interval || 100;
  opts.debounceDelay = opts.debounceDelay || 500;
  opts.cwd = opts.cwd || process.cwd();
  this.options = opts;

  // Default done callback
  done = done || function() {};

  // Remember our watched dir:files
  this._watched = Object.create(null);

  // Store watchers
  this._watchers = Object.create(null);

  // Store watchFile listeners
  this._pollers = Object.create(null);

  // Store patterns
  this._patterns = [];

  // Cached events for debouncing
  this._cached = Object.create(null);

  // Set maxListeners
  if (this.options.maxListeners) {
    this.setMaxListeners(this.options.maxListeners);
    Gaze.super_.prototype.setMaxListeners(this.options.maxListeners);
    delete this.options.maxListeners;
  }

  // Initialize the watch on files
  if (patterns) {
    this.add(patterns, done);
  }

  return this;
}
util.inherits(Gaze, EE);

// Main entry point. Start watching and call done when setup
module.exports = function gaze(patterns, opts, done) {
  return new Gaze(patterns, opts, done);
};
module.exports.Gaze = Gaze;

// Override the emit function to emit `all` events
// and debounce on duplicate events per file
Gaze.prototype.emit = function() {
  var self = this;
  var args = arguments;

  var e = args[0];
  var filepath = args[1];
  var timeoutId;

  // If not added/deleted/changed/renamed then just emit the event
  if (e.slice(-2) !== 'ed') {
    Gaze.super_.prototype.emit.apply(self, args);
    return this;
  }

  // Detect rename event, if added and previous deleted is in the cache
  if (e === 'added') {
    Object.keys(this._cached).forEach(function(oldFile) {
      if (self._cached[oldFile].indexOf('deleted') !== -1) {
        args[0] = e = 'renamed';
        [].push.call(args, oldFile);
        delete self._cached[oldFile];
        return false;
      }
    });
  }

  // If cached doesnt exist, create a delay before running the next
  // then emit the event
  var cache = this._cached[filepath] || [];
  if (cache.indexOf(e) === -1) {
    helper.objectPush(self._cached, filepath, e);
    clearTimeout(timeoutId);
    timeoutId = setTimeout(function() {
      delete self._cached[filepath];
    }, this.options.debounceDelay);
    // Emit the event and `all` event
    Gaze.super_.prototype.emit.apply(self, args);
    Gaze.super_.prototype.emit.apply(self, ['all', e].concat([].slice.call(args, 1)));
  }

  return this;
};

// Close watchers
Gaze.prototype.close = function(_reset) {
  var self = this;
  _reset = _reset === false ? false : true;
  Object.keys(self._watchers).forEach(function(file) {
    self._watchers[file].close();
  });
  self._watchers = Object.create(null);
  Object.keys(this._watched).forEach(function(dir) {
    self._unpollDir(dir);
  });
  if (_reset) {
    self._watched = Object.create(null);
    setTimeout(function() {
      self.emit('end');
      self.removeAllListeners();
    }, delay + 100);
  }
  return self;
};

// Add file patterns to be watched
Gaze.prototype.add = function(files, done) {
  if (typeof files === 'string') { files = [files]; }
  this._patterns = helper.unique.apply(null, [this._patterns, files]);
  files = globule.find(this._patterns, this.options);
  this._addToWatched(files);
  this.close(false);
  this._initWatched(done);
};

// Dont increment patterns and dont call done if nothing added
Gaze.prototype._internalAdd = function(file, done) {
  var files = [];
  if (helper.isDir(file)) {
    files = globule.find(this._patterns, this.options);
  } else {
    if (globule.isMatch(this._patterns, file, this.options)) {
      files = [file];
    }
  }
  if (files.length > 0) {
    this._addToWatched(files);
    this.close(false);
    this._initWatched(done);
  }
};

// Remove file/dir from `watched`
Gaze.prototype.remove = function(file) {
  var self = this;
  if (this._watched[file]) {
    // is dir, remove all files
    this._unpollDir(file);
    delete this._watched[file];
  } else {
    // is a file, find and remove
    Object.keys(this._watched).forEach(function(dir) {
      var index = self._watched[dir].indexOf(file);
      if (index) {
        self._unpollFile(file);
        delete self._watched[dir][index];
        return false;
      }
    });
  }
  if (this._watchers[file]) {
    this._watchers[file].close();
  }
  return this;
};

// Return watched files
Gaze.prototype.watched = function() {
  return this._watched;
};

// Returns `watched` files with relative paths to process.cwd()
Gaze.prototype.relative = function(dir, unixify) {
  var self = this;
  var relative = Object.create(null);
  var relDir, relFile, unixRelDir;
  var cwd = this.options.cwd || process.cwd();
  if (dir === '') { dir = '.'; }
  dir = helper.markDir(dir);
  unixify = unixify || false;
  Object.keys(this._watched).forEach(function(dir) {
    relDir = path.relative(cwd, dir) + path.sep;
    if (relDir === path.sep) { relDir = '.'; }
    unixRelDir = unixify ? helper.unixifyPathSep(relDir) : relDir;
    relative[unixRelDir] = self._watched[dir].map(function(file) {
      relFile = path.relative(path.join(cwd, relDir) || '', file || '');
      if (helper.isDir(file)) {
        relFile = helper.markDir(relFile);
      }
      if (unixify) {
        relFile = helper.unixifyPathSep(relFile);
      }
      return relFile;
    });
  });
  if (dir && unixify) {
    dir = helper.unixifyPathSep(dir);
  }
  return dir ? relative[dir] || [] : relative;
};

// Adds files and dirs to watched
Gaze.prototype._addToWatched = function(files) {
  for (var i = 0; i < files.length; i++) {
    var file = files[i];
    var filepath = path.resolve(this.options.cwd, file);

    var dirname = (helper.isDir(file)) ? filepath : path.dirname(filepath);
    dirname = helper.markDir(dirname);

    if (file.slice(-1) === '/') { filepath += path.sep; }
    helper.objectPush(this._watched, path.dirname(filepath) + path.sep, filepath);

    // add folders into the mix
    var readdir = fs.readdirSync(dirname);
    for (var j = 0; j < readdir.length; j++) {
      var dirfile = path.join(dirname, readdir[j]);
      if (fs.statSync(dirfile).isDirectory()) {
        helper.objectPush(this._watched, dirname, dirfile + path.sep);
      }
    }
  }
  return this;
};

Gaze.prototype._watchDir = function(dir, done) {
  try {
    this._watchers[dir] = fs.watch(dir, function(event) {
      // race condition. Let's give the fs a little time to settle down. so we
      // don't fire events on non existent files.
      setTimeout(function() {
        if (fs.existsSync(dir)) {
          done(null, dir);
        }
      }, delay + 100);
    });
  } catch (err) {
    return this._handleError(err);
  }
  return this;
};

Gaze.prototype._unpollFile = function(file) {
  if (this._pollers[file]) {
    fs.unwatchFile(file, this._pollers[file] );
    delete this._pollers[file];
  }
  return this;
};

Gaze.prototype._unpollDir = function(dir) {
  this._unpollFile(dir);
  for (var i = 0; i < this._watched[dir].length; i++) {
    this._unpollFile(this._watched[dir][i]);
  }
};

Gaze.prototype._pollFile = function(file, done) {
  var opts = { persistent: true, interval: this.options.interval };
  if (!this._pollers[file]) {
    this._pollers[file] = function(curr, prev) {
      done(null, file);
    };
    try {
      fs.watchFile(file, opts, this._pollers[file]);
    } catch (err) {
      return this._handleError(err);
    }
  }
  return this;
};

// Initialize the actual watch on `watched` files
Gaze.prototype._initWatched = function(done) {
  var self = this;
  var cwd = this.options.cwd || process.cwd();
  var curWatched = Object.keys(self._watched);
  helper.forEachSeries(curWatched, function(dir, next) {
    var files = self._watched[dir];
    // Triggered when a watched dir has an event
    self._watchDir(dir, function(event, dirpath) {
      var relDir = cwd === dir ? '.' : path.relative(cwd, dir);

      fs.readdir(dirpath, function(err, current) {
        if (err) { return self.emit('error', err); }
        if (!current) { return; }

        try {
          // append path.sep to directories so they match previous.
          current = current.map(function(curPath) {
            if (fs.existsSync(path.join(dir, curPath)) && fs.statSync(path.join(dir, curPath)).isDirectory()) {
              return curPath + path.sep;
            } else {
              return curPath;
            }
          });
        } catch (err) {
          // race condition-- sometimes the file no longer exists
        }

        // Get watched files for this dir
        var previous = self.relative(relDir);

        // If file was deleted
        previous.filter(function(file) {
          return current.indexOf(file) < 0;
        }).forEach(function(file) {
          if (!helper.isDir(file)) {
            var filepath = path.join(dir, file);
            self.remove(filepath);
            self.emit('deleted', filepath);
          }
        });

        // If file was added
        current.filter(function(file) {
          return previous.indexOf(file) < 0;
        }).forEach(function(file) {
          // Is it a matching pattern?
          var relFile = path.join(relDir, file);
          // Add to watch then emit event
          self._internalAdd(relFile, function() {
            self.emit('added', path.join(dir, file));
          });
        });

      });
    });

    // Watch for change/rename events on files
    files.forEach(function(file) {
      if (helper.isDir(file)) { return; }
      self._pollFile(file, function(err, filepath) {
        // Only emit changed if the file still exists
        // Prevents changed/deleted duplicate events
        if (fs.existsSync(filepath)) {
          self.emit('changed', filepath);
        }
      });
    });

    next();
  }, function() {

    // Return this instance of Gaze
    // delay before ready solves a lot of issues
    setTimeout(function() {
      self.emit('ready', self);
      done.call(self, null, self);
    }, delay + 100);

  });
};

// If an error, handle it here
Gaze.prototype._handleError = function(err) {
  if (err.code === 'EMFILE') {
    return this.emit('error', new Error('EMFILE: Too many opened files.'));
  }
  return this.emit('error', err);
};
<|endoftext|>


<commit_before>/*
 * gith
 * https://github.com/danheberden/gith
 *
 * Copyright (c) 2012 Dan Heberden
 * Licensed under the MIT license.
 */

var http = require("http");
var EventEmitter2 = require( "eventemitter2" ).EventEmitter2;
var util = require("util");
var _ = require("lodash");
var querystring = require("querystring");

var filterSettings = function( settings, payload ) {

  if ( !payload ) {
    return false;
  }

  settings = settings || {};

  // add the matches object for later
  payload.matches = {};

  // check all the things
  var checksPassed = true;

  [ 'repo', 'branch', 'file', 'tag' ].forEach( function( thing ) {
    var wat = settings[ thing ];

    // default to a passed state
    var passed = true;

    // was a filter specified? and is it not a wildcard?
    if ( wat && wat !== '*' ) {

      // checking, so default passed to false
      passed = false;

      // make an array of the thing or all the files
      var checks = [].concat( thing === 'file' ? payload.files.all : payload[ thing ] );

      // all the checks - did any of them pass?
      passed = checks.some( function( check ) {

         // direct match
        if ( wat === check ) {
          return true;
        }

        // if negated match (!string)
        if ( _.isString( wat ) && wat[0] === "!" &&
              wat.slice(1) !== check ) {
          return true;
        }

        // regex?
        if ( _.isRegExp( wat ) ) {
          var match = check.match( wat );

          // did it match? huh? did it?
          if ( match ) {
            // goddamn files being different
            if ( thing === 'file' ) {
              if ( !payload.matches.files ) {
                payload.matches.files = {};
              }
              payload.matches.files[ check ] = match;
            } else {
              payload.matches[ thing ] = match;
            }

            return true;
          }
        }
      });

      // usr function?
      if ( _.isFunction( wat ) ) {
        passed = wat( payload[ thing ], payload );
      }

      // assign the final result of this 'thing' to checksPassed
      checksPassed = passed && checksPassed;
    }
  });

  return checksPassed;
};

// Used by exports.module.create's returned function to create
// new gith objects that hold settings and emit events
var Gith = function( eventaur, settings ) {
  var gith = this;
  this.settings = settings || {};

  // make this bad boy an event emitter
  EventEmitter2.call( this, {
    delimiter: ':',
    maxListeners: 0
  });

  // handle bound payloads
  eventaur.on( 'payload', function( originalPayload ) {

    // make a simpler payload
    var payload = gith.simplifyPayload( originalPayload );

    // bother doing anything?
    if ( filterSettings( settings, payload ) ) {

      // all the things
      gith.emit( 'all', payload );

      // did we do any branch work?
      if ( originalPayload.created && originalPayload.forced && payload.branch ) {
        gith.emit( 'branch:add', payload );
      }
      if (  originalPayload.deleted && originalPayload.forced && payload.branch ) {
        gith.emit( 'branch:delete', payload );
      }

      // how about files?
      if ( payload.files.added.length > 0 ) {
        gith.emit( 'file:add', payload );
      }
      if ( payload.files.deleted.length > 0 ) {
        gith.emit( 'file:delete', payload );
      }
      if ( payload.files.modified.length > 0 ) {
        gith.emit( 'file:modify', payload );
      }
      if ( payload.files.all.length > 0 ) {
        gith.emit( 'file:all', payload );
      }

      // tagging?
      if ( payload.tag && originalPayload.created ) {
        gith.emit( 'tag:add', payload );
      }
      if ( payload.tag && originalPayload.deleted ) {
        gith.emit( 'tag:delete', payload );
      }
    }
  });
};

// inherit the EventEmitter2 stuff
util.inherits( Gith, EventEmitter2 );

// expose the simpliyPayload method on gith()
Gith.prototype.simplifyPayload = function( payload ) {
  payload = payload || {};

  var branch = '';
  var tag = '';
  var rRef = /refs\/(tags|heads)\/(.*)$/;

  // break out if it was a tag or branch and assign
  var refMatches = ( payload.ref || "" ).match( rRef );
  if ( refMatches ) {
    if ( refMatches[1] === "heads" ) {
      branch = refMatches[2];
    }
    if ( refMatches[1] === "tags" ) {
      tag = refMatches[2];
    }
  }

  // if branch wasn't found, use base_ref if available
  if ( !branch && payload.base_ref ) {
    branch = payload.base_ref.replace( rRef, '$2' );
  }

  var simpler = {
    original: payload,
    files: {
      all: [],
      added: [],
      deleted: [],
      modified: []
    },
    tag: tag,
    branch: branch,
    repo: payload.repository ? (payload.repository.owner.name + '/' + payload.repository.name) : null,
    sha: payload.after,
    time: payload.repository ? payload.repository.pushed_at : null,
    urls: {
      head: payload.head_commit ? payload.head_commit.url : '',
      branch: '',
      tag: '',
      repo: payload.repository ? payload.repository.url : null,
      compare: payload.compare
    },
    reset: !payload.created && payload.forced,
    pusher: payload.pusher ? payload.pusher.name : null,
    owner: (payload.repository && payload.repository.owner) ? payload.repository.owner.name : null
  };

  if ( branch ) {
    simpler.urls.branch = simpler.urls.branch + '/tree/' + branch;
  }
  if ( tag ) {
    simpler.urls.tag = simpler.urls.head;
  }

  // populate files for every commit
  (payload.commits || []).forEach( function( commit ) {
    // github label and simpler label ( make 'removed' deleted to be consistant )
    _.each( { added: 'added', modified: 'modified', removed: 'deleted' }, function( s, g ) {
      simpler.files[ s ] = simpler.files[ s ].concat( commit[ g ] );
      simpler.files.all = simpler.files.all.concat( commit[ g ] );
    });
  });

  return simpler;
  // todo: use github api to find what files were removed if the
  // head was reset? maybe?
};

// the listen method - this gets added/bound in
// module.exports.create, fyi
var listen = function( eventaur, port ) {
  var gith = this;

  // are we changing ports?
  if ( port ) {
    this.port = port;
  }
  if ( !this.port ) {
    throw new Error('.listen() requires a port to be set');
  }

  this.server = http.createServer( function( req, res ) {
    var data = "";
    if ( req.method === "POST" ) {
      req.on( "data", function( chunk ) {
        data += chunk;
      });
    }

    req.on( "end", function() {
       console.log(gith.ips);
      if ( _.indexOf( gith.ips, req.connection.remoteAddress ) >= 0 ||
           _.indexOf( gith.ips, "*" ) >= 0 ) {
          console.log(data);
          /*
           * FIXED : Paylod does not seem to come with prefix "payload="
           */
        if ( /^payload=/.test( data )  || true) {
//          var payload = JSON.parse( querystring.unescape(data.slice(8)) );
          var payload = JSON.parse( data );
          eventaur.emit( "payload", payload );
          res.writeHead( 200, {
            'Content-type': 'text/html'
          });
        }
      }
      res.end();
    });

  }).listen( port );
};

// make require('gith')( 9001 ) work if someone really wants to
module.exports = function( port ) {
  return module.exports.create( port );
};

// make the preferred way of `require('gith').create( 9001 ) work
module.exports.create = function( port ) {

  // make an event emitter to use for the hardcore stuff
  var eventaur = new EventEmitter2({
    delimter: ':',
    maxListeners: 0
  });

  // return a function that
  //   a) holds its own server/port/whatever
  //   b) exposes a listen method
  //   c) is a function that returns a new Gith object
  var ret = function( map ) {
    // make a new Gith with a reference to this factory
    return new Gith( eventaur, map );
  };

  // https://github.com/danheberden/gith/issues/13
  ret.ips = [
    '207.97.227.253',
    '50.57.128.197',
    '108.171.174.178',
    '50.57.231.61'
  ];

  // add the listen method to the function - bind to ret
  // and send eventaur to it
  ret.listen = listen.bind( ret, eventaur );

  // expose ability to close http server
  ret.close = function(){
    this.server.close();
  };

  ret.payload = function( payload ) {
   eventaur.emit( 'payload', payload );
  };

  // if create was sent port, call listen automatically
  if ( port ) {
    ret.listen( port );
  }

  // return the new function
  return ret;
};
<commit_msg>Fixed: with debug<commit_after>/*
 * gith
 * https://github.com/danheberden/gith
 *
 * Copyright (c) 2012 Dan Heberden
 * Licensed under the MIT license.
 */

var http = require("http");
var EventEmitter2 = require( "eventemitter2" ).EventEmitter2;
var util = require("util");
var _ = require("lodash");
var querystring = require("querystring");

var filterSettings = function( settings, payload ) {

  if ( !payload ) {
    return false;
  }

  settings = settings || {};

  // add the matches object for later
  payload.matches = {};

  // check all the things
  var checksPassed = true;

  [ 'repo', 'branch', 'file', 'tag' ].forEach( function( thing ) {
    var wat = settings[ thing ];

    // default to a passed state
    var passed = true;

    // was a filter specified? and is it not a wildcard?
    if ( wat && wat !== '*' ) {

      // checking, so default passed to false
      passed = false;

      // make an array of the thing or all the files
      var checks = [].concat( thing === 'file' ? payload.files.all : payload[ thing ] );

      // all the checks - did any of them pass?
      passed = checks.some( function( check ) {

         // direct match
        if ( wat === check ) {
          return true;
        }

        // if negated match (!string)
        if ( _.isString( wat ) && wat[0] === "!" &&
              wat.slice(1) !== check ) {
          return true;
        }

        // regex?
        if ( _.isRegExp( wat ) ) {
          var match = check.match( wat );

          // did it match? huh? did it?
          if ( match ) {
            // goddamn files being different
            if ( thing === 'file' ) {
              if ( !payload.matches.files ) {
                payload.matches.files = {};
              }
              payload.matches.files[ check ] = match;
            } else {
              payload.matches[ thing ] = match;
            }

            return true;
          }
        }
      });

      // usr function?
      if ( _.isFunction( wat ) ) {
        passed = wat( payload[ thing ], payload );
      }

      // assign the final result of this 'thing' to checksPassed
      checksPassed = passed && checksPassed;
    }
  });

  return checksPassed;
};

// Used by exports.module.create's returned function to create
// new gith objects that hold settings and emit events
var Gith = function( eventaur, settings ) {
  var gith = this;
  this.settings = settings || {};

  // make this bad boy an event emitter
  EventEmitter2.call( this, {
    delimiter: ':',
    maxListeners: 0
  });

  // handle bound payloads
  eventaur.on( 'payload', function( originalPayload ) {

    // make a simpler payload
    var payload = gith.simplifyPayload( originalPayload );

    // bother doing anything?
    if ( filterSettings( settings, payload ) ) {

      // all the things
      gith.emit( 'all', payload );

      // did we do any branch work?
      if ( originalPayload.created && originalPayload.forced && payload.branch ) {
        gith.emit( 'branch:add', payload );
      }
      if (  originalPayload.deleted && originalPayload.forced && payload.branch ) {
        gith.emit( 'branch:delete', payload );
      }

      // how about files?
      if ( payload.files.added.length > 0 ) {
        gith.emit( 'file:add', payload );
      }
      if ( payload.files.deleted.length > 0 ) {
        gith.emit( 'file:delete', payload );
      }
      if ( payload.files.modified.length > 0 ) {
        gith.emit( 'file:modify', payload );
      }
      if ( payload.files.all.length > 0 ) {
        gith.emit( 'file:all', payload );
      }

      // tagging?
      if ( payload.tag && originalPayload.created ) {
        gith.emit( 'tag:add', payload );
      }
      if ( payload.tag && originalPayload.deleted ) {
        gith.emit( 'tag:delete', payload );
      }
    }
  });
};

// inherit the EventEmitter2 stuff
util.inherits( Gith, EventEmitter2 );

// expose the simpliyPayload method on gith()
Gith.prototype.simplifyPayload = function( payload ) {
  payload = payload || {};

  var branch = '';
  var tag = '';
  var rRef = /refs\/(tags|heads)\/(.*)$/;

  // break out if it was a tag or branch and assign
  var refMatches = ( payload.ref || "" ).match( rRef );
  if ( refMatches ) {
    if ( refMatches[1] === "heads" ) {
      branch = refMatches[2];
    }
    if ( refMatches[1] === "tags" ) {
      tag = refMatches[2];
    }
  }

  // if branch wasn't found, use base_ref if available
  if ( !branch && payload.base_ref ) {
    branch = payload.base_ref.replace( rRef, '$2' );
  }

  var simpler = {
    original: payload,
    files: {
      all: [],
      added: [],
      deleted: [],
      modified: []
    },
    tag: tag,
    branch: branch,
    repo: payload.repository ? (payload.repository.owner.name + '/' + payload.repository.name) : null,
    sha: payload.after,
    time: payload.repository ? payload.repository.pushed_at : null,
    urls: {
      head: payload.head_commit ? payload.head_commit.url : '',
      branch: '',
      tag: '',
      repo: payload.repository ? payload.repository.url : null,
      compare: payload.compare
    },
    reset: !payload.created && payload.forced,
    pusher: payload.pusher ? payload.pusher.name : null,
    owner: (payload.repository && payload.repository.owner) ? payload.repository.owner.name : null
  };

  if ( branch ) {
    simpler.urls.branch = simpler.urls.branch + '/tree/' + branch;
  }
  if ( tag ) {
    simpler.urls.tag = simpler.urls.head;
  }

  // populate files for every commit
  (payload.commits || []).forEach( function( commit ) {
    // github label and simpler label ( make 'removed' deleted to be consistant )
    _.each( { added: 'added', modified: 'modified', removed: 'deleted' }, function( s, g ) {
      simpler.files[ s ] = simpler.files[ s ].concat( commit[ g ] );
      simpler.files.all = simpler.files.all.concat( commit[ g ] );
    });
  });

  return simpler;
  // todo: use github api to find what files were removed if the
  // head was reset? maybe?
};

// the listen method - this gets added/bound in
// module.exports.create, fyi
var listen = function( eventaur, port ) {
  var gith = this;

  // are we changing ports?
  if ( port ) {
    this.port = port;
  }
  if ( !this.port ) {
    throw new Error('.listen() requires a port to be set');
  }

  this.server = http.createServer( function( req, res ) {
    var data = "";
    if ( req.method === "POST" ) {
      req.on( "data", function( chunk ) {
        data += chunk;
      });
    }

    req.on( "end", function() {
        console.log(gith.ips);
        console.log(req.connection.remoteAddress);
      if ( _.indexOf( gith.ips, req.connection.remoteAddress ) >= 0 ||
           _.indexOf( gith.ips, "*" ) >= 0 ) {
          console.log(data);
          /*
           * FIXED : Paylod does not seem to come with prefix "payload="
           */
        if ( /^payload=/.test( data )  || true) {
//          var payload = JSON.parse( querystring.unescape(data.slice(8)) );
          var payload = JSON.parse( data );
          eventaur.emit( "payload", payload );
          res.writeHead( 200, {
            'Content-type': 'text/html'
          });
        }
      }
      res.end();
    });

  }).listen( port );
};

// make require('gith')( 9001 ) work if someone really wants to
module.exports = function( port ) {
  return module.exports.create( port );
};

// make the preferred way of `require('gith').create( 9001 ) work
module.exports.create = function( port ) {

  // make an event emitter to use for the hardcore stuff
  var eventaur = new EventEmitter2({
    delimter: ':',
    maxListeners: 0
  });

  // return a function that
  //   a) holds its own server/port/whatever
  //   b) exposes a listen method
  //   c) is a function that returns a new Gith object
  var ret = function( map ) {
    // make a new Gith with a reference to this factory
    return new Gith( eventaur, map );
  };

  // https://github.com/danheberden/gith/issues/13
  ret.ips = [
    '207.97.227.253',
    '50.57.128.197',
    '108.171.174.178',
    '50.57.231.61'
  ];

  // add the listen method to the function - bind to ret
  // and send eventaur to it
  ret.listen = listen.bind( ret, eventaur );

  // expose ability to close http server
  ret.close = function(){
    this.server.close();
  };

  ret.payload = function( payload ) {
   eventaur.emit( 'payload', payload );
  };

  // if create was sent port, call listen automatically
  if ( port ) {
    ret.listen( port );
  }

  // return the new function
  return ret;
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Module dependencies
 */

var fs = require('fs');
var path = require('path');
var glob = require('globby');
var debug = require('debug')('lint-deps:glob');
var uniq = require('array-unique');
var mm = require('minimatch');
var invalid = require('./exclusions').invalid;

/**
 * Expose `glob`
 */

module.exports = function(options, fn) {
  debug('glob: %j', options);

  var opts = options || {};
  opts.cwd = opts.cwd || '.';
  opts.patterns = opts.patterns || '*.js';
  opts.exclusions = union(invalid, opts.exclusions || []);

  var files = fs.readdirSync(opts.cwd).filter(function(fp) {
    debug('glob fp: %s', fp);

    if (fn && typeof fn === 'function') {
      return fn(fp, opts);
    }
    return opts.exclusions.indexOf(fp) === -1;
  });

  return files.reduce(function (acc, cwd) {
    if (fs.statSync(cwd).isFile()) {
      var dir = path.dirname(cwd);
      if (dir !== '.') {
        cwd = dir;
      }

      debug('glob cwd: %s', cwd);
      acc = union(acc, opts.patterns.reduce(function (res, pattern) {
        debug('glob pattern: %s', pattern);
        return union(res, mm.match(arrayify(cwd), pattern));
      }, []));
    }

    var res = glob.sync(opts.patterns, {cwd: cwd}).map(function(fp) {
      debug('glob filepath: %s', fp);
      return path.join(cwd, fp);
    });

    return union(acc, res.filter(function(fp) {
      debug('glob filter: %s', fp);
      if (fn && typeof fn === 'function') {
        opts.cwd = cwd;
        return fn(fp, opts);
      }

      return !(new RegExp(opts.exclusions.join('|')).test(fp));
    }));
  }, []);
};

function arrayify(arr) {
  return !Array.isArray(arr)
    ? [arr]
    : arr;
}

function union() {
  return uniq([].concat.apply([], arguments));
}
<commit_msg>switch to micromatch<commit_after>'use strict';

/**
 * Module dependencies
 */

var fs = require('fs');
var path = require('path');
var glob = require('globby');
var debug = require('debug')('lint-deps:glob');
var uniq = require('array-unique');
var mm = require('micromatch');
var invalid = require('./exclusions').invalid;

/**
 * Expose `glob`
 */

module.exports = function(options, fn) {
  debug('glob: %j', options);

  var opts = options || {};
  opts.cwd = opts.cwd || '.';
  opts.patterns = opts.patterns || '*.js';
  opts.exclusions = union(invalid, opts.exclusions || []);

  var files = fs.readdirSync(opts.cwd).filter(function(fp) {
    debug('glob fp: %s', fp);

    if (fn && typeof fn === 'function') {
      return fn(fp, opts);
    }
    return opts.exclusions.indexOf(fp) === -1;
  });

  return files.reduce(function (acc, cwd) {
    if (fs.statSync(cwd).isFile()) {
      var dir = path.dirname(cwd);
      if (dir !== '.') {
        cwd = dir;
      }

      debug('glob cwd: %s', cwd);
      acc = union(acc, opts.patterns.reduce(function (res, pattern) {
        debug('glob pattern: %s', pattern);
        return union(res, mm.match(arrayify(cwd), pattern));
      }, []));
    }

    var res = glob.sync(opts.patterns, {cwd: cwd}).map(function(fp) {
      debug('glob filepath: %s', fp);
      return path.join(cwd, fp);
    });

    return union(acc, res.filter(function(fp) {
      debug('glob filter: %s', fp);
      if (fn && typeof fn === 'function') {
        opts.cwd = cwd;
        return fn(fp, opts);
      }

      return !(new RegExp(opts.exclusions.join('|')).test(fp));
    }));
  }, []);
};

function arrayify(arr) {
  return !Array.isArray(arr)
    ? [arr]
    : arr;
}

function union() {
  return uniq([].concat.apply([], arguments));
}
<|endoftext|>


<commit_before>let socket = null, store = null

window.addEventListener('keydown', keyDown, true)
window.addEventListener('keyup', keyUp, true)
window.onpopstate = () => run()

window.addEventListener('load', () => {
  Latte.setup('debug')
  Latte.onTest = (test, error) => store.set({focusFailure: !!error})
  Zen.store = store = new svelte.Store({results: [], totalCount: 0, focus: null, focusStatus: 'none', compile: {errors: []}})
  store.compute('failureGroups', ['results', 'focus'], failureGroups)
  store.compute('groupForFocus', ['failureGroups'], (groups) => groups.find(g => g.containsFocus))
  Object.assign(store, {run, closeCommand, focusGroup})

  Zen.mini = new Zen.Mini({target: document.querySelector('body'), store})
  socket = new WebSocket(`ws://${location.host}/head`)
  socket.onopen = () => run()
  socket.onmessage = serverMessage
})

function serverMessage(msg) {
  let data = JSON.parse(msg.data)

  if (data.results && !data.runId) { // incremental update of results
    store.set({results: store.get().results.concat(data.results)})
  } else {
    // update our state from the server. If our code is out of date, update and re-run the focused test
    store.set(data)
    runIfCodeChanged()
  }
}

function run(opts={}) {
  let params = new URLSearchParams(location.search)
  let grep = opts.hasOwnProperty('grep') ? opts.grep : params.get('grep')
  let focus = opts.hasOwnProperty('focus') ? opts.focus : params.get('focus')
  store.set({grep, focus})

  // Update the url if we're changing grep or focus
  if (opts.hasOwnProperty('grep') || opts.hasOwnProperty('focus')) {
    let sp = new URLSearchParams(location.search)
    grep ? sp.set('grep', grep) : sp.delete('grep')
    focus ? sp.set('focus', focus) : sp.delete('focus')
    history.pushState({}, 'Zen', '?' + sp.toString())
  }

  let grepRegex = grep && new RegExp(grep, 'i')
  let tests = Latte.flatten().filter(t => (!grep || grepRegex.test(t.fullName)))

  let testNames = tests.map(t => t.fullName)
  socket.send(JSON.stringify(Object.assign({grep, testNames}, opts)))

  if (focus) {
    tests = tests.filter(t => t.fullName === focus)
    store.set({focusStatus: 'running'})
    Latte.run(tests).then(() => store.set({focusStatus: 'passed'}))
  }
}

function focusGroup(group) {
  if (typeof group === 'number') {
    let {failureGroups} = store.get()
    let index = failureGroups.findIndex(g => g.containsFocus)
    group = failureGroups[index + group] || failureGroups[0]
  }
  run({focus: group[0].fullName})
}

let onlyAlt = false
function keyDown(ev) {
  onlyAlt = ev.keyCode == 18 && !ev.shiftKey && !ev.metaKey && !ev.ctrlKey
  if (!ev.altKey) return
  let command = Zen.commands.find(c => c.keyCode === ev.keyCode)
  if (command && (!command.condition || command.condition())) {
    ev.preventDefault()
    if (Zen.command) closeCommand()
    command.action()
    onlyAlt = false
  }
}

function keyUp(ev) {
  let altUp = ev.keyCode == 18 && !ev.shiftKey && !ev.metaKey && !ev.ctrlKey
  if (altUp && onlyAlt) {
    if (Zen.command) {
      closeCommand()
    } else {
      Zen.command = new Zen.Command({target: document.querySelector('body'), store})
    }
  }
  onlyAlt = false
}

function closeCommand() {
  Zen.command.destroy()
  Zen.command = null
}

function failureGroups(results, focus) {
  let groups = {}
  results.forEach(t => {
    if (!t.error) return
    if (!t.error.stack) t.error.stack = 'unknown \n unknown'
    let key = t.error.message + t.error.stack.split('\n')[1]
    groups[key] = groups[key] || []
    groups[key].key = key
    groups[key].push(t)
  })

  return Object.values(groups).map(g => {
    g.shade = Math.min(Math.floor(Math.sqrt(g.length)), 6)
    g.containsFocus = !!g.find(r => focus === r.fullName)
    return g
  }).sort((a, b) => b.length - a.length)
}

Zen.commands = [
  {
    type: 'command', title: 'Run the focused test', key: 'Alt Space', keyCode: 32,
    condition: () => store.get().focus,
    action: () => store.run(),
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M4 18c0-7.73 6.27-14 14-14s14 6.27 14 14c0-5.523-4.477-10-10-10s-10 4.477-10 10v2h4l-8 8-8-8h4v-2z"></path></svg>`
  }, {
    type: 'command', title: 'Focus the next failing group', key: 'Alt →', keyCode: 39,
    condition: () => store.get().failureGroups.length > 0,
    action: () => focusGroup(+1),
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M31 16l-15-15v9h-16v12h16v9z"></path></svg>`
  // }, {
  //   type: 'command', title: 'Filter to failed tests', key: 'Alt F', keyCode: 70,
  //   condition: () => store.get('failureGroups').length > 0,
  //   action: () => this.store.run({filterFailed: true}),
  //   icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M31 16l-15-15v9h-16v12h16v9z"></path></svg>`
  }, {
    type: 'command', title: 'Run filtered tests', key: 'Alt Enter', keyCode: 13,
    action: () => store.run({force: true}),
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M4 18c0-7.73 6.27-14 14-14s14 6.27 14 14c0-5.523-4.477-10-10-10s-10 4.477-10 10v2h4l-8 8-8-8h4v-2z"></path></svg>`
  }, {
    type: 'command', title: 'Run all tests', key: 'Alt A', keyCode: 65,
    action: () => store.run({focus: null, grep: null, force: true}),
    altText: 'Clear the filter to run every suite',
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 26 26"><path d="M23.156 16.406c0.953 0.547 1.281 1.781 0.734 2.734l-1 1.719c-0.547 0.953-1.781 1.281-2.734 0.734l-4.156-2.391v4.797c0 1.094-0.906 2-2 2h-2c-1.094 0-2-0.906-2-2v-4.797l-4.156 2.391c-0.953 0.547-2.187 0.219-2.734-0.734l-1-1.719c-0.547-0.953-0.219-2.188 0.734-2.734l4.156-2.406-4.156-2.406c-0.953-0.547-1.281-1.781-0.734-2.734l1-1.719c0.547-0.953 1.781-1.281 2.734-0.734l4.156 2.391v-4.797c0-1.094 0.906-2 2-2h2c1.094 0 2 0.906 2 2v4.797l4.156-2.391c0.953-0.547 2.188-0.219 2.734 0.734l1 1.719c0.547 0.953 0.219 2.188-0.734 2.734l-4.156 2.406z"></path></svg>`
  }, {
    type: 'command', title: 'Dev: Reload headless chrome',
    action: () => store.run({reload: true}),
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M31 16l-15-15v9h-16v12h16v9z"></path></svg>`
  }
]

let hotReloading = false, compileHasFailed = false
async function runIfCodeChanged() {
  let {compile} = store.get()
  if (compile.status == 'compiling') return
  if (hotReloading) return

  // HMR can't recover from compile errors. Once the build is good again, we need to reload
  if (compile.status == 'error')
    return compileHasFailed = true

  if (compileHasFailed) // after recovering, we need to reload
    return window.location.reload()

  if (!ZenWebpackClient.needsUpdate(compile.hash)) return

  // continue hot reloading until we're up to date. NB that store.compile might change during
  // the async hop, so we we re-`get` it.
  hotReloading = true
  while (window.ZenWebpackClient && ZenWebpackClient.needsUpdate(store.get().compile.hash))
    await ZenWebpackClient.update()
  hotReloading = false

  run()
}
<commit_msg>Add command for previous group<commit_after>let socket = null, store = null

window.addEventListener('keydown', keyDown, true)
window.addEventListener('keyup', keyUp, true)
window.onpopstate = () => run()

window.addEventListener('load', () => {
  Latte.setup('debug')
  Latte.onTest = (test, error) => store.set({focusFailure: !!error})
  Zen.store = store = new svelte.Store({results: [], totalCount: 0, focus: null, focusStatus: 'none', compile: {errors: []}})
  store.compute('failureGroups', ['results', 'focus'], failureGroups)
  store.compute('groupForFocus', ['failureGroups'], (groups) => groups.find(g => g.containsFocus))
  Object.assign(store, {run, closeCommand, focusGroup})

  Zen.mini = new Zen.Mini({target: document.querySelector('body'), store})
  socket = new WebSocket(`ws://${location.host}/head`)
  socket.onopen = () => run()
  socket.onmessage = serverMessage
})

function serverMessage(msg) {
  let data = JSON.parse(msg.data)

  if (data.results && !data.runId) { // incremental update of results
    store.set({results: store.get().results.concat(data.results)})
  } else {
    // update our state from the server. If our code is out of date, update and re-run the focused test
    store.set(data)
    runIfCodeChanged()
  }
}

function run(opts={}) {
  let params = new URLSearchParams(location.search)
  let grep = opts.hasOwnProperty('grep') ? opts.grep : params.get('grep')
  let focus = opts.hasOwnProperty('focus') ? opts.focus : params.get('focus')
  store.set({grep, focus})

  // Update the url if we're changing grep or focus
  if (opts.hasOwnProperty('grep') || opts.hasOwnProperty('focus')) {
    let sp = new URLSearchParams(location.search)
    grep ? sp.set('grep', grep) : sp.delete('grep')
    focus ? sp.set('focus', focus) : sp.delete('focus')
    history.pushState({}, 'Zen', '?' + sp.toString())
  }

  let grepRegex = grep && new RegExp(grep, 'i')
  let tests = Latte.flatten().filter(t => (!grep || grepRegex.test(t.fullName)))

  let testNames = tests.map(t => t.fullName)
  socket.send(JSON.stringify(Object.assign({grep, testNames}, opts)))

  if (focus) {
    tests = tests.filter(t => t.fullName === focus)
    store.set({focusStatus: 'running'})
    Latte.run(tests).then(() => store.set({focusStatus: 'passed'}))
  }
}

function focusGroup(group) {
  if (typeof group === 'number') {
    let {failureGroups} = store.get()
    let index = failureGroups.findIndex(g => g.containsFocus)
    group = failureGroups[index + group] || failureGroups[0]
  }
  run({focus: group[0].fullName})
}

let onlyAlt = false
function keyDown(ev) {
  onlyAlt = ev.keyCode == 18 && !ev.shiftKey && !ev.metaKey && !ev.ctrlKey
  if (!ev.altKey) return
  let command = Zen.commands.find(c => c.keyCode === ev.keyCode)
  if (command && (!command.condition || command.condition())) {
    ev.preventDefault()
    if (Zen.command) closeCommand()
    command.action()
    onlyAlt = false
  }
}

function keyUp(ev) {
  let altUp = ev.keyCode == 18 && !ev.shiftKey && !ev.metaKey && !ev.ctrlKey
  if (altUp && onlyAlt) {
    if (Zen.command) {
      closeCommand()
    } else {
      Zen.command = new Zen.Command({target: document.querySelector('body'), store})
    }
  }
  onlyAlt = false
}

function closeCommand() {
  Zen.command.destroy()
  Zen.command = null
}

function failureGroups(results, focus) {
  let groups = {}
  results.forEach(t => {
    if (!t.error) return
    if (!t.error.stack) t.error.stack = 'unknown \n unknown'
    let key = t.error.message + t.error.stack.split('\n')[1]
    groups[key] = groups[key] || []
    groups[key].key = key
    groups[key].push(t)
  })

  return Object.values(groups).map(g => {
    g.shade = Math.min(Math.floor(Math.sqrt(g.length)), 6)
    g.containsFocus = !!g.find(r => focus === r.fullName)
    return g
  }).sort((a, b) => b.length - a.length)
}

Zen.commands = [
  {
    type: 'command', title: 'Run the focused test', key: 'Alt Space', keyCode: 32,
    condition: () => store.get().focus,
    action: () => store.run(),
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M4 18c0-7.73 6.27-14 14-14s14 6.27 14 14c0-5.523-4.477-10-10-10s-10 4.477-10 10v2h4l-8 8-8-8h4v-2z"></path></svg>`
  }, {
    type: 'command', title: 'Focus the next failing group', key: 'Alt →', keyCode: 39,
    condition: () => store.get().failureGroups.length > 0,
    action: () => focusGroup(+1),
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M31 16l-15-15v9h-16v12h16v9z"></path></svg>`
  }, {
    type: 'command', title: 'Focus the previous failing group', key: 'Alt ←', keyCode: 37,
    condition: () => store.get().failureGroups.length > 0,
    action: () => focusGroup(-1),
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M31 16l-15-15v9h-16v12h16v9z"></path></svg>`
  // }, {
  //   type: 'command', title: 'Filter to failed tests', key: 'Alt F', keyCode: 70,
  //   condition: () => store.get('failureGroups').length > 0,
  //   action: () => this.store.run({filterFailed: true}),
  //   icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M31 16l-15-15v9h-16v12h16v9z"></path></svg>`
  }, {
    type: 'command', title: 'Run filtered tests', key: 'Alt Enter', keyCode: 13,
    action: () => store.run({force: true}),
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M4 18c0-7.73 6.27-14 14-14s14 6.27 14 14c0-5.523-4.477-10-10-10s-10 4.477-10 10v2h4l-8 8-8-8h4v-2z"></path></svg>`
  }, {
    type: 'command', title: 'Run all tests', key: 'Alt A', keyCode: 65,
    action: () => store.run({focus: null, grep: null, force: true}),
    altText: 'Clear the filter to run every suite',
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 26 26"><path d="M23.156 16.406c0.953 0.547 1.281 1.781 0.734 2.734l-1 1.719c-0.547 0.953-1.781 1.281-2.734 0.734l-4.156-2.391v4.797c0 1.094-0.906 2-2 2h-2c-1.094 0-2-0.906-2-2v-4.797l-4.156 2.391c-0.953 0.547-2.187 0.219-2.734-0.734l-1-1.719c-0.547-0.953-0.219-2.188 0.734-2.734l4.156-2.406-4.156-2.406c-0.953-0.547-1.281-1.781-0.734-2.734l1-1.719c0.547-0.953 1.781-1.281 2.734-0.734l4.156 2.391v-4.797c0-1.094 0.906-2 2-2h2c1.094 0 2 0.906 2 2v4.797l4.156-2.391c0.953-0.547 2.188-0.219 2.734 0.734l1 1.719c0.547 0.953 0.219 2.188-0.734 2.734l-4.156 2.406z"></path></svg>`
  }, {
    type: 'command', title: 'Dev: Reload headless chrome',
    action: () => store.run({reload: true}),
    icon: `<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"><path d="M31 16l-15-15v9h-16v12h16v9z"></path></svg>`
  }
]

let hotReloading = false, compileHasFailed = false
async function runIfCodeChanged() {
  let {compile} = store.get()
  if (compile.status == 'compiling') return
  if (hotReloading) return

  // HMR can't recover from compile errors. Once the build is good again, we need to reload
  if (compile.status == 'error')
    return compileHasFailed = true

  if (compileHasFailed) // after recovering, we need to reload
    return window.location.reload()

  if (!ZenWebpackClient.needsUpdate(compile.hash)) return

  // continue hot reloading until we're up to date. NB that store.compile might change during
  // the async hop, so we we re-`get` it.
  hotReloading = true
  while (window.ZenWebpackClient && ZenWebpackClient.needsUpdate(store.get().compile.hash))
    await ZenWebpackClient.update()
  hotReloading = false

  run()
}
<|endoftext|>


<commit_before>"use strict";

// this is a basic wrapper for making Request() requests
//  we wrap this so we can have the same debug information for all requests

var needle = require("needle");

// get our project Log function for writing log output
var Log = require("./debugPrint");

// include our Promise library
var Promise = require("./promise");

/**
 * Make a network request
 * @private
 * @param parameters to pass to request library
 */
function MakeRequest(networkRequest) {
    if (arguments.length != 1) {
        return Promise.reject("HTTP requires 1 argument. The network object configuration");
    }

    // debug log if we're in debug mode
    Log(`Making request to ${networkRequest.url}`);

    // grab method from the request (we'll call .[method] directly using the needle library)
    var requestMethod = networkRequest.method || "get";
    delete networkRequest.method;

    // extract the required URL
    var requestURL = networkRequest.url;
    if (!requestURL) return Promise.reject(`No URL defined for ${requestMethod} request`);
    delete networkRequest.url;

    var requestData = networkRequest.data || networkRequest.body || {};
    delete networkRequest.data;
    delete networkRequest.body;

    // build-in retires into this wrapper (default 3)
    var retries = networkRequest.retries || 3;
    // un-set retries in-case request suddenly supports this or something!
    delete networkRequest.retries;

    // default delay of 2 seconds for each retry attempt
    var retryDelay = networkRequest.retryDelay || 2000;
    delete networkRequest.retryDelay;

    // we will default to returning the body, but can return the full response object if we want
    var returnFullResponse = networkRequest.returnFullResponse || false;
    delete networkRequest.returnFullResponse;

    // add ability to force responses into JSON objects, even if they don't return application/json content header
    var forceJSON = networkRequest.forceJSON || false;
    delete networkRequest.forceJSON;

    // return result as a Promise!
    return new Promise(function(resolve, reject) {
        var attempt = 0;

        // make request in an anonymouse function so we can make multiple requests to it easily
        var attemptRequest = function() {
            Log(`Calling ${requestMethod}:${requestURL}`);

            // build Needle request
            needle.request(requestMethod, requestURL, requestData, networkRequest, function(err, resp) {
                if (err || resp.statusCode >= 400 || (resp.statusCode == 200 && !resp.body)) {
                    Log(`Network request failed attempt ${attempt}/${retries} for URL ${requestURL}`);
                    Log(err || (resp.statusCode + ": " + JSON.stringify(resp.body, null, 2)));

                    // if we have retires left, try again!
                    attempt++;
                    if (attempt < retries) {
                        // try again after retryDelay milliseconds
                        setTimeout(attemptRequest, retryDelay);
                        return;
                    } else {
                        return reject(err || (resp.statusCode + ": " + JSON.stringify(resp.body, null, 2)), resp.statusCode);
                    }
                }

                // no error! return the result
                if (returnFullResponse) {
                    Log(`Successfully fetched response for URL ${requestURL}`);
                    return resolve(resp);
                } else {
                    // if we want to force JSON (and we're not already a JSON object!)
                    if (forceJSON && resp.body.constructor !== {}.constructor && resp.body.constructor !== [].constructor) {
                        var JSONData;
                        try {
                            JSONData = JSON.parse(resp.body);
                        } catch (e) {
                            return reject(`Unable to parse ${resp.body} into a JSON object: ${e}`);
                        }
                        Log(`Successfully fetched and parsed JSON from response at ${requestURL}`);
                        return resolve(JSONData);
                    } else {
                        Log(`Successfully fetched body for URL ${requestURL}`);
                        return resolve(resp.body);
                    }
                }
            });
        };

        // make first request attempt
        process.nextTick(attemptRequest);
    }.bind(this));
}

module.exports = MakeRequest;<commit_msg>Parse JSON responses from HTTP lib when "application/json" content-type header is set<commit_after>"use strict";

// this is a basic wrapper for making Request() requests
//  we wrap this so we can have the same debug information for all requests

var needle = require("needle");

// get our project Log function for writing log output
var Log = require("./debugPrint");

// include our Promise library
var Promise = require("./promise");

/**
 * Make a network request
 * @private
 * @param parameters to pass to request library
 */
function MakeRequest(networkRequest) {
    if (arguments.length != 1) {
        return Promise.reject("HTTP requires 1 argument. The network object configuration");
    }

    // debug log if we're in debug mode
    Log(`Making request to ${networkRequest.url}`);

    // grab method from the request (we'll call .[method] directly using the needle library)
    var requestMethod = networkRequest.method || "get";
    delete networkRequest.method;

    // extract the required URL
    var requestURL = networkRequest.url;
    if (!requestURL) return Promise.reject(`No URL defined for ${requestMethod} request`);
    delete networkRequest.url;

    var requestData = networkRequest.data || networkRequest.body || {};
    delete networkRequest.data;
    delete networkRequest.body;

    // build-in retires into this wrapper (default 3)
    var retries = networkRequest.retries || 3;
    // un-set retries in-case request suddenly supports this or something!
    delete networkRequest.retries;

    // default delay of 2 seconds for each retry attempt
    var retryDelay = networkRequest.retryDelay || 2000;
    delete networkRequest.retryDelay;

    // we will default to returning the body, but can return the full response object if we want
    var returnFullResponse = networkRequest.returnFullResponse || false;
    delete networkRequest.returnFullResponse;

    // add ability to force responses into JSON objects, even if they don't return application/json content header
    var forceJSON = networkRequest.forceJSON || false;
    delete networkRequest.forceJSON;

    // return result as a Promise!
    return new Promise(function(resolve, reject) {
        var attempt = 0;

        // make request in an anonymouse function so we can make multiple requests to it easily
        var attemptRequest = function() {
            Log(`Calling ${requestMethod}:${requestURL}`);

            // build Needle request
            needle.request(requestMethod, requestURL, requestData, networkRequest, function(err, resp) {
                if (err || resp.statusCode >= 400 || (resp.statusCode == 200 && !resp.body)) {
                    Log(`Network request failed attempt ${attempt}/${retries} for URL ${requestURL}`);
                    Log(err || (resp.statusCode + ": " + JSON.stringify(resp.body, null, 2)));

                    // if we have retires left, try again!
                    attempt++;
                    if (attempt < retries) {
                        // try again after retryDelay milliseconds
                        setTimeout(attemptRequest, retryDelay);
                        return;
                    } else {
                        return reject(err || (resp.statusCode + ": " + JSON.stringify(resp.body, null, 2)), resp.statusCode);
                    }
                }

                // no error! return the result
                if (returnFullResponse) {
                    Log(`Successfully fetched response for URL ${requestURL}`);
                    return resolve(resp);
                } else {
                    // enable "forceJSON" if the return header type is "application/json"
                    if (resp.headers && resp.headers["content-type"] && resp.headers["content-type"].indexOf("application/json") >= 0) {
                        Log("Found 'application/json' header from in HTTP request, parsing JSON data");
                        forceJSON = true;
                    }

                    // if we want to force JSON (and we're not already a JSON object!)
                    if (forceJSON && resp.body.constructor !== {}.constructor && resp.body.constructor !== [].constructor) {
                        var JSONData;
                        try {
                            JSONData = JSON.parse(resp.body);
                        } catch (e) {
                            return reject(`Unable to parse ${resp.body} into a JSON object: ${e}`);
                        }
                        Log(`Successfully fetched and parsed JSON from response at ${requestURL}`);
                        return resolve(JSONData);
                    } else {
                        Log(`Successfully fetched body for URL ${requestURL}`);
                        return resolve(resp.body);
                    }
                }
            });
        };

        // make first request attempt
        process.nextTick(attemptRequest);
    }.bind(this));
}

module.exports = MakeRequest;<|endoftext|>