<commit_before>'use strict';
var fs = require('fs');
var path = require('path');
var gutil = require('gulp-util');
var through = require('through2');
var spawn = require('win-spawn');
var tempWrite = require('temp-write');
var dargs = require('dargs');
var shelljs = require('shelljs');
var chalk = require('chalk');

module.exports = function (options) {
	options = options || {};
	var passedArgs = dargs(options, ['bundleExec']);
	var bundleExec = options.bundleExec;
	var versionTest = bundleExec === true ? 'bundle exec jekyll -v' : 'jekyll -v';

	try {
		shelljs.exec(versionTest, {silent: true});
	}
	catch (err) {
		throw new gutil.PluginError('gulp-ruby-jekyll', 'You need to have Ruby and Jekyll installed and in your PATH for this task to work.');
	}

	return through.obj(function (file, enc, cb) {
		var self = this;

		if (path.basename(file.path)[0] === '_') {
			return cb();
		}

		if (file.isNull()) {
			this.push(file);
			return cb();
		}

		if (file.isStream()) {
			this.emit('error', new gutil.PluginError('gulp-ruby-sass', 'Streaming not supported'));
			return cb();
		}

		var fileDirname = file.base;

		tempWrite(file.contents, path.basename(file.path), function (err, inputTempFile) {
			if (err) {
				self.emit('error', new gutil.PluginError('gulp-ruby-sass', err));
				return cb();
			}

			var outputTempFile = gutil.replaceExtension(inputTempFile, '.css');
			var args = [
				'sass',
				inputTempFile,
				outputTempFile,
				'--load-path', fileDirname
			].concat(passedArgs);

			if (bundleExec) {
				args.unshift('bundle', 'exec');
			}

			// if we're compiling SCSS or CSS files
			if (path.extname(file.path) === '.css') {
				args.push('--scss');
			}

			var cmd = args.shift();
			var cp = spawn(cmd, args);

			if (process.argv.indexOf('--verbose') !== -1) {
				gutil.log('gulp-ruby-sass:', 'Running command:', cmd, chalk.blue(args.join(' ')));
			}

			cp.on('error', function (err) {
				// TODO: remove when gulp has fixed error handling
				gutil.log('[gulp-ruby-sass] ' + err);

				self.emit('error', new gutil.PluginError('gulp-ruby-sass', err));
				return cb();
			});

			var errors = '';
			cp.stderr.setEncoding('utf8');
			cp.stderr.on('data', function (data) {
				// ignore deprecation and empty warnings
				if (/DEPRECATION WARNING/.test(data) || data.trim() === '') {
					return;
				}

				errors += data;
			});

			cp.on('close', function (code) {
				if (errors) {
					// TODO: remove when gulp has fixed error handling
					gutil.log('[gulp-ruby-sass]', '\n' + errors.replace(inputTempFile, file.path).replace('Use --trace for backtrace.\n', ''));

					self.emit('error', new gutil.PluginError('gulp-ruby-sass', '\n' + errors.replace(inputTempFile, file.path).replace('Use --trace for backtrace.\n', '')));
					return cb();
				}

				if (code > 0) {
					self.emit('error', new gutil.PluginError('gulp-ruby-sass', 'Exited with error code ' + code));
					return cb();
				}

				fs.readFile(outputTempFile, function (err, data) {
					if (err) {
						self.emit('error', new gutil.PluginError('gulp-ruby-sass', err));
						return cb();
					}

					var outputFilePath = gutil.replaceExtension(file.path, '.css');

					self.push(new gutil.File({
						base: fileDirname,
						path: outputFilePath,
						contents: data
					}));

					if (!options.sourcemap) {
						return cb();
					}

					fs.readFile(outputTempFile + '.map', function (err, data) {
						if (err) {
							self.emit('error', new gutil.PluginError('gulp-ruby-sass', err));
							return cb();
						}

						self.push(new gutil.File({
							base: fileDirname,
							path: outputFilePath + '.map',
							contents: data
						}));

						cb();
					});
				});
			});
		});
	});
};
<commit_msg>Add notes, delete some unnecessary codes<commit_after>'use strict';
var fs = require('fs');
var path = require('path');
var gutil = require('gulp-util');
var through = require('through2');
var spawn = require('win-spawn');
var tempWrite = require('temp-write');
var dargs = require('dargs');
var shelljs = require('shelljs');
var chalk = require('chalk');

module.exports = function (options) {
	options = options || {};
	var passedArgs = dargs(options, ['bundleExec']);
	var bundleExec = options.bundleExec;
	var versionTest = bundleExec === true ? 'bundle exec jekyll -v' : 'jekyll -v';

	try {
		shelljs.exec(versionTest, {silent: true});
	}
	catch (err) {
		throw new gutil.PluginError('gulp-ruby-jekyll', 'You need to have Ruby and Jekyll installed and in your PATH for this task to work.');
	}

	// NOTE: This looks at single files -- we need to look at the dir as a whole.
	// NOTE: Should source matter / be applied to the input glob? No, let's just
	// say that stream is source, output is dest

	return through.obj(function (file, enc, cb) {
		var self = this;
		// var fileDirname = file.base;

		if (file.isNull()) {
			this.push(file);
			return cb();
		}

		if (file.isStream()) {
			this.emit('error', new gutil.PluginError('gulp-ruby-jekyll', 'Streaming not supported'));
			return cb();
		}

		// NOTE: inputTempFile is a single file based idea.
		tempWrite(file.contents, path.basename(file.path), function (err, inputTempFile) {
			if (err) {
				self.emit('error', new gutil.PluginError('gulp-ruby-jekyll', err));
				return cb();
			}

			// NOTE: set up real args. Let people pass anything except --dest && --source
			var args = [
				// 'sass',
				// inputTempFile,
				// outputTempFile,
				// '--load-path', fileDirname
			].concat(passedArgs);

			if (bundleExec) {
				args.unshift('bundle', 'exec');
			}

			// if we're compiling SCSS or CSS files
			// if (path.extname(file.path) === '.css') {
			// 	args.push('--scss');
			// }

			var cmd = args.shift();
			var cp = spawn(cmd, args);

			// NOTE: Will need this.
			// if (process.argv.indexOf('--verbose') !== -1) {
			// 	gutil.log('gulp-ruby-jekyll:', 'Running command:', cmd, chalk.blue(args.join(' ')));
			// }

			cp.on('error', function (err) {
				// TODO: remove when gulp has fixed error handling
				gutil.log('[gulp-ruby-jekyll] ' + err);

				self.emit('error', new gutil.PluginError('gulp-ruby-jekyll', err));
				return cb();
			});

			// NOTE: Setting up stderr output
			var errors = '';
			cp.stderr.setEncoding('utf8');
			cp.stderr.on('data', function (data) {
				// ignore deprecation and empty warnings
				// if (/DEPRECATION WARNING/.test(data) || data.trim() === '') {
				// 	return;
				// }

				errors += data;
			});

			cp.on('close', function (code) {
				if (errors) {
					// TODO: remove when gulp has fixed error handling
					// NOTE: Probably need to rewrite these two
					// gutil.log('[gulp-ruby-jekyll]', '\n' + errors.replace(inputTempFile, file.path).replace('Use --trace for backtrace.\n', ''));

					// self.emit('error', new gutil.PluginError('gulp-ruby-jekyll', '\n' + errors.replace(inputTempFile, file.path).replace('Use --trace for backtrace.\n', '')));
					return cb();
				}

				if (code > 0) {
					self.emit('error', new gutil.PluginError('gulp-ruby-jekyll', 'Exited with error code ' + code));
					return cb();
				}

				// NOTE: Pull in those new files
				// NOTE: Once again, doing single files here, got to move to dirs
				// fs.readFile(outputTempFile, function (err, data) {
				// 	if (err) {
				// 		self.emit('error', new gutil.PluginError('gulp-ruby-jekyll', err));
				// 		return cb();
				// 	}

				// 	self.push(new gutil.File({
				// 		base: fileDirname,
				// 		contents: data
				// 	}));

				// NOTE: final callback
				// 	cb();
				// });
			});
		});
	});
};
<|endoftext|>


<commit_before>var signals = require('signals');
var EventUtil = require('browser-event-adder');

var Mouse = function(targetElement) {

	this.x = 0;
	this.y = 0;
	this.lastX = 0;
	this.lastY = 0;
	this.relX = 0;
	this.relY = 0;
	this.downX = 0;
	this.downY = 0;
	this.isDown = false;

	//for convenience, move has 3 modes:
	//drag when mouse is down
	//hover when mouse is up
	//move for both
	this.onMoveSignal = new signals.Signal();
	this.onMoveRelativeSignal = new signals.Signal();
	this.onDragSignal = new signals.Signal();
	this.onDragRelativeSignal = new signals.Signal();
	this.onHoverSignal = new signals.Signal();
	this.onHoverRelativeSignal = new signals.Signal();
	this.onDownSignal = new signals.Signal();
	this.onUpSignal = new signals.Signal();
	this.onClickSignal = new signals.Signal();
	this.onOutSignal = new signals.Signal();

	this.start = true;
	this.clickDistanceThreshPixels = 5;
	this.clickDistanceThreshPixelsSquared = this.clickDistanceThreshPixels * this.clickDistanceThreshPixels;

	EventUtil.addEvent(targetElement, 'mousemove', this.onMouseMove.bind(this) );
	EventUtil.addEvent(targetElement, 'mousedown', this.onMouseDown.bind(this) );
	EventUtil.addEvent(targetElement, 'mouseup', this.onMouseUp.bind(this) );
	EventUtil.addEvent(targetElement, 'mouseout', this.onMouseOut.bind(this) );
};

Mouse.prototype = {
	testMovedSinceDown: function() {
		var deltaX = this.downX - this.x;
		var deltaY = this.downY - this.y;
		return ((deltaX * deltaX + deltaY * deltaY) > this.clickDistanceThreshPixelsSquared);
	},

	onMouseMove: function( event ) {
		this.x = event.offsetX;
		this.y = event.offsetY;

		this.onMoveSignal.dispatch(this.x, this.y);
		if(this.isDown) {
			this.onDragSignal.dispatch(this.x, this.y);
		} else {
			this.onHoverSignal.dispatch(this.x, this.y);
		}

		if(this.start) {
			this.start = false;
		} else {
			this.relX = this.x - this.lastX;
			this.relY = this.y - this.lastY;
			this.onMoveRelativeSignal.dispatch(this.relX, this.relY);
			if(this.isDown) {
				this.onDragRelativeSignal.dispatch(this.relX, this.relY);
			} else {
				this.onHoverRelativeSignal.dispatch(this.relX, this.relY);
			}
		}
		this.lastX = this.x;
		this.lastY = this.y;
	},

	onMouseDown: function( event ) {
		this.isDown = true;
		this.downX = this.x;
		this.downY = this.y;
		this.onDownSignal.dispatch(this.x, this.y);
	},

	onMouseUp: function( event ) {
		if(!this.testMovedSinceDown()) {
			this.onClickSignal.dispatch(this.x, this.y);
		}
		this.isDown = false;
		this.onUpSignal.dispatch(this.x, this.y);
	},

	onMouseOut: function( event ) {
		this.isDown = false;
		this.onOutSignal.dispatch();
	},

};

module.exports = Mouse;<commit_msg>cleaned up bind just in case<commit_after>var signals = require('signals');
var EventUtil = require('browser-event-adder');

var Mouse = function(targetElement) {

	this.x = 0;
	this.y = 0;
	this.lastX = 0;
	this.lastY = 0;
	this.relX = 0;
	this.relY = 0;
	this.downX = 0;
	this.downY = 0;
	this.isDown = false;

	//for convenience, move has 3 modes:
	//drag when mouse is down
	//hover when mouse is up
	//move for both
	this.onMoveSignal = new signals.Signal();
	this.onMoveRelativeSignal = new signals.Signal();
	this.onDragSignal = new signals.Signal();
	this.onDragRelativeSignal = new signals.Signal();
	this.onHoverSignal = new signals.Signal();
	this.onHoverRelativeSignal = new signals.Signal();
	this.onDownSignal = new signals.Signal();
	this.onUpSignal = new signals.Signal();
	this.onClickSignal = new signals.Signal();
	this.onOutSignal = new signals.Signal();

	this.start = true;
	this.clickDistanceThreshPixels = 5;
	this.clickDistanceThreshPixelsSquared = this.clickDistanceThreshPixels * this.clickDistanceThreshPixels;

	this.onMouseMove = this.onMouseMove.bind(this);
	this.onMouseDown = this.onMouseDown.bind(this);
	this.onMouseUp = this.onMouseUp.bind(this);
	this.onMouseOut = this.onMouseOut.bind(this);

	EventUtil.addEvent(targetElement, 'mousemove', this.onMouseMove );
	EventUtil.addEvent(targetElement, 'mousedown', this.onMouseDown );
	EventUtil.addEvent(targetElement, 'mouseup', this.onMouseUp );
	EventUtil.addEvent(targetElement, 'mouseout', this.onMouseOut );
};

Mouse.prototype = {
	testMovedSinceDown: function() {
		var deltaX = this.downX - this.x;
		var deltaY = this.downY - this.y;
		return ((deltaX * deltaX + deltaY * deltaY) > this.clickDistanceThreshPixelsSquared);
	},

	onMouseMove: function( event ) {
		this.x = event.offsetX;
		this.y = event.offsetY;

		this.onMoveSignal.dispatch(this.x, this.y);
		if(this.isDown) {
			this.onDragSignal.dispatch(this.x, this.y);
		} else {
			this.onHoverSignal.dispatch(this.x, this.y);
		}

		if(this.start) {
			this.start = false;
		} else {
			this.relX = this.x - this.lastX;
			this.relY = this.y - this.lastY;
			this.onMoveRelativeSignal.dispatch(this.relX, this.relY);
			if(this.isDown) {
				this.onDragRelativeSignal.dispatch(this.relX, this.relY);
			} else {
				this.onHoverRelativeSignal.dispatch(this.relX, this.relY);
			}
		}
		this.lastX = this.x;
		this.lastY = this.y;
	},

	onMouseDown: function( event ) {
		this.isDown = true;
		this.downX = this.x;
		this.downY = this.y;
		this.onDownSignal.dispatch(this.x, this.y);
	},

	onMouseUp: function( event ) {
		if(!this.testMovedSinceDown()) {
			this.onClickSignal.dispatch(this.x, this.y);
		}
		this.isDown = false;
		this.onUpSignal.dispatch(this.x, this.y);
	},

	onMouseOut: function( event ) {
		this.isDown = false;
		this.onOutSignal.dispatch();
	},

};

module.exports = Mouse;<|endoftext|>


<commit_before>var tls = require('tls')
var fs = require('fs')
var _ = require('icebreaker')
if (!_.peer) require('icebreaker-peer')

if (!_.peers) _.mixin({
  peers: {}
})

function isString(s) {
  return typeof s === 'string'
}

function isPath(p) {
  return isString(p) && isNaN(p)
}

function pick(keys, p) {
  var opts = {}
  keys.forEach(function (key) {
    if (key === 'address') return opts.host = !isPath(p['port']) ? p[key] : null
    if (key === 'port' && isPath(p[key])) return opts.path = p[key]
    opts[key] = p[key]
  })
  return opts
}

function connection(original) {
  if (original.setKeepAlive) original.setKeepAlive(true)
  if (original.setNoDelay) original.setNoDelay(true)
  var connection = _.pair(original)
  if (original.remoteAddress) connection.address = original.remoteAddress
  if (original.remotePort) connection.port = original.remotePort
  this.connection(net)
}

_.mixin({
  tls: _.peer({
    name: 'tls',
    auto: true,
    requestCert: false,
    rejectUnauthorized: false,
    start: function () {
      var self = this

      self.server = tls.createServer(
        pick(
          ['key', 'ca', 'cert', 'pfx', 'rejectUnauthorized', 'requestCert'],
          this
        ),
        connection.bind(this)
      )

      self.server.once('error', function (err) {
        if (isPath(self.port) && err.code === 'EADDRINUSE') {
          var socket = tls.connect(
            pick(
              ['key', 'ca', 'cert', 'pfx', 'rejectUnauthorized', 'port'],
              self
            ),
            function () {
              _(
                'peer ' + self.name + ' port ' + self.port +
                ' is already in use by another process.',
                _.log(process.exit.bind(null, 1), 'emerg')
              )
            }
          )

          socket.once('error', function (err) {
            if (err.code == 'ECONNREFUSED') {
              fs.unlink(self.port, function (err) {
                if (err)
                  _(
                    'cannot remove unix socket ' + self.port,
                    _.log(process.exit.bind(null, 1), 'emerg')
                  )
                listen()
              })
            }
            else if (err.code === 'ENOENT') {
              listen()
            }
          })

          return
        }

        _(
        ['cannot start peer' + self.name + ' on port ' + self.port, err],
          _.log(process.exit.bind(null, 1), 'emerg')
        )

      })

      var listen = function (onListening) {
        self.server.listen(
          self.port, isPath(self.port) ? null :
          self.address, onListening
        )
      }

      listen(function () {
        if (isPath(self.port)) fs.chmod(self.port, 0777)
        self.emit('started')
      })
    },

    connect: function (params) {
      var keys = [
        'key', 'ca', 'cert', 'pfx', 'rejectUnauthorized', 'port', 'address'
      ]

      params = pick(keys, params)

      if (!params.ca && this.cert && this.rejectUnauthorized === false)
        params.ca = [this.cert]

      var defaults = pick(keys, this)
      for (var i in defaults) {
        if (!params[i] && defaults[i]) params[i] = defaults[i]
      }

      connection.call(this, tls.connect(params))
    },

    stop: function stop() {
      var self = this
      try {
        self.server.close(function close() {
          if (Object.keys(self.connections).length > 0) {
            process.nextTick(function () {
              close.call(self)
            })
            return
          } else
            self.emit('stopped')
        })
      } catch (e) {
        _([e], _.log(function () {
          self.emit('stopped')
        }), 'error')
      }
    }
  })
},
_.peers)
<commit_msg>Fix undefined variable<commit_after>var tls = require('tls')
var fs = require('fs')
var _ = require('icebreaker')
if (!_.peer) require('icebreaker-peer')

if (!_.peers) _.mixin({
  peers: {}
})

function isString(s) {
  return typeof s === 'string'
}

function isPath(p) {
  return isString(p) && isNaN(p)
}

function pick(keys, p) {
  var opts = {}
  keys.forEach(function (key) {
    if (key === 'address') return opts.host = !isPath(p['port']) ? p[key] : null
    if (key === 'port' && isPath(p[key])) return opts.path = p[key]
    opts[key] = p[key]
  })
  return opts
}

function connection(original) {
  if (original.setKeepAlive) original.setKeepAlive(true)
  if (original.setNoDelay) original.setNoDelay(true)
  var connection = _.pair(original)
  if (original.remoteAddress) connection.address = original.remoteAddress
  if (original.remotePort) connection.port = original.remotePort
  this.connection(connection)
}

_.mixin({
  tls: _.peer({
    name: 'tls',
    auto: true,
    requestCert: false,
    rejectUnauthorized: false,
    start: function () {
      var self = this

      self.server = tls.createServer(
        pick(
          ['key', 'ca', 'cert', 'pfx', 'rejectUnauthorized', 'requestCert'],
          this
        ),
        connection.bind(this)
      )

      self.server.once('error', function (err) {
        if (isPath(self.port) && err.code === 'EADDRINUSE') {
          var socket = tls.connect(
            pick(
              ['key', 'ca', 'cert', 'pfx', 'rejectUnauthorized', 'port'],
              self
            ),
            function () {
              _(
                'peer ' + self.name + ' port ' + self.port +
                ' is already in use by another process.',
                _.log(process.exit.bind(null, 1), 'emerg')
              )
            }
          )

          socket.once('error', function (err) {
            if (err.code == 'ECONNREFUSED') {
              fs.unlink(self.port, function (err) {
                if (err)
                  _(
                    'cannot remove unix socket ' + self.port,
                    _.log(process.exit.bind(null, 1), 'emerg')
                  )
                listen()
              })
            }
            else if (err.code === 'ENOENT') {
              listen()
            }
          })

          return
        }

        _(
        ['cannot start peer' + self.name + ' on port ' + self.port, err],
          _.log(process.exit.bind(null, 1), 'emerg')
        )

      })

      var listen = function (onListening) {
        self.server.listen(
          self.port, isPath(self.port) ? null :
          self.address, onListening
        )
      }

      listen(function () {
        if (isPath(self.port)) fs.chmod(self.port, 0777)
        self.emit('started')
      })
    },

    connect: function (params) {
      var keys = [
        'key', 'ca', 'cert', 'pfx', 'rejectUnauthorized', 'port', 'address'
      ]

      params = pick(keys, params)

      if (!params.ca && this.cert && this.rejectUnauthorized === false)
        params.ca = [this.cert]

      var defaults = pick(keys, this)
      for (var i in defaults) {
        if (!params[i] && defaults[i]) params[i] = defaults[i]
      }

      connection.call(this, tls.connect(params))
    },

    stop: function stop() {
      var self = this
      try {
        self.server.close(function close() {
          if (Object.keys(self.connections).length > 0) {
            process.nextTick(function () {
              close.call(self)
            })
            return
          } else
            self.emit('stopped')
        })
      } catch (e) {
        _([e], _.log(function () {
          self.emit('stopped')
        }), 'error')
      }
    }
  })
},
_.peers)
<|endoftext|>


<commit_before>'use strict';

function parse(json, fallback) {
    try {
        return JSON.parse(json);
    } catch (e) {
        return fallback;
    }
}

function stringify(object, fallback) {
    try {
        if (object === undefined) throw undefined;
        return JSON.stringify(object);
    } catch (e) {
        return JSON.stringify(fallback === undefined ? null : fallback);
    }
}

module.exports = {
    parse: parse,
    stringify: stringify,
};
<commit_msg>add support for invalid fallbacks<commit_after>'use strict';

function parse(json, fallback) {
    try {
        return JSON.parse(json);
    } catch (e) {
        return fallback;
    }
}

function stringify(object, fallback) {
    try {
        if (object === undefined) throw undefined;
        return JSON.stringify(object);
    } catch (e) {
        return fallback === undefined ? JSON.stringify(null) : stringify(fallback);
    }
}

module.exports = {
    parse: parse,
    stringify: stringify,
};
<|endoftext|>


<commit_before>'use strict';

var fs = require('graceful-fs');
var path = require('path');
var extend = require('extend');
var errcode = require('err-code');
var retry = require('retry');
var async = require('async');
var uuid = require('uuid');

var locks = {};

function getLockFile(file) {
    return file + '.lock';
}

function getUidFile(file) {
    return path.join(getLockFile(file), '.uid');
}

function canonicalPath(file, options, callback) {
    if (!options.resolve) {
        return callback(null, path.normalize(file));
    }

    options.fs.realpath(file, callback);
}

function acquireLock(file, options, callback) {
    // Fast fail if a lock is acquired here
    if (locks[file]) {
        return callback(errcode('Lock file is already being hold', 'ELOCKED', { file: file }));
    }

    // Use mkdir to create the lockfile (atomic operation)
    options.fs.mkdir(getLockFile(file), function (err) {
        var uid;

        // If we successfuly created the lockfile,
        // write the uidfile and we are done
        if (!err) {
            uid = uuid.v4();
            return options.fs.writeFile(getUidFile(file), uid, function (err) {
                if (err) {
                    return removeLock(file, options, function () {
                        callback(err);
                    });
                }

                return callback(null, uid);
            });
        }

        // Otherwise, check if lock is stale by analyzing the file mtime
        if (options.stale <= 0) {
            return callback(errcode('Lock file is already being hold', 'ELOCKED', { file: file }));
        }

        options.fs.stat(getLockFile(file), function (err, stat) {
            if (err) {
                // Retry if the lockfile has been removed (meanwhile)
                // Skip stale check to avoid recursiveness
                if (err.code === 'ENOENT') {
                    return acquireLock(file, extend({}, options, { stale: 0 }), callback);
                }

                return callback(err);
            }

            if (stat.mtime.getTime() >= Date.now() - options.stale) {
                return callback(errcode('Lock file is already being hold', 'ELOCKED', { file: file }));
            }

            // If it's stale, remove it and try again!
            // Skip stale check to avoid recursiveness
            removeLock(file, options, function (err) {
                if (err) {
                    return callback(err);
                }

                acquireLock(file, extend({}, options, { stale: 0 }), callback);
            });
        });
    });
}

function removeLock(file, options, callback) {
    // Remove uidfile, ignoring ENOENT errors
    options.fs.unlink(getUidFile(file), function (err) {
        if (err && err.code !== 'ENOENT') {
            return callback(err);
        }

        // Remove lockfile, ignoring ENOENT errors
        options.fs.rmdir(getLockFile(file), function (err) {
            if (err && err.code !== 'ENOENT') {
                return callback(err);
            }

            callback();
        });
    });
}

function updateLock(file, options) {
    var lock = locks[file];

    lock.updateDelay = lock.updateDelay || options.update;
    lock.updateTimeout = setTimeout(function () {
        var mtime = Date.now() / 1000;

        lock.updateTimeout = null;

        async.parallel({
            read: options.fs.readFile.bind(options.fs, getUidFile(file)),
            utimes: options.fs.utimes.bind(options.fs, getLockFile(file), mtime, mtime),
        }, function (err, result) {
            // Ignore if the lock was released
            if (lock.released) {
                return;
            }

            // Verify if we are within the stale threshold
            if (lock.lastUpdate <= Date.now() - options.stale) {
                return unlock(file, extend({}, options, { resolve: false }), function () {
                    lock.compromised(lock.updateError || errcode('Unable to update lock within the stale threshold', 'EUPDATE'));
                });
            }

            // If it failed to update the lockfile, keep trying unless
            // the lockfile/uidfile was deleted!
            if (err) {
                if (err.code === 'ENOENT') {
                    return unlock(file, extend({}, options, { resolve: false }), function () {
                        lock.compromised(err);
                    });
                }

                lock.updateError = err;
                lock.updateDelay = 1000;
                return updateLock(file, options);
            }

            // Verify lock uid
            if (result.read.toString().trim() !== lock.uid) {
                lock.released = true;
                delete locks[file];
                return lock.compromised(errcode('Lock uid mismatch', 'EMISMATCH'));
            }

            // All ok, keep updating..
            lock.lastUpdate = Date.now();
            lock.updateError = null;
            lock.updateDelay = null;
            updateLock(file, options);
        });
    }, lock.updateDelay);
}

// -----------------------------------------

function lock(file, options, compromised, callback) {
    if (typeof options === 'function') {
        callback = compromised;
        compromised = options;
        options = null;
    }

    if (!callback) {
        callback = compromised;
        compromised = null;
    }

    options = extend({
        stale: 10000,   // 10 secs
        update: 5000,   // 5 secs
        resolve: true,
        retries: 0,
        fs: fs
    }, options);

    options.retries = options.retries || 0;
    options.retries = typeof options.retries === 'number' ? { retries: options.retries } : options.retries;
    options.stale = Math.max(options.stale || 0, 2000);
    options.update = Math.max(Math.min(options.update || 0, Math.round(options.stale / 2)), 1000);
    compromised = compromised || function (err) { throw err; };

    // Resolve to a canonical file path
    canonicalPath(file, options, function (err, file) {
        var operation;

        if (err) {
            return callback(err);
        }

        // Attempt to acquire the lock
        operation = retry.operation(options.retries);
        operation.attempt(function () {
            acquireLock(file, options, function (err, uid) {
                var lock;

                if (operation.retry(err)) {
                    return;
                }

                if (err) {
                    return callback(operation.mainError());
                }

                // We now own the lock
                locks[file] = lock = {
                    uid: uid,
                    options: options,
                    compromised: compromised,
                    lastUpdate: Date.now()
                };

                // We must keep the lock fresh to avoid staleness
                updateLock(file, options);

                callback(null, function (releasedCallback) {
                    releasedCallback = releasedCallback || function () {};

                    if (lock.released) {
                        return releasedCallback(errcode('Lock is already released', 'ERELEASED'));
                    }

                    // Not necessary to resolve twice when unlocking
                    unlock(file, extend({}, options, { resolve: false }), releasedCallback);
                });
            });
        });
    });
}

function unlock(file, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = null;
    }

    options = extend({
        fs: fs,
        resolve: true
    }, options);

    callback = callback || function () {};

    // Resolve to a canonical file path
    canonicalPath(file, options, function (err, file) {
        var lock;

        if (err) {
            return callback(err);
        }

        // Skip if the lock is not acquired
        lock = locks[file];
        if (!lock) {
            return callback(errcode('Lock is not acquired', 'ENOTACQUIRED'));
        }

        lock.updateTimeout && clearTimeout(lock.updateTimeout);  // Cancel lock mtime update
        lock.released = true;                                    // Signal the lock has been released
        delete locks[file];                                      // Delete from locks

        removeLock(file, options, callback);
    });
}

// Remove acquired locks on exit
/* istanbul ignore next */
process.on('exit', function () {
    Object.keys(locks).forEach(function (file) {
        try {
            locks[file].options.fs.unlinkSync.sync(getUidFile(file));
            locks[file].options.fs.rmdirSync.sync(getLockFile(file));
        } catch (e) {}
    });
});

module.exports = lock;
module.exports.lock = lock;
module.exports.unlock = unlock;
<commit_msg>Fix default value for update.<commit_after>'use strict';

var fs = require('graceful-fs');
var path = require('path');
var extend = require('extend');
var errcode = require('err-code');
var retry = require('retry');
var async = require('async');
var uuid = require('uuid');

var locks = {};

function getLockFile(file) {
    return file + '.lock';
}

function getUidFile(file) {
    return path.join(getLockFile(file), '.uid');
}

function canonicalPath(file, options, callback) {
    if (!options.resolve) {
        return callback(null, path.normalize(file));
    }

    options.fs.realpath(file, callback);
}

function acquireLock(file, options, callback) {
    // Fast fail if a lock is acquired here
    if (locks[file]) {
        return callback(errcode('Lock file is already being hold', 'ELOCKED', { file: file }));
    }

    // Use mkdir to create the lockfile (atomic operation)
    options.fs.mkdir(getLockFile(file), function (err) {
        var uid;

        // If we successfuly created the lockfile,
        // write the uidfile and we are done
        if (!err) {
            uid = uuid.v4();
            return options.fs.writeFile(getUidFile(file), uid, function (err) {
                if (err) {
                    return removeLock(file, options, function () {
                        callback(err);
                    });
                }

                return callback(null, uid);
            });
        }

        // Otherwise, check if lock is stale by analyzing the file mtime
        if (options.stale <= 0) {
            return callback(errcode('Lock file is already being hold', 'ELOCKED', { file: file }));
        }

        options.fs.stat(getLockFile(file), function (err, stat) {
            if (err) {
                // Retry if the lockfile has been removed (meanwhile)
                // Skip stale check to avoid recursiveness
                if (err.code === 'ENOENT') {
                    return acquireLock(file, extend({}, options, { stale: 0 }), callback);
                }

                return callback(err);
            }

            if (stat.mtime.getTime() >= Date.now() - options.stale) {
                return callback(errcode('Lock file is already being hold', 'ELOCKED', { file: file }));
            }

            // If it's stale, remove it and try again!
            // Skip stale check to avoid recursiveness
            removeLock(file, options, function (err) {
                if (err) {
                    return callback(err);
                }

                acquireLock(file, extend({}, options, { stale: 0 }), callback);
            });
        });
    });
}

function removeLock(file, options, callback) {
    // Remove uidfile, ignoring ENOENT errors
    options.fs.unlink(getUidFile(file), function (err) {
        if (err && err.code !== 'ENOENT') {
            return callback(err);
        }

        // Remove lockfile, ignoring ENOENT errors
        options.fs.rmdir(getLockFile(file), function (err) {
            if (err && err.code !== 'ENOENT') {
                return callback(err);
            }

            callback();
        });
    });
}

function updateLock(file, options) {
    var lock = locks[file];

    lock.updateDelay = lock.updateDelay || options.update;
    lock.updateTimeout = setTimeout(function () {
        var mtime = Date.now() / 1000;

        lock.updateTimeout = null;

        async.parallel({
            read: options.fs.readFile.bind(options.fs, getUidFile(file)),
            utimes: options.fs.utimes.bind(options.fs, getLockFile(file), mtime, mtime),
        }, function (err, result) {
            // Ignore if the lock was released
            if (lock.released) {
                return;
            }

            // Verify if we are within the stale threshold
            if (lock.lastUpdate <= Date.now() - options.stale) {
                return unlock(file, extend({}, options, { resolve: false }), function () {
                    lock.compromised(lock.updateError || errcode('Unable to update lock within the stale threshold', 'EUPDATE'));
                });
            }

            // If it failed to update the lockfile, keep trying unless
            // the lockfile/uidfile was deleted!
            if (err) {
                if (err.code === 'ENOENT') {
                    return unlock(file, extend({}, options, { resolve: false }), function () {
                        lock.compromised(err);
                    });
                }

                lock.updateError = err;
                lock.updateDelay = 1000;
                return updateLock(file, options);
            }

            // Verify lock uid
            if (result.read.toString().trim() !== lock.uid) {
                lock.released = true;
                delete locks[file];
                return lock.compromised(errcode('Lock uid mismatch', 'EMISMATCH'));
            }

            // All ok, keep updating..
            lock.lastUpdate = Date.now();
            lock.updateError = null;
            lock.updateDelay = null;
            updateLock(file, options);
        });
    }, lock.updateDelay);
}

// -----------------------------------------

function lock(file, options, compromised, callback) {
    if (typeof options === 'function') {
        callback = compromised;
        compromised = options;
        options = null;
    }

    if (!callback) {
        callback = compromised;
        compromised = null;
    }

    options = extend({
        stale: 10000,
        update: null,
        resolve: true,
        retries: 0,
        fs: fs
    }, options);

    options.retries = options.retries || 0;
    options.retries = typeof options.retries === 'number' ? { retries: options.retries } : options.retries;
    options.stale = Math.max(options.stale || 0, 2000);
    options.update = options.update == null ? options.stale / 2 : options.update || 0;
    options.update = Math.max(Math.min(options.update, options.stale / 2), 1000);
    compromised = compromised || function (err) { throw err; };

    // Resolve to a canonical file path
    canonicalPath(file, options, function (err, file) {
        var operation;

        if (err) {
            return callback(err);
        }

        // Attempt to acquire the lock
        operation = retry.operation(options.retries);
        operation.attempt(function () {
            acquireLock(file, options, function (err, uid) {
                var lock;

                if (operation.retry(err)) {
                    return;
                }

                if (err) {
                    return callback(operation.mainError());
                }

                // We now own the lock
                locks[file] = lock = {
                    uid: uid,
                    options: options,
                    compromised: compromised,
                    lastUpdate: Date.now()
                };

                // We must keep the lock fresh to avoid staleness
                updateLock(file, options);

                callback(null, function (releasedCallback) {
                    releasedCallback = releasedCallback || function () {};

                    if (lock.released) {
                        return releasedCallback(errcode('Lock is already released', 'ERELEASED'));
                    }

                    // Not necessary to resolve twice when unlocking
                    unlock(file, extend({}, options, { resolve: false }), releasedCallback);
                });
            });
        });
    });
}

function unlock(file, options, callback) {
    if (typeof options === 'function') {
        callback = options;
        options = null;
    }

    options = extend({
        fs: fs,
        resolve: true
    }, options);

    callback = callback || function () {};

    // Resolve to a canonical file path
    canonicalPath(file, options, function (err, file) {
        var lock;

        if (err) {
            return callback(err);
        }

        // Skip if the lock is not acquired
        lock = locks[file];
        if (!lock) {
            return callback(errcode('Lock is not acquired', 'ENOTACQUIRED'));
        }

        lock.updateTimeout && clearTimeout(lock.updateTimeout);  // Cancel lock mtime update
        lock.released = true;                                    // Signal the lock has been released
        delete locks[file];                                      // Delete from locks

        removeLock(file, options, callback);
    });
}

// Remove acquired locks on exit
/* istanbul ignore next */
process.on('exit', function () {
    Object.keys(locks).forEach(function (file) {
        try {
            locks[file].options.fs.unlinkSync.sync(getUidFile(file));
            locks[file].options.fs.rmdirSync.sync(getLockFile(file));
        } catch (e) {}
    });
});

module.exports = lock;
module.exports.lock = lock;
module.exports.unlock = unlock;
<|endoftext|>


<commit_before>'use strict';
var ghGot = require('gh-got');

module.exports = function (repo, cb) {
	ghGot('repos/' + repo, function (err, data) {
		if (err && err.code === 404) {
			cb(null, false);
			return;
		}

		if (err) {
			cb(err);
			return;
		}

		cb(null, true);
	});
};
<commit_msg>Fix XO warning<commit_after>'use strict';
var ghGot = require('gh-got');

module.exports = function (repo, cb) {
	ghGot('repos/' + repo, function (err) {
		if (err && err.code === 404) {
			cb(null, false);
			return;
		}

		if (err) {
			cb(err);
			return;
		}

		cb(null, true);
	});
};
<|endoftext|>


<commit_before>"use strict";

// Constants
var STATUS_CODES = require( "http" ).STATUS_CODES;
var EXPANDED_URL_HEADER = "X-Expanded-Url";

// Modules
var https = require( "https" );
var bower = require( "bower" );
var mime = require( "mime" );

// Express and middlewares
var express = require( "express" );
var compression = require( "compression" );
var Promise = require( "bluebird" );

// -------------------------------------------------------------------------------------------------

var app = express();

// Set the default mime type to an object, so we know that it didn't found anything that could
// conflict with a valid mimetype.
mime.default_type = {};

app.use( compression({
    threshold: 1024
}));

app.route( "/:package/:version/*" ).get(function( req, res ) {
    var pkgname = req.params.package;
    var ver = req.params.version.trim().replace( /^v/, "" );

    var cmd = bower.commands.lookup( pkgname );
    cmd.on( "end", function( pkg ) {
        var slug = pkg ? getSlug( pkg.url ) : null;

        if ( !pkg || !slug ) {
            return send( res, 404 );
        }

        cmd = bower.commands.info( pkgname );
        cmd.on( "end", function( pkg ) {
            var promises;

            if ( !~pkg.versions.indexOf( ver ) ) {
                return send( res, 404 );
            }

            promises = [ "v", "" ].map(function( prefix ) {
                return new Promise(function( resolve, reject ) {
                    var url = getGithubUrl(slug);
                    url += prefix + ver + "/";
                    url += req.params[ 0 ];

                    https.get( url, function( ghResp ) {
                        var method = ghResp.statusCode >= 400 ? reject : resolve;
                        method({
                            gh: ghResp,
                            url: url
                        });
                    });
                });
            });

            Promise.any( promises ).then(function( resp ) {
                var type = findMimetype( resp.url, resp.gh );

                res.statusCode = resp.gh.statusCode;
                res.set( "Content-Type", type );
                res.set( EXPANDED_URL_HEADER, resp.url );

                resp.gh.pipe( res );
            }, function( err ) {
                var resp = err[ 0 ];
                send( res, resp.gh.status, resp.url );
            });
        });
    });
});

app.route( "/*" ).all(function( req, res ) {
    send( res, 404 );
});

app.listen( process.env.PORT || 3000, function() {
    console.log( "Listening on port " + this.address().port );
});

// -------------------------------------------------------------------------------------------------

function getSlug( url ) {
    // Regexp taken from
    // https://github.com/bower/bower/blob/337c0f2d0adc4a04a8c282f8fa2eef85b48616a4/lib/core/resolvers/GitHubResolver.js#L114
    var match = url.match( /(?:@|:\/\/)github.com[:\/]([^\/\s]+?)\/([^\/\s]+?)(?:\.git)?\/?$/i );
    if ( !match ) {
        return;
    }

    return match[ 1 ] + "/" + match[ 2 ];
}

function getGithubUrl( slug ) {
    return "https://raw.githubusercontent.com/" + slug + "/";
}

function findMimetype( url, ghResp ) {
    var type = mime.lookup( url );

    // If the defaule mime type was used, this means we should rely on GitHub and return their
    // content-type (which is usually plain text or octet-stream).
    // Otherwise, we simply use the mime module returned value.
    return type === mime.default_type ? ghResp.headers[ "content-type" ] : type;
}

function send( res, status, url ) {
    res.set( "Content-Type", "text/plain" );
    if ( url ) {
        res.set( EXPANDED_URL_HEADER, url );
    }

    return res.send( status, STATUS_CODES[ status ] );
}<commit_msg>feat(server): pipe cache headers<commit_after>"use strict";

// Constants
var STATUS_CODES = require( "http" ).STATUS_CODES;
var EXPANDED_URL_HEADER = "X-Expanded-Url";

// Modules
var https = require( "https" );
var bower = require( "bower" );
var mime = require( "mime" );
var url = require( "url" );

// Express and middlewares
var express = require( "express" );
var compression = require( "compression" );
var Promise = require( "bluebird" );

// -------------------------------------------------------------------------------------------------

var app = express();

// Set the default mime type to an object, so we know that it didn't found anything that could
// conflict with a valid mimetype.
mime.default_type = {};

app.use( compression({
    threshold: 1024
}));

app.route( "/:package/:version/*" ).get(function( req, res ) {
    var reqEtag = req.get( "if-none-match" );
    var pkgname = req.params.package;
    var ver = req.params.version.trim().replace( /^v/, "" );

    var cmd = bower.commands.lookup( pkgname );
    cmd.on( "end", function( pkg ) {
        var slug = pkg ? getSlug( pkg.url ) : null;

        if ( !pkg || !slug ) {
            return send( res, 404 );
        }

        cmd = bower.commands.info( pkgname );
        cmd.on( "end", function( pkg ) {
            var promises;

            if ( !~pkg.versions.indexOf( ver ) ) {
                return send( res, 404 );
            }

            promises = [ "v", "" ].map(function( prefix ) {
                return new Promise(function( resolve, reject ) {
                    var options;
                    var ghUrl = getGithubUrl( slug );
                    ghUrl += prefix + ver + "/";
                    ghUrl += req.params[ 0 ];

                    options = url.parse( ghUrl );
                    options.headers = {
                        "if-none-match": reqEtag
                    };

                    https.get( options, function( ghResp ) {
                        var method = ghResp.statusCode >= 400 ? reject : resolve;
                        method({
                            gh: ghResp,
                            url: ghUrl
                        });
                    });
                });
            });

            Promise.any( promises ).then(function( resp ) {
                var etag = resp.gh.headers.etag;
                var type = findMimetype( resp.url, resp.gh );

                res.statusCode = resp.gh.statusCode;
                res.set( "Content-Type", type );
                res.set( "ETag", etag );
                res.set( EXPANDED_URL_HEADER, resp.url );

                resp.gh.pipe( res );
            }, function( err ) {
                var resp = err[ 0 ];
                send( res, resp.gh.status, resp.url );
            });
        });
    });
});

app.route( "/*" ).all(function( req, res ) {
    send( res, 404 );
});

app.listen( process.env.PORT || 3000, function() {
    console.log( "Listening on port " + this.address().port );
});

// -------------------------------------------------------------------------------------------------

function getSlug( url ) {
    // Regexp taken from
    // https://github.com/bower/bower/blob/337c0f2d0adc4a04a8c282f8fa2eef85b48616a4/lib/core/resolvers/GitHubResolver.js#L114
    var match = url.match( /(?:@|:\/\/)github.com[:\/]([^\/\s]+?)\/([^\/\s]+?)(?:\.git)?\/?$/i );
    if ( !match ) {
        return;
    }

    return match[ 1 ] + "/" + match[ 2 ];
}

function getGithubUrl( slug ) {
    return "https://raw.githubusercontent.com/" + slug + "/";
}

function findMimetype( url, ghResp ) {
    var type = mime.lookup( url );

    // If the defaule mime type was used, this means we should rely on GitHub and return their
    // content-type (which is usually plain text or octet-stream).
    // Otherwise, we simply use the mime module returned value.
    return type === mime.default_type ? ghResp.headers[ "content-type" ] : type;
}

function send( res, status, url ) {
    res.set( "Content-Type", "text/plain" );
    if ( url ) {
        res.set( EXPANDED_URL_HEADER, url );
    }

    return res.send( status, STATUS_CODES[ status ] );
}<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

require('shelljs/global');
var argv = require('yargs')
  .boolean('update')
  .argv;

var prepareUpdate = require('./update').prepareUpdate;

function safeOutput(str) {
  return str.replace(new RegExp(process.env.GH_TOKEN, 'g'), 'xxPASSxx');
}

function safeExec(command) {
  set('+v');
  set('+e');
  console.log(safeOutput(command));
  var res = exec(command, {silent:true});
  if (res.code == 0) {
    console.log(safeOutput(res.stdout));
  } else {
    console.error(safeOutput(res.stderr));
    process.exit(1);
  }
  set('-e');
  set('-v');
  return res;
}

set('-e');
set('-v');

if (argv._.length < 1) {
  console.error('Missed argument <folder>:\nUsage: deploy-to-gh-pages [--update] <folder>');
  process.exit(1);
}

if (!process.env.GH_TOKEN) {
  console.error('You need to setup GH_TOKEN environment variable')
  process.exit(1);
}

var folder = argv._[0];
var GH_REPO=exec('git config --get remote.origin.url').stdout;
GH_REPO = GH_REPO && GH_REPO.trim();
if (GH_REPO.substring(0, 8) !== 'https://') {
  console.log('This tool works only for https:// protocol');
  process.exit(1);
}

function doRelease() {
  if (argv.update) {
    prepareUpdate(folder, GH_REPO);
  }

  cd(folder);
  exec('git init')
  exec('git config user.name "Travis-CI"');
  exec('git config user.email "travis@travis"');

  exec('git add .');
  exec('git commit -m "Deployed to Github Pages"');

  var GH_URL= GH_REPO.replace('://', '://' + process.env.GH_TOKEN + '@');
  safeExec('git push --force "' + GH_URL + '" master:gh-pages');
}

try {
  doRelease();
} catch(e) {
  console.log(safeOutput(e.message));
  process.exit(1);
}

console.log('Deployed successfully');
<commit_msg>Add --repo argument<commit_after>#!/usr/bin/env node
'use strict';

require('shelljs/global');
var argv = require('yargs')
  .boolean('update')
  .argv;

var prepareUpdate = require('./update').prepareUpdate;

function safeOutput(str) {
  return str.replace(new RegExp(process.env.GH_TOKEN, 'g'), 'xxPASSxx');
}

function safeExec(command) {
  set('+v');
  set('+e');
  console.log(safeOutput(command));
  var res = exec(command, {silent:true});
  if (res.code == 0) {
    console.log(safeOutput(res.stdout));
  } else {
    console.error(safeOutput(res.stderr));
    process.exit(1);
  }
  set('-e');
  set('-v');
  return res;
}

set('-e');
set('-v');

if (argv._.length < 1) {
  console.error('Missed argument <folder>:\nUsage: deploy-to-gh-pages [--repo <repo>] [--update] <folder>');
  process.exit(1);
}

if (!process.env.GH_TOKEN) {
  console.error('You need to setup GH_TOKEN environment variable')
  process.exit(1);
}

var folder = argv._[0];

function getRepoUrl() {
  if (argv.repo) {
    if (argv.repo.split('/').length !== 2)
      throw Error('Repo should be specified as "<org_name|user_name>/<repo_name>"');
    return 'https://github.com/' + argv.repo + '.git';
  }

  var repo = exec('git config --get remote.origin.url').stdout;
  repo = repo && repo.trim();
  if (repo.substring(0, 8) !== 'https://')
    throw Error('This tool works only for https:// protocol');
  return repo;
}

function doRelease() {
  var GH_REPO = getRepoUrl();

  if (argv.update) {
    prepareUpdate(folder, GH_REPO);
  }

  cd(folder);
  exec('git init')
  exec('git config user.name "Travis-CI"');
  exec('git config user.email "travis@travis"');

  exec('git add .');
  exec('git commit -m "Deployed to Github Pages"');

  var GH_URL= GH_REPO.replace('://', '://' + process.env.GH_TOKEN + '@');
  safeExec('git push --force "' + GH_URL + '" master:gh-pages');
}

try {
  doRelease();
} catch(e) {
  console.log(safeOutput(e.message));
  process.exit(1);
}

console.log('Deployed successfully');
<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var path = require('path');
var merge = require('lodash/merge');
var spawn = require('child_process').spawn;
var fsExtra = require('fs-extra');

function Plugin(translationOptions) {
  var defaultOptions = {
    conf: './jsdoc.conf'
  };

  this.options = merge({}, defaultOptions, translationOptions);
}

/**
 * Spawns a jsdoc process.
 * It will look for the binary in the basePath passed.
 * @param  {String} basePath - the basepath
 * @param  {Object} obj      - config object
 * @return {Promise} with an array of errors, if any
 */
function spawnJsDoc (basePath, obj) {
  return new Promise((resolve, reject) => {
    var jsdocErrors = [];
    var spawnErr = false;
    var cwd = process.cwd();
    var jsDocConfTmp = path.resolve(cwd, 'jsdoc.' + Date.now() + '.conf.tmp');
    var command = /^win/.test(process.platform) ? 'jsdoc.cmd' : 'jsdoc';
    var jsdoc;

    fs.writeFileSync(jsDocConfTmp, JSON.stringify(obj));

    if (typeof basePath === 'string') {
      jsdoc = spawn(path.resolve(basePath, command), ['-c', jsDocConfTmp])
    } else {
      jsdoc = spawn('jsdoc', ['-c', jsDocConfTmp])
    }

    jsdoc.on('error', (err) => {
      spawnErr = err;
    });
    jsdoc.stderr.on('data', function (data) {
      jsdocErrors.push(data.toString());
    });
    jsdoc.on('close', function (data, code) {
      jsdocErrors = jsdocErrors.join('\n').split('\n').filter((item) => { return item !== ''; });
      jsdocErrors.forEach((message, index, list) => {
        list[index] = new Error(message);
      });
      fs.unlink(jsDocConfTmp, function (err) {
        if (err) return reject(err);
        if (spawnErr) return reject(spawnErr);
        resolve(jsdocErrors);
      });
    });
  });
}

Plugin.prototype.apply = function (compiler) {
  var self = this;
  var options = self.options;

  compiler.plugin('watch-run', function (watching, callback) {
    self.webpackIsWatching = true;
    callback(null, null);
  });

  compiler.plugin('emit', function (compilation, callback) {
    console.log('JSDOC Start generating');

    fsExtra.readJson(path.resolve(process.cwd(), options.conf), function (err, obj) {
      var files = [];
      if(err) {
        callback(err);
        return;
      }

      if (obj.source && obj.source.include) {
        console.log('Taking sources from config file');
      } else {
        compilation.chunks.forEach(function (chunk) {
          chunk.modules.forEach(function (module) {
            if (module.fileDependencies) {
              module.fileDependencies.forEach(function (filepath) {
                files.push(path.relative(process.cwd(), filepath));
              });
            }
          });
        });
        merge(obj.source, { include: files });
      }

      /**
       * First try to spawn the jsdoc command from `node_modules/jsdoc-webpack-plugin/node_modules/.bin` path
       */
      spawnJsDoc(`${__dirname}/node_modules/.bin/`, obj).then((errs) => {
        if (errs && errs.length > 0) compilation.errors = compilation.errors.concat(errs);
        callback();
      }).catch((err) => {
        if (err.code === "ENOENT") {
            /**
             * Finally try to let node find it
             */
            return spawnJsDoc(null, obj);
        } else {
          return Promise.reject(err);
        }
      }).then((errs) => {
        if (errs && errs.length > 0) compilation.errors = compilation.errors.concat(errs);
        callback();
      }).catch((err) => {
        if (err.code === "ENOENT") {
          compilation.errors.push(new Error('JSDOC not found.'))
        } else {
          compilation.errors.push(err);
        }
        callback();
      });

    });
  });

};

module.exports = Plugin;
<commit_msg>Webpack 4.5 support added<commit_after>'use strict';

var fs = require('fs');
var path = require('path');
var merge = require('lodash/merge');
var spawn = require('child_process').spawn;
var fsExtra = require('fs-extra');

function Plugin(translationOptions) {
  var defaultOptions = {
    conf: './jsdoc.conf'
  };

  this.options = merge({}, defaultOptions, translationOptions);
}

/**
 * Spawns a jsdoc process.
 * It will look for the binary in the basePath passed.
 * @param  {String} basePath - the basepath
 * @param  {Object} obj      - config object
 * @return {Promise} with an array of errors, if any
 */
function spawnJsDoc (basePath, obj) {
  return new Promise((resolve, reject) => {
    var jsdocErrors = [];
    var spawnErr = false;
    var cwd = process.cwd();
    var jsDocConfTmp = path.resolve(cwd, 'jsdoc.' + Date.now() + '.conf.tmp');
    var command = /^win/.test(process.platform) ? 'jsdoc.cmd' : 'jsdoc';
    var jsdoc;

    fs.writeFileSync(jsDocConfTmp, JSON.stringify(obj));

    if (typeof basePath === 'string') {
      jsdoc = spawn(path.resolve(basePath, command), ['-c', jsDocConfTmp])
    } else {
      jsdoc = spawn('jsdoc', ['-c', jsDocConfTmp])
    }

    jsdoc.on('error', (err) => {
      spawnErr = err;
    });
    jsdoc.stderr.on('data', function (data) {
      jsdocErrors.push(data.toString());
    });
    jsdoc.on('close', function (data, code) {
      jsdocErrors = jsdocErrors.join('\n').split('\n').filter((item) => { return item !== ''; });
      jsdocErrors.forEach((message, index, list) => {
        list[index] = new Error(message);
      });
      fs.unlink(jsDocConfTmp, function (err) {
        if (err) return reject(err);
        if (spawnErr) return reject(spawnErr);
        resolve(jsdocErrors);
      });
    });
  });
}

Plugin.prototype.apply = function (compiler) {
  var self = this;
  var options = self.options;

  compiler.plugin('watch-run', function (watching, callback) {
    self.webpackIsWatching = true;
    callback(null, null);
  });

  compiler.plugin('emit', function (compilation, callback) {
    console.log('JSDOC Start generating');

    fsExtra.readJson(path.resolve(process.cwd(), options.conf), function (err, obj) {
      var files = [];
      if(err) {
        callback(err);
        return;
      }

      if (obj.source && obj.source.include) {
        console.log('Taking sources from config file');
      } else {
        compilation.chunks.forEach(function (chunk) {
          for(const module of chunk.modulesIterable) {
            if (module.fileDependencies) {
              module.fileDependencies.forEach(function (filepath) {
                files.push(path.relative(process.cwd(), filepath));
              });
            }
          }
        });
        merge(obj.source, { include: files });
      }

      /**
       * First try to spawn the jsdoc command from `node_modules/jsdoc-webpack-plugin/node_modules/.bin` path
       */
      spawnJsDoc(`${__dirname}/node_modules/.bin/`, obj).then((errs) => {
        if (errs && errs.length > 0) compilation.errors = compilation.errors.concat(errs);
        callback();
      }).catch((err) => {
        if (err.code === "ENOENT") {
            /**
             * Finally try to let node find it
             */
            return spawnJsDoc(null, obj);
        } else {
          return Promise.reject(err);
        }
      }).then((errs) => {
        if (errs && errs.length > 0) compilation.errors = compilation.errors.concat(errs);
        callback();
      }).catch((err) => {
        if (err.code === "ENOENT") {
          compilation.errors.push(new Error('JSDOC not found.'))
        } else {
          compilation.errors.push(err);
        }
        callback();
      });

    });
  });

};

module.exports = Plugin;
<|endoftext|>


<commit_before>//filter will reemit the data if cb(err,pass) pass is truthy

// reduce is more tricky
// maybe we want to group the reductions or emit progress updates occasionally
// the most basic reduce just emits one 'data' event after it has recieved 'end'

var Stream = require('stream').Stream
  , es = exports
  , through = require('through')
  , from = require('from')
  , duplex = require('duplexer')
  , map = require('map-stream')
  , pause = require('pause-stream')
  , split = require('split')
  , pipeline = require('stream-combiner')
  , immediately = global.setImmediate || process.nextTick;

es.Stream = Stream //re-export Stream from core
es.through = through
es.from = from
es.duplex = duplex
es.map = map
es.pause = pause
es.split = split
es.pipeline = es.connect = es.pipe = pipeline
// merge / concat
//
// combine multiple streams into a single stream.
// will emit end only once

es.concat = //actually this should be called concat
es.merge = function (/*streams...*/) {
  var toMerge = [].slice.call(arguments)
  if (toMerge.length === 1 && (toMerge[0] instanceof Array)) {
    toMerge = toMerge[0] //handle array as arguments object
  }
  var stream = new Stream()
  stream.setMaxListeners(0) // allow adding more than 11 streams
  var endCount = 0
  stream.writable = stream.readable = true

  if (toMerge.length) {
    toMerge.forEach(function (e) {
      e.pipe(stream, {end: false})
      var ended = false
      e.on('end', function () {
        if(ended) return
        ended = true
        endCount ++
        if(endCount == toMerge.length)
          stream.emit('end')
      })
    })
  } else {
    process.nextTick(() => stream.emit('end'))
  }

  stream.write = function (data) {
    this.emit('data', data)
  }
  stream.destroy = function () {
    toMerge.forEach(function (e) {
      if(e.destroy) e.destroy()
    })
  }
  return stream
}

// writable stream, collects all events into an array
// and calls back when 'end' occurs
// mainly I'm using this to test the other functions

es.writeArray = function (done) {
  if ('function' !== typeof done)
    throw new Error('function writeArray (done): done must be function')

  var a = new Stream ()
    , array = [], isDone = false
  a.write = function (l) {
    array.push(l)
  }
  a.end = function () {
    isDone = true
    done(null, array)
  }
  a.writable = true
  a.readable = false
  a.destroy = function () {
    a.writable = a.readable = false
    if(isDone) return
    done(new Error('destroyed before end'), array)
  }
  return a
}

//return a Stream that reads the properties of an object
//respecting pause() and resume()

es.readArray = function (array) {
  var stream = new Stream()
    , i = 0
    , paused = false
    , ended = false

  stream.readable = true
  stream.writable = false

  if(!Array.isArray(array))
    throw new Error('event-stream.read expects an array')

  stream.resume = function () {
    if(ended) return
    paused = false
    var l = array.length
    while(i < l && !paused && !ended) {
      stream.emit('data', array[i++])
    }
    if(i == l && !ended)
      ended = true, stream.readable = false, stream.emit('end')
  }
  process.nextTick(stream.resume)
  stream.pause = function () {
     paused = true
  }
  stream.destroy = function () {
    ended = true
    stream.emit('close')
  }
  return stream
}

//
// readable (asyncFunction)
// return a stream that calls an async function while the stream is not paused.
//
// the function must take: (count, callback) {...
//

es.readable =
function (func, continueOnError) {
  var stream = new Stream()
    , i = 0
    , paused = false
    , ended = false
    , reading = false

  stream.readable = true
  stream.writable = false

  if('function' !== typeof func)
    throw new Error('event-stream.readable expects async function')

  stream.on('end', function () { ended = true })

  function get (err, data) {

    if(err) {
      stream.emit('error', err)
      if(!continueOnError) stream.emit('end')
    } else if (arguments.length > 1)
      stream.emit('data', data)

    immediately(function () {
      if(ended || paused || reading) return
      try {
        reading = true
        func.call(stream, i++, function () {
          reading = false
          get.apply(null, arguments)
        })
      } catch (err) {
        stream.emit('error', err)
      }
    })
  }
  stream.resume = function () {
    paused = false
    get()
  }
  process.nextTick(get)
  stream.pause = function () {
     paused = true
  }
  stream.destroy = function () {
    stream.emit('end')
    stream.emit('close')
    ended = true
  }
  return stream
}

//
// map sync
//

es.mapSync = function (sync) {
  return es.through(function write(data) {
    var mappedData
    try {
      mappedData = sync(data)
    } catch (err) {
      return this.emit('error', err)
    }
    if (mappedData !== undefined)
      this.emit('data', mappedData)
  })
}

//
// log just print out what is coming through the stream, for debugging
//

es.log = function (name) {
  return es.through(function (data) {
    var args = [].slice.call(arguments)
    if(name) console.error(name, data)
    else     console.error(data)
    this.emit('data', data)
  })
}

//
// child -- pipe through a child process
//

es.child = function (child) {

  return es.duplex(child.stdin, child.stdout)

}

//
// parse
//
// must be used after es.split() to ensure that each chunk represents a line
// source.pipe(es.split()).pipe(es.parse())

es.parse = function (options) {
  var emitError = !!(options ? options.error : false)
  return es.through(function (data) {
    var obj
    try {
      if(data) //ignore empty lines
        obj = JSON.parse(data.toString())
    } catch (err) {
      if (emitError)
        return this.emit('error', err)
      return console.error(err, 'attempting to parse:', data)
    }
    //ignore lines that where only whitespace.
    if(obj !== undefined)
      this.emit('data', obj)
  })
}
//
// stringify
//

es.stringify = function () {
  var Buffer = require('buffer').Buffer
  return es.mapSync(function (e){
    return JSON.stringify(Buffer.isBuffer(e) ? e.toString() : e) + '\n'
  })
}

//
// replace a string within a stream.
//
// warn: just concatenates the string and then does str.split().join().
// probably not optimal.
// for smallish responses, who cares?
// I need this for shadow-npm so it's only relatively small json files.

es.replace = function (from, to) {
  return es.pipeline(es.split(from), es.join(to))
}

//
// join chunks with a joiner. just like Array#join
// also accepts a callback that is passed the chunks appended together
// this is still supported for legacy reasons.
//

es.join = function (str) {

  //legacy api
  if('function' === typeof str)
    return es.wait(str)

  var first = true
  return es.through(function (data) {
    if(!first)
      this.emit('data', str)
    first = false
    this.emit('data', data)
    return true
  })
}

//
// wait. callback when 'end' is emitted, with all chunks appended as string.
//

es.wait = function (callback) {
  var arr = []
  return es.through(function (data) { arr.push(data) },
    function () {
      var body = Buffer.isBuffer(arr[0]) ? Buffer.concat(arr)
        : arr.join('')
      this.emit('data', body)
      this.emit('end')
      if(callback) callback(null, body)
    })
}

es.pipeable = function () {
  throw new Error('[EVENT-STREAM] es.pipeable is deprecated')
}
<commit_msg>un-es2015<commit_after>//filter will reemit the data if cb(err,pass) pass is truthy

// reduce is more tricky
// maybe we want to group the reductions or emit progress updates occasionally
// the most basic reduce just emits one 'data' event after it has recieved 'end'

var Stream = require('stream').Stream
  , es = exports
  , through = require('through')
  , from = require('from')
  , duplex = require('duplexer')
  , map = require('map-stream')
  , pause = require('pause-stream')
  , split = require('split')
  , pipeline = require('stream-combiner')
  , immediately = global.setImmediate || process.nextTick;

es.Stream = Stream //re-export Stream from core
es.through = through
es.from = from
es.duplex = duplex
es.map = map
es.pause = pause
es.split = split
es.pipeline = es.connect = es.pipe = pipeline
// merge / concat
//
// combine multiple streams into a single stream.
// will emit end only once

es.concat = //actually this should be called concat
es.merge = function (/*streams...*/) {
  var toMerge = [].slice.call(arguments)
  if (toMerge.length === 1 && (toMerge[0] instanceof Array)) {
    toMerge = toMerge[0] //handle array as arguments object
  }
  var stream = new Stream()
  stream.setMaxListeners(0) // allow adding more than 11 streams
  var endCount = 0
  stream.writable = stream.readable = true

  if (toMerge.length) {
    toMerge.forEach(function (e) {
      e.pipe(stream, {end: false})
      var ended = false
      e.on('end', function () {
        if(ended) return
        ended = true
        endCount ++
        if(endCount == toMerge.length)
          stream.emit('end')
      })
    })
  } else {
    process.nextTick(function () {
      stream.emit('end')
    })
  }

  stream.write = function (data) {
    this.emit('data', data)
  }
  stream.destroy = function () {
    toMerge.forEach(function (e) {
      if(e.destroy) e.destroy()
    })
  }
  return stream
}

// writable stream, collects all events into an array
// and calls back when 'end' occurs
// mainly I'm using this to test the other functions

es.writeArray = function (done) {
  if ('function' !== typeof done)
    throw new Error('function writeArray (done): done must be function')

  var a = new Stream ()
    , array = [], isDone = false
  a.write = function (l) {
    array.push(l)
  }
  a.end = function () {
    isDone = true
    done(null, array)
  }
  a.writable = true
  a.readable = false
  a.destroy = function () {
    a.writable = a.readable = false
    if(isDone) return
    done(new Error('destroyed before end'), array)
  }
  return a
}

//return a Stream that reads the properties of an object
//respecting pause() and resume()

es.readArray = function (array) {
  var stream = new Stream()
    , i = 0
    , paused = false
    , ended = false

  stream.readable = true
  stream.writable = false

  if(!Array.isArray(array))
    throw new Error('event-stream.read expects an array')

  stream.resume = function () {
    if(ended) return
    paused = false
    var l = array.length
    while(i < l && !paused && !ended) {
      stream.emit('data', array[i++])
    }
    if(i == l && !ended)
      ended = true, stream.readable = false, stream.emit('end')
  }
  process.nextTick(stream.resume)
  stream.pause = function () {
     paused = true
  }
  stream.destroy = function () {
    ended = true
    stream.emit('close')
  }
  return stream
}

//
// readable (asyncFunction)
// return a stream that calls an async function while the stream is not paused.
//
// the function must take: (count, callback) {...
//

es.readable =
function (func, continueOnError) {
  var stream = new Stream()
    , i = 0
    , paused = false
    , ended = false
    , reading = false

  stream.readable = true
  stream.writable = false

  if('function' !== typeof func)
    throw new Error('event-stream.readable expects async function')

  stream.on('end', function () { ended = true })

  function get (err, data) {

    if(err) {
      stream.emit('error', err)
      if(!continueOnError) stream.emit('end')
    } else if (arguments.length > 1)
      stream.emit('data', data)

    immediately(function () {
      if(ended || paused || reading) return
      try {
        reading = true
        func.call(stream, i++, function () {
          reading = false
          get.apply(null, arguments)
        })
      } catch (err) {
        stream.emit('error', err)
      }
    })
  }
  stream.resume = function () {
    paused = false
    get()
  }
  process.nextTick(get)
  stream.pause = function () {
     paused = true
  }
  stream.destroy = function () {
    stream.emit('end')
    stream.emit('close')
    ended = true
  }
  return stream
}

//
// map sync
//

es.mapSync = function (sync) {
  return es.through(function write(data) {
    var mappedData
    try {
      mappedData = sync(data)
    } catch (err) {
      return this.emit('error', err)
    }
    if (mappedData !== undefined)
      this.emit('data', mappedData)
  })
}

//
// log just print out what is coming through the stream, for debugging
//

es.log = function (name) {
  return es.through(function (data) {
    var args = [].slice.call(arguments)
    if(name) console.error(name, data)
    else     console.error(data)
    this.emit('data', data)
  })
}

//
// child -- pipe through a child process
//

es.child = function (child) {

  return es.duplex(child.stdin, child.stdout)

}

//
// parse
//
// must be used after es.split() to ensure that each chunk represents a line
// source.pipe(es.split()).pipe(es.parse())

es.parse = function (options) {
  var emitError = !!(options ? options.error : false)
  return es.through(function (data) {
    var obj
    try {
      if(data) //ignore empty lines
        obj = JSON.parse(data.toString())
    } catch (err) {
      if (emitError)
        return this.emit('error', err)
      return console.error(err, 'attempting to parse:', data)
    }
    //ignore lines that where only whitespace.
    if(obj !== undefined)
      this.emit('data', obj)
  })
}
//
// stringify
//

es.stringify = function () {
  var Buffer = require('buffer').Buffer
  return es.mapSync(function (e){
    return JSON.stringify(Buffer.isBuffer(e) ? e.toString() : e) + '\n'
  })
}

//
// replace a string within a stream.
//
// warn: just concatenates the string and then does str.split().join().
// probably not optimal.
// for smallish responses, who cares?
// I need this for shadow-npm so it's only relatively small json files.

es.replace = function (from, to) {
  return es.pipeline(es.split(from), es.join(to))
}

//
// join chunks with a joiner. just like Array#join
// also accepts a callback that is passed the chunks appended together
// this is still supported for legacy reasons.
//

es.join = function (str) {

  //legacy api
  if('function' === typeof str)
    return es.wait(str)

  var first = true
  return es.through(function (data) {
    if(!first)
      this.emit('data', str)
    first = false
    this.emit('data', data)
    return true
  })
}

//
// wait. callback when 'end' is emitted, with all chunks appended as string.
//

es.wait = function (callback) {
  var arr = []
  return es.through(function (data) { arr.push(data) },
    function () {
      var body = Buffer.isBuffer(arr[0]) ? Buffer.concat(arr)
        : arr.join('')
      this.emit('data', body)
      this.emit('end')
      if(callback) callback(null, body)
    })
}

es.pipeable = function () {
  throw new Error('[EVENT-STREAM] es.pipeable is deprecated')
}
<|endoftext|>


<commit_before>/*!
 * promised-handlebars <https://github.com/nknapp/promised-handlebars>
 *
 * Copyright (c) 2015 Nils Knappmeier.
 * Released under the MIT license.
 */

'use strict'

var Q = require('q')

/**
 * Returns a new Handlebars instance that
 * * Allows helpers to return promises
 * * Creates `compiled` templates that always
 *   return a promise for the resulting string.
 *   The promise is fulfilled after all helper-promsises
 *   are resolved.
 *
 * @param {Handlebars} Handlebars
 * @param {object} options
 * @param {string=} options.placeholder
 * @returns {Handlebars} a modified Handlebars object
 */
module.exports = function promisedHandlebars (Handlebars, options) {
  options = options || {}
  options.placeholder = options.placeholder || '\u0001'

  // one line from from substack's quotemeta-package
  var regex = new RegExp(String(options.placeholder).replace(/(\W)/g, '\\$1'), 'g')

  var promises = null
  var engine = Handlebars.create()
  var oldRegisterHelper = engine.registerHelper
  engine.registerHelper = function (keyOrObject, fn) {
    if (typeof keyOrObject === 'string') {
      oldRegisterHelper.call(this, keyOrObject, function () {
        var result = Q(fn.apply(this, arguments))
        promises.push(result)
        return options.placeholder
      })
    } else {
      // `keyOrObject` is actual an object of helpers
      Object.keys(keyOrObject).forEach(function (key) {
        engine.registerHelper(key, keyOrObject[key])
      })
    }
  }

  var oldCompile = engine.compile
  engine.compile = function () {
    var fn = oldCompile.apply(this, arguments)
    // Wrap the compiled function
    return function () {
      if (promises) {
        // "promises" array already exists: We are executing a partial
        // Do not return a promise or things will fail.
        return fn.apply(engine, arguments)
      }
      try {
        promises = []
        var resultWithPlaceholders = fn.apply(engine, arguments)
        // Save a local copy for concurrency reasons
        var _promises = promises
        return Q.all(_promises).then(function (results) {
          return resultWithPlaceholders.replace(regex, function () {
            return results.shift()
          })
        })
      } finally {
        promises = null
      }

    }

  }
  return engine
}
<commit_msg>More comments<commit_after>/*!
 * promised-handlebars <https://github.com/nknapp/promised-handlebars>
 *
 * Copyright (c) 2015 Nils Knappmeier.
 * Released under the MIT license.
 */

'use strict'

var Q = require('q')

/**
 * Returns a new Handlebars instance that
 * * Allows helpers to return promises
 * * Creates `compiled` templates that always
 *   return a promise for the resulting string.
 *   The promise is fulfilled after all helper-promsises
 *   are resolved.
 *
 * @param {Handlebars} Handlebars
 * @param {object} options
 * @param {string=} options.placeholder
 * @returns {Handlebars} a modified Handlebars object
 */
module.exports = function promisedHandlebars (Handlebars, options) {
  options = options || {}
  options.placeholder = options.placeholder || '\u0001'

  // one line from from substack's quotemeta-package
  var regex = new RegExp(String(options.placeholder).replace(/(\W)/g, '\\$1'), 'g')

  var promises = null
  var engine = Handlebars.create()

  // Wrap `registerHelper` with a custom function
  var oldRegisterHelper = engine.registerHelper
  engine.registerHelper = function (keyOrObject, fn) {

    if (typeof keyOrObject === 'string') {
      // Register a custom helper-function instead of actual helper
      oldRegisterHelper.call(this, keyOrObject, function () {
        var result = Q(fn.apply(this, arguments))
        // Remember promise and return placeholder instead
        promises.push(result)
        return options.placeholder
      })
    } else {
      // `keyOrObject` is actual an object of helpers
      // Call `registerHelper` again for each object
      // This simulates the default Handlebars-behaviour
      Object.keys(keyOrObject).forEach(function (key) {
        engine.registerHelper(key, keyOrObject[key])
      })
    }
  }

  var oldCompile = engine.compile
  engine.compile = function () {
    var fn = oldCompile.apply(this, arguments)
    // Wrap the compiled function
    return function () {
      if (promises) {
        // "promises" array already exists: We are executing a partial
        // That means we are called from somewhere within Handlebars.
        // Handlebars does not like promises, so we act as normal as possible.
        return fn.apply(engine, arguments)
      }
      try {
        // We are called from outside Handlebars (initial call)
        // Initialize the `promise` variable (see above) with an empty
        // array, so that helpers can store their promises during the
        // template execution.
        promises = []
        // Execute template (helpers are getting called and store promises
        // into the array
        var resultWithPlaceholders = fn.apply(engine, arguments)
        return Q.all(promises).then(function (results) {
          // Promises are fulfilled. Insert real values into the result.
          return resultWithPlaceholders.replace(regex, function () {
            return results.shift()
          })
        })
      } finally {
        // Reset promises for the next execution run
        promises = null
      }

    }

  }
  return engine
}
<|endoftext|>


<commit_before>describe('Inverted Index Tests: ', function() {

  var index = new Index();
  var filter = new Filter();
  var filePath = './books.json';
  var results, path;

  beforeEach(function(done) {
    index.createIndex(filePath, index.populateIndex).done(function(data) {
      results = data;
      path = index.extractFileName(filePath);

      // Invoke jasmine's done callback
      done();
    });
  });

  describe('Reads book data', function() {

    it('reads the JSON file successfully', function() {
      expect(results).toBeDefined();
      expect(results.length).not.toEqual(0);
      expect(results.length).toEqual(2);
    });

    it('Ensures each object property contains a string value', function() {
      for (var i = 0, len = results.length; i < len; i++) {
        // Iterate over the properties of each object
        for(var key in results[i]) {
          // Ensure the value is a string
          expect(typeof results[i][key]).toBe('string');
        }
      }
    });

    it("tracks that the spy was called", function(done) {
      var testObj = new Index();
      // Spy on the `populateIndex method`
      spyOn(testObj, 'populateIndex').and.callThrough();
      testObj.createIndex(filePath, testObj.populateIndex)
      .done(function(data) {
        expect(testObj.populateIndex).toHaveBeenCalled();
        expect(testObj.populateIndex).toHaveBeenCalledWith(testObj, path, data);
        // Indicate to jasmine that the async function is complete
        done();
      });
    });

  });

  describe('Populates Index', function() {

    var index0 = ['alice', 'rabbit', 'imagination', 'world'];
    var index1 = ['alliance', 'elf', 'dwarf', 'wizard'];

    it('creates the index once JSON file has been read', function() {
      expect(index. getIndex(path)).not.toEqual({});
    });

    it('creates an index containing only lowercase terms', function() {
      var keys = Object.keys(index. getIndex(path));
      for (var i = 0, len = keys.length; i < len; i++) {
        expect(keys[i]).toBe(keys[i].toLowerCase());
      }
    });

    it('creates index strings without punctuation', function() {
      var keys = Object.keys(index. getIndex(path));
      for (var i = 0, len = keys.length; i < len; i++) {
        expect(keys[i].indexOf('.')).toBe(-1);
        expect(keys[i].indexOf(',')).toBe(-1);
      }
    });

    it('creates index strings without stop words', function() {
      var keys = Object.keys(index. getIndex(path));
      for (var i = 0, len = keys.length; i < len; i++) {
        expect(filter.stopWords.indexOf(keys[i])).toBe(-1);
      }
    });

    it('creates index containing correct terms', function() {
      var keys = Object.keys(index. getIndex(path));

      for (var i = 0, len = index0.length; i < len; i++) {
        expect(keys).toContain(index0[i]);
      }

      for (var i = 0, len = index1.length; i < len; i++) {
        expect(keys).toContain(index0[i]);
      }
    });

    it('maps string keys to the correct objects', function() {
      var keys = Object.keys(index. getIndex(path));

      // Should return 0 for objects in the first array element
      for (var i = 0, len = index0.length; i < len; i++) {
        expect(index.results[path][index0[i]]).toBe(0);
      }

      // Should return 1 for objects in the second array element
      for (var i = 0, len = index0.length; i < len; i++) {
        expect(index.results[path][index1[i]]).toBe(1);
      }
    });
  });

  describe('Search Index', function() {

    it('returns accurate results when searched', function() {
      expect(Array.isArray(index.searchIndex('elf'))).toBe(true);
      expect(index.searchIndex('rabbit')).toContain(0);
      expect(index.searchIndex('hobbit')).toContain(1);

      // The returned array should have a maximum of 2 elements
      expect(index.searchIndex(['imagination', 'dwarf', 'hobbit']).length)
            .toBe(2);
    });

    it('returns -1 when none of the terms is found', function() {
      expect(index.searchIndex('haha')).toBe(-1);
    });

    it('handles an array of search terms', function() {
      expect(index.searchIndex(['haha', 'impossible'])).toBe(-1);
      expect(index.searchIndex(['imagination', 'dwarf'])).toContain(0);
      expect(index.searchIndex(['imagination', 'dwarf'])).toContain(1);
    });

    it('handles varied number of of search terms as arguments', function() {
      expect(index.searchIndex()).toBe(-1);
      expect(index.searchIndex([])).toBe(-1);
      expect(index.searchIndex(['imagination'])).toContain(0);
      expect(index.searchIndex('imagination', 'dwarf')).toContain(1);
      expect(index.searchIndex('imagination', 'dwarf', 'warrior')).toContain(1);
      expect(index.searchIndex('imagination', 'dwarf', 'warrior').length).toBe(2);
    });

  });
});
<commit_msg>Add spec to check search does not take too long<commit_after>describe('Inverted Index Tests: ', function() {

  var index = new Index();
  var filter = new Filter();
  var filePath = './books.json';
  var results, path;

  beforeEach(function(done) {
    index.createIndex(filePath, index.populateIndex).done(function(data) {
      results = data;
      path = index.extractFileName(filePath);

      // Invoke jasmine's done callback
      done();
    });
  });

  describe('Reads book data', function() {

    it('reads the JSON file successfully', function() {
      expect(results).toBeDefined();
      expect(results.length).not.toEqual(0);
      expect(results.length).toEqual(2);
    });

    it('Ensures each object property contains a string value', function() {
      for (var i = 0, len = results.length; i < len; i++) {
        // Iterate over the properties of each object
        for(var key in results[i]) {
          // Ensure the value is a string
          expect(typeof results[i][key]).toBe('string');
        }
      }
    });

    it("tracks that the spy was called", function(done) {
      var testObj = new Index();
      // Spy on the `populateIndex method`
      spyOn(testObj, 'populateIndex').and.callThrough();
      testObj.createIndex(filePath, testObj.populateIndex)
      .done(function(data) {
        expect(testObj.populateIndex).toHaveBeenCalled();
        expect(testObj.populateIndex).toHaveBeenCalledWith(testObj, path, data);
        // Indicate to jasmine that the async function is complete
        done();
      });
    });

  });

  describe('Populates Index', function() {

    var index0 = ['alice', 'rabbit', 'imagination', 'world'];
    var index1 = ['alliance', 'elf', 'dwarf', 'wizard'];

    it('creates the index once JSON file has been read', function() {
      expect(index. getIndex(path)).not.toEqual({});
    });

    it('creates an index containing only lowercase terms', function() {
      var keys = Object.keys(index. getIndex(path));
      for (var i = 0, len = keys.length; i < len; i++) {
        expect(keys[i]).toBe(keys[i].toLowerCase());
      }
    });

    it('creates index strings without punctuation', function() {
      var keys = Object.keys(index. getIndex(path));
      for (var i = 0, len = keys.length; i < len; i++) {
        expect(keys[i].indexOf('.')).toBe(-1);
        expect(keys[i].indexOf(',')).toBe(-1);
      }
    });

    it('creates index strings without stop words', function() {
      var keys = Object.keys(index. getIndex(path));
      for (var i = 0, len = keys.length; i < len; i++) {
        expect(filter.stopWords.indexOf(keys[i])).toBe(-1);
      }
    });

    it('creates index containing correct terms', function() {
      var keys = Object.keys(index. getIndex(path));

      for (var i = 0, len = index0.length; i < len; i++) {
        expect(keys).toContain(index0[i]);
      }

      for (var i = 0, len = index1.length; i < len; i++) {
        expect(keys).toContain(index0[i]);
      }
    });

    it('maps string keys to the correct objects', function() {
      var keys = Object.keys(index. getIndex(path));

      // Should return 0 for objects in the first array element
      for (var i = 0, len = index0.length; i < len; i++) {
        expect(index.results[path][index0[i]]).toBe(0);
      }

      // Should return 1 for objects in the second array element
      for (var i = 0, len = index0.length; i < len; i++) {
        expect(index.results[path][index1[i]]).toBe(1);
      }
    });
  });

  describe('Search Index', function() {

    it('returns accurate results when searched', function() {
      expect(Array.isArray(index.searchIndex('elf'))).toBe(true);
      expect(index.searchIndex('rabbit')).toContain(0);
      expect(index.searchIndex('hobbit')).toContain(1);

      // The returned array should have a maximum of 2 elements
      expect(index.searchIndex(['imagination', 'dwarf', 'hobbit']).length)
            .toBe(2);
    });

    it('returns -1 when none of the terms is found', function() {
      expect(index.searchIndex('haha')).toBe(-1);
    });

    it('handles an array of search terms', function() {
      expect(index.searchIndex(['haha', 'impossible'])).toBe(-1);
      expect(index.searchIndex(['imagination', 'dwarf'])).toContain(0);
      expect(index.searchIndex(['imagination', 'dwarf'])).toContain(1);
    });

    it('handles varied number of of search terms as arguments', function() {
      expect(index.searchIndex()).toBe(-1);
      expect(index.searchIndex([])).toBe(-1);
      expect(index.searchIndex(['imagination'])).toContain(0);
      expect(index.searchIndex('imagination', 'dwarf')).toContain(1);
      expect(index.searchIndex('imagination', 'dwarf', 'warrior')).toContain(1);
      expect(index.searchIndex('imagination', 'dwarf', 'warrior').length).toBe(2);
    });

    it('does not take too long to execute', function() {
      var terms = ['alice', 'rings', 'lord', 'wonderland',
                 'enters', 'imagination', 'hole', 'rabbit',
                 'world', 'elf', 'dwarf', 'hobbit', 'wizard',
                 'destroy', 'ring', 'seek', 'alliance', 'man',
                 'non-existent', 'words', 'too'];
      // Start tracking time juts before the function is called
      var a = performance.now();
      // save the results in a variable
      var results = index.searchIndex(terms);
      // Check the time after the function is done
      var b = performance.now();

      // check that the correct results are returned
      expect(results.length).toBe(2);
      // chekc that the time is below 1 ms
      expect(b - a).toBeLessThan(1);
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

const transpileModule = require('./transpile');
const ts = require('typescript');
const anymatch = require('anymatch');
const path = require('path');

const resolveEnum = (choice, opts) => {
  const defaultValue = 1; // CommonJS/ES5/Preserve JSX defaults

  if (!choice) return defaultValue;
  if (!isNaN(choice)) return choice - 0;

  for (const opt of Object.keys(opts)) {
    if (choice && choice.toUpperCase() === opt.toUpperCase()) {
      return opts[opt];
    }
  }

  return defaultValue;
};

const getTsconfig = configRoot => {
  if (!configRoot) return {};

  const file = path.resolve(configRoot, 'tsconfig.json');

  try {
    return require(file).compilerOptions;
  } catch (e) {
    return {};
  }
};

const findLessOrEqual = (haystack, needle) => {
  let i = 0;
  while (i + 1 < haystack.length && needle >= haystack[i + 1]) {
    i += 1;
  }
  return i === haystack.length ? -1 : i;
};

const errPos = err => {
  if (err.file) {
    const lineMap = err.file.lineMap;
    if (lineMap) {
      const lineIndex = findLessOrEqual(lineMap, err.start);

      return `Line: ${lineIndex + 1}, Col: ${err.start - err.file.lineMap[lineIndex] + 1}`;
    }
  }
  return 'No line map';
};

const toMeaningfulMessage = err => `Error ${err.code}: ${err.messageText} (${errPos(err)})`;

class TypeScriptCompiler {
  constructor(config) {
    if (!config) {
      config = {};
    }

    const options = config.plugins && config.plugins.brunchTypescript || {};
    this.options = getTsconfig(config.paths && config.paths.root);

    Object.keys(options).forEach(key => {
      if (key === 'sourceMap' || key === 'ignore') return;
      this.options[key] = options[key];
    });

    this.options.module = resolveEnum(this.options.module, ts.ModuleKind);
    this.options.target = resolveEnum(this.options.target, ts.ScriptTarget);
    this.options.jsx = resolveEnum(this.options.jsx, ts.JsxEmit);
    this.options.emitDecoratorMetadata = this.options.emitDecoratorMetadata !== false;
    this.options.experimentalDecorators = this.options.experimentalDecorators !== false;
    this.options.noEmitOnError = false; // This can"t be true when compiling this way.

    delete this.options.moduleResolution;

    this.options.sourceMap = !!config.sourceMaps;
    this.isIgnored = anymatch(options.ignore || config.conventions.vendor);
    if (this.options.pattern) {
      this.pattern = this.options.pattern;
      delete this.options.pattern;
    }

    if (this.options.ignoreErrors) {
      if (this.options.ignoreErrors === true) {
        this.ignoreAllErrors = true;
      } else {
        this.ignoreErrors = new Set(this.options.ignoreErrors);
      }
      delete this.options.ignoreErrors;
    }
  }

  compile(params) {
    if (this.isIgnored(params.path)) return Promise.resolve(params);

    const tsOptions = {
      fileName: params.path,
      reportDiagnostics: true,
      compilerOptions: this.options,
    };

    return new Promise((resolve, reject) => {
      let compiled;

      try {
        compiled = transpileModule(params.data, tsOptions);
        let reportable = compiled.diagnostics;

        if (this.ignoreAllErrors === true) {
          reportable = [];
        } else if (this.ignoreErrors) {
          reportable = reportable.filter(err => !this.ignoreErrors.has(err.code));
        }

        if (reportable.length) {
          reject(reportable.map(toMeaningfulMessage).join('\n'));
        }
      } catch (err) {
        return reject(err);
      }

      const result = {data: `${compiled.outputText || compiled}\n`};

      if (compiled.sourceMapText) {
        // Fix the sources path so Brunch can merge them.
        const rawMap = JSON.parse(compiled.sourceMapText);
        rawMap.sources[0] = params.path;
        result.map = JSON.stringify(rawMap);
      }

      resolve(result);
    });
  }
}

TypeScriptCompiler.prototype.brunchPlugin = true;
TypeScriptCompiler.prototype.type = 'javascript';
TypeScriptCompiler.prototype.pattern = /\.tsx?$/;

module.exports = TypeScriptCompiler;
<commit_msg>Emit js/jsx files using new targetExtension API (#39)<commit_after>'use strict';

const transpileModule = require('./transpile');
const ts = require('typescript');
const anymatch = require('anymatch');
const path = require('path');

const resolveEnum = (choice, opts) => {
  const defaultValue = 1; // CommonJS/ES5/Preserve JSX defaults

  if (!choice) return defaultValue;
  if (!isNaN(choice)) return choice - 0;

  for (const opt of Object.keys(opts)) {
    if (choice && choice.toUpperCase() === opt.toUpperCase()) {
      return opts[opt];
    }
  }

  return defaultValue;
};

const getTsconfig = configRoot => {
  if (!configRoot) return {};

  const file = path.resolve(configRoot, 'tsconfig.json');

  try {
    return require(file).compilerOptions;
  } catch (e) {
    return {};
  }
};

const findLessOrEqual = (haystack, needle) => {
  let i = 0;
  while (i + 1 < haystack.length && needle >= haystack[i + 1]) {
    i += 1;
  }
  return i === haystack.length ? -1 : i;
};

const errPos = err => {
  if (err.file) {
    const lineMap = err.file.lineMap;
    if (lineMap) {
      const lineIndex = findLessOrEqual(lineMap, err.start);

      return `Line: ${lineIndex + 1}, Col: ${err.start - err.file.lineMap[lineIndex] + 1}`;
    }
  }
  return 'No line map';
};

const toMeaningfulMessage = err => `Error ${err.code}: ${err.messageText} (${errPos(err)})`;

class TypeScriptCompiler {
  constructor(config) {
    if (!config) {
      config = {};
    }

    const options = config.plugins && config.plugins.brunchTypescript || {};
    this.options = getTsconfig(config.paths && config.paths.root);

    Object.keys(options).forEach(key => {
      if (key === 'sourceMap' || key === 'ignore') return;
      this.options[key] = options[key];
    });

    if (this.options.jsx === 'preserve') {
      this.targetExtension = '.jsx';
    } else {
      this.targetExtension = '.js';
    }

    this.options.module = resolveEnum(this.options.module, ts.ModuleKind);
    this.options.target = resolveEnum(this.options.target, ts.ScriptTarget);
    this.options.jsx = resolveEnum(this.options.jsx, ts.JsxEmit);
    this.options.emitDecoratorMetadata = this.options.emitDecoratorMetadata !== false;
    this.options.experimentalDecorators = this.options.experimentalDecorators !== false;
    this.options.noEmitOnError = false; // This can"t be true when compiling this way.

    delete this.options.moduleResolution;

    this.options.sourceMap = !!config.sourceMaps;
    this.isIgnored = anymatch(options.ignore || config.conventions.vendor);
    if (this.options.pattern) {
      this.pattern = this.options.pattern;
      delete this.options.pattern;
    }

    if (this.options.ignoreErrors) {
      if (this.options.ignoreErrors === true) {
        this.ignoreAllErrors = true;
      } else {
        this.ignoreErrors = new Set(this.options.ignoreErrors);
      }
      delete this.options.ignoreErrors;
    }
  }

  compile(params) {
    if (this.isIgnored(params.path)) return Promise.resolve(params);

    const tsOptions = {
      fileName: params.path,
      reportDiagnostics: true,
      compilerOptions: this.options,
    };

    return new Promise((resolve, reject) => {
      let compiled;

      try {
        compiled = transpileModule(params.data, tsOptions);
        let reportable = compiled.diagnostics;

        if (this.ignoreAllErrors === true) {
          reportable = [];
        } else if (this.ignoreErrors) {
          reportable = reportable.filter(err => !this.ignoreErrors.has(err.code));
        }

        if (reportable.length) {
          reject(reportable.map(toMeaningfulMessage).join('\n'));
        }
      } catch (err) {
        return reject(err);
      }

      const result = {data: `${compiled.outputText || compiled}\n`};

      if (compiled.sourceMapText) {
        // Fix the sources path so Brunch can merge them.
        const rawMap = JSON.parse(compiled.sourceMapText);
        rawMap.sources[0] = params.path;
        result.map = JSON.stringify(rawMap);
      }

      resolve(result);
    });
  }
}

TypeScriptCompiler.prototype.brunchPlugin = true;
TypeScriptCompiler.prototype.type = 'javascript';
TypeScriptCompiler.prototype.pattern = /\.tsx?$/;

module.exports = TypeScriptCompiler;
<|endoftext|>