<commit_before>module.exports = SteamTradeOffers;

var request = require('request');
var cheerio = require('cheerio');
var Long = require('long');

require('util').inherits(SteamTradeOffers, require('events').EventEmitter);

function SteamTradeOffers() {
  require('events').EventEmitter.call(this);

  this._j = request.jar();
  this._request = request.defaults({jar:this._j});
}

SteamTradeOffers.prototype.setup = function(sessionID, webCookie, callback){
  var self = this;

  this.sessionID = sessionID;

  webCookie.forEach(function(name){
    setCookie(self, name);
  });

  getAPIKey(this, callback);
};

function getAPIKey(self, callback) {
  self._request.get({
    uri: 'http://steamcommunity.com/dev/apikey'
  }, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'retrieving apikey: ' + (error || response.statusCode));
      self.getAPIKey();
    } else {
      var $ = cheerio.load(body);
      if($('#bodyContents_ex h2').html() == 'Your Steam Web API Key'){
        var key = $('#bodyContents_ex p').html().split(' ')[1];
        self.APIKey = key;
        if(typeof callback == 'function'){
         callback();
        }
      } else {
        self._request.post({
          uri: 'http://steamcommunity.com/dev/registerkey',
          form: {
            domain: 'localhost',
            agreeToTerms: 1
          }
        }, function(error, response, body) {
            self.getAPIKey();
        }.bind(self));
      }
    }
  }.bind(self));
}

function setCookie(self, cookie) {
  self._j.add(request.cookie(cookie));
}

SteamTradeOffers.prototype.loadMyInventory = function(appid, contextid, callback) {
  var self = this;

  this._request.get({
    uri: 'http://steamcommunity.com/my/inventory/json/' + appid + '/' + contextid,
    json: true
  }, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'loading my inventory: ' + (error || response.statusCode));
      this.loadMyInventory(appid, contextid, callback);
    } else if (typeof body != 'object') {
      // no session
      if(typeof callback == 'function'){
       callback(new Error('No session'));
      }
    } else {
      if(typeof callback == 'function'){
        callback(null, mergeWithDescriptions(body.rgInventory, body.rgDescriptions, contextid)
        .concat(mergeWithDescriptions(body.rgCurrency, body.rgDescriptions, contextid)));
      }
    }
  }.bind(this));
};

SteamTradeOffers.prototype.loadPartnerInventory = function(partner, appid, contextid, callback) {
  var self = this;

  this._request.post({
    uri: 'http://steamcommunity.com/tradeoffer/new/partnerinventory/',
    json: true,
    headers: {
      referer: 'http://steamcommunity.com/tradeoffer/new/?partner=' + toAccountId(partner)
    },
    form: {
      sessionid: this.sessionID,
      partner: partner,
      appid: appid,
      contextid: contextid
    }
  }, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'loading partner inventory: ' + (error || response.statusCode));
      this.loadPartnerInventory(partner, appid, contextid, callback);
    } else if (typeof body != 'object') {
      // no session
      if(typeof callback == 'function'){
       callback(new Error('No session'));
      }
    } else {
      if(typeof callback == 'function'){
        callback(null, mergeWithDescriptions(body.rgInventory, body.rgDescriptions, contextid)
        .concat(mergeWithDescriptions(body.rgCurrency, body.rgDescriptions, contextid)));
      }
    }
  }.bind(this));
};

function mergeWithDescriptions(items, descriptions, contextid) {
  return Object.keys(items).map(function(id) {
    var item = items[id];
    var description = descriptions[item.classid + '_' + (item.instanceid || '0')];
    for (var key in description) {
        item[key] = description[key];
    }
    // add contextid because Steam is retarded
    item.contextid = contextid;
    return item;
  });
}

function doAPICall(self, options) {
  var request_params = {
    uri: 'http://api.steampowered.com/IEconService/' + options.method + '/?key=' + self.APIKey + ((options.post) ? '' : '&' + buildQuery(options.params)),
    json: true,
    method: options.post ? 'POST' : 'GET'
  };

  if (options.post) {
    request_params.form = options.params;
  }

  request(request_params, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'doing API call ' + options.method + ': ' + (error || response.statusCode));
      if(typeof options.callback == 'function'){
        options.callback(error || new Error(response.statusCode));
      }
    } else if (typeof body != 'object') {
      if(typeof options.callback == 'function'){
        options.callback(new Error('Invalid response'));
      }
    } else {
      if(typeof options.callback == 'function'){
        options.callback(null, body);
      }
    }
  });
}

function buildQuery(params) {
  var query = [];
  for(var x in params){
    if (params.hasOwnProperty(x)) {
      query.push(encodeURI(x) + '=' + encodeURI(params[x]));
    }
  }
  return query.join('&');
}

SteamTradeOffers.prototype.getOffers = function(options, callback) {
  doAPICall(this, {
    method: 'GetTradeOffers/v1',
    params: options,
    callback: function(error, res) {
      if (error) {
        if(typeof callback == 'function'){
          callback(error);
        }
      } else {
        if (res.response.trade_offers_received !== undefined) {
          res.response.trade_offers_received = res.response.trade_offers_received.map(function(offer) {
            offer.steamid_other = toSteamId(offer.accountid_other);
            return offer;
          });
        }
        if (res.response.trade_offers_sent !== undefined) {
          res.response.trade_offers_sent = res.response.trade_offers_sent.map(function(offer) {
            offer.steamid_other = toSteamId(offer.accountid_other);
            return offer;
          });
        }
        if(typeof callback == 'function'){
          callback(null, res);
        }
      }
    }
  });
};

SteamTradeOffers.prototype.getOffer = function(options, callback) {
  doAPICall(this, {
    method: 'GetTradeOffer/v1',
    params: options,
    callback: function(error, res) {
      if (error) {
        if(typeof callback == 'function'){
          callback(error);
        }
      } else {
        res.response.offer.steamid_other = toSteamId(res.response.offer.accountid_other);
        if(typeof callback == 'function'){
          callback(null, res);
        }
      }
    }
  });
};

SteamTradeOffers.prototype.declineOffer = function(tradeofferid, callback) {
  doAPICall(this, {method: 'DeclineTradeOffer/v1', params: {tradeofferid: tradeofferid}, post: true, callback: callback});
};

SteamTradeOffers.prototype.cancelOffer = function(tradeofferid, callback) {
  doAPICall(this, {method: 'CancelTradeOffer/v1', params: {tradeofferid: tradeofferid}, post: true, callback: callback});
};

SteamTradeOffers.prototype.acceptOffer = function(tradeofferid, callback) {
  if (typeof tradeofferid == 'undefined') {
    if(typeof callback == 'function'){
      callback(new Error('No options'));
    }
  } else {
    this._request.post({
      uri: 'http://steamcommunity.com/tradeoffer/' + tradeofferid + '/accept',
      headers: {
        referer: 'http://steamcommunity.com/tradeoffer/' + tradeofferid + '/'
      },
      form: {
        sessionid: this.sessionID,
        tradeofferid: tradeofferid
      }
    }, function(error, response, body) {
      if (error || response.statusCode != 200) {
        if(typeof callback == 'function'){
          callback(error);
        }
      } else {
        if(typeof callback == 'function'){
          callback(null, {response:{}});
        }
      }
    });
  }
};

function toSteamId(accountId) {
  return new Long(parseInt(accountId, 10), 0x1100001).toString();
}

function toAccountId(steamId) {
  return Long.fromString(steamId).toInt().toString();
}

SteamTradeOffers.prototype.makeOffer = function(partner, message, itemsFromMe, itemsFromThem, callback) {
  var self = this;

  var tradeoffer = {
    "newversion":true,
    "version":2,
    "me":{"assets": itemsFromMe,"currency":[],"ready":false},
    "them":{"assets": itemsFromThem,"currency":[],"ready":false}
  };

  this._request.post({
    uri: 'http://steamcommunity.com/tradeoffer/new/send',
    headers: {
      referer: 'http://steamcommunity.com/tradeoffer/new/?partner=' + toAccountId(partner)
    },
    form: {
      sessionid: this.sessionID,
      partner: partner,
      tradeoffermessage: message,
      json_tradeoffer: JSON.stringify(tradeoffer)
    }
  }, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'making an offer: ' + (error || response.statusCode));
      if(typeof callback == 'function'){
        callback(error || new Error(response.statusCode));
      }
    } else {
      if(typeof callback == 'function'){
        callback(null, JSON.parse(body));
      }
    }
  });
};
<commit_msg>Fix getAPIKey<commit_after>module.exports = SteamTradeOffers;

var request = require('request');
var cheerio = require('cheerio');
var Long = require('long');

require('util').inherits(SteamTradeOffers, require('events').EventEmitter);

function SteamTradeOffers() {
  require('events').EventEmitter.call(this);

  this._j = request.jar();
  this._request = request.defaults({jar:this._j});
}

SteamTradeOffers.prototype.setup = function(sessionID, webCookie, callback){
  var self = this;

  this.sessionID = sessionID;

  webCookie.forEach(function(name){
    setCookie(self, name);
  });

  getAPIKey(this, callback);
};

function getAPIKey(self, callback) {
  self._request.get({
    uri: 'http://steamcommunity.com/dev/apikey'
  }, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'retrieving apikey: ' + (error || response.statusCode));
      getAPIKey(self, callback);
    } else {
      var $ = cheerio.load(body);
      if($('#bodyContents_ex h2').html() == 'Your Steam Web API Key'){
        var key = $('#bodyContents_ex p').html().split(' ')[1];
        self.APIKey = key;
        if(typeof callback == 'function'){
         callback();
        }
      } else {
        self._request.post({
          uri: 'http://steamcommunity.com/dev/registerkey',
          form: {
            domain: 'localhost',
            agreeToTerms: 1
          }
        }, function(error, response, body) {
            getAPIKey(self, callback);
        }.bind(self));
      }
    }
  }.bind(self));
}

function setCookie(self, cookie) {
  self._j.add(request.cookie(cookie));
}

SteamTradeOffers.prototype.loadMyInventory = function(appid, contextid, callback) {
  var self = this;

  this._request.get({
    uri: 'http://steamcommunity.com/my/inventory/json/' + appid + '/' + contextid,
    json: true
  }, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'loading my inventory: ' + (error || response.statusCode));
      this.loadMyInventory(appid, contextid, callback);
    } else if (typeof body != 'object') {
      // no session
      if(typeof callback == 'function'){
       callback(new Error('No session'));
      }
    } else {
      if(typeof callback == 'function'){
        callback(null, mergeWithDescriptions(body.rgInventory, body.rgDescriptions, contextid)
        .concat(mergeWithDescriptions(body.rgCurrency, body.rgDescriptions, contextid)));
      }
    }
  }.bind(this));
};

SteamTradeOffers.prototype.loadPartnerInventory = function(partner, appid, contextid, callback) {
  var self = this;

  this._request.post({
    uri: 'http://steamcommunity.com/tradeoffer/new/partnerinventory/',
    json: true,
    headers: {
      referer: 'http://steamcommunity.com/tradeoffer/new/?partner=' + toAccountId(partner)
    },
    form: {
      sessionid: this.sessionID,
      partner: partner,
      appid: appid,
      contextid: contextid
    }
  }, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'loading partner inventory: ' + (error || response.statusCode));
      this.loadPartnerInventory(partner, appid, contextid, callback);
    } else if (typeof body != 'object') {
      // no session
      if(typeof callback == 'function'){
       callback(new Error('No session'));
      }
    } else {
      if(typeof callback == 'function'){
        callback(null, mergeWithDescriptions(body.rgInventory, body.rgDescriptions, contextid)
        .concat(mergeWithDescriptions(body.rgCurrency, body.rgDescriptions, contextid)));
      }
    }
  }.bind(this));
};

function mergeWithDescriptions(items, descriptions, contextid) {
  return Object.keys(items).map(function(id) {
    var item = items[id];
    var description = descriptions[item.classid + '_' + (item.instanceid || '0')];
    for (var key in description) {
        item[key] = description[key];
    }
    // add contextid because Steam is retarded
    item.contextid = contextid;
    return item;
  });
}

function doAPICall(self, options) {
  var request_params = {
    uri: 'http://api.steampowered.com/IEconService/' + options.method + '/?key=' + self.APIKey + ((options.post) ? '' : '&' + buildQuery(options.params)),
    json: true,
    method: options.post ? 'POST' : 'GET'
  };

  if (options.post) {
    request_params.form = options.params;
  }

  request(request_params, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'doing API call ' + options.method + ': ' + (error || response.statusCode));
      if(typeof options.callback == 'function'){
        options.callback(error || new Error(response.statusCode));
      }
    } else if (typeof body != 'object') {
      if(typeof options.callback == 'function'){
        options.callback(new Error('Invalid response'));
      }
    } else {
      if(typeof options.callback == 'function'){
        options.callback(null, body);
      }
    }
  });
}

function buildQuery(params) {
  var query = [];
  for(var x in params){
    if (params.hasOwnProperty(x)) {
      query.push(encodeURI(x) + '=' + encodeURI(params[x]));
    }
  }
  return query.join('&');
}

SteamTradeOffers.prototype.getOffers = function(options, callback) {
  doAPICall(this, {
    method: 'GetTradeOffers/v1',
    params: options,
    callback: function(error, res) {
      if (error) {
        if(typeof callback == 'function'){
          callback(error);
        }
      } else {
        if (res.response.trade_offers_received !== undefined) {
          res.response.trade_offers_received = res.response.trade_offers_received.map(function(offer) {
            offer.steamid_other = toSteamId(offer.accountid_other);
            return offer;
          });
        }
        if (res.response.trade_offers_sent !== undefined) {
          res.response.trade_offers_sent = res.response.trade_offers_sent.map(function(offer) {
            offer.steamid_other = toSteamId(offer.accountid_other);
            return offer;
          });
        }
        if(typeof callback == 'function'){
          callback(null, res);
        }
      }
    }
  });
};

SteamTradeOffers.prototype.getOffer = function(options, callback) {
  doAPICall(this, {
    method: 'GetTradeOffer/v1',
    params: options,
    callback: function(error, res) {
      if (error) {
        if(typeof callback == 'function'){
          callback(error);
        }
      } else {
        res.response.offer.steamid_other = toSteamId(res.response.offer.accountid_other);
        if(typeof callback == 'function'){
          callback(null, res);
        }
      }
    }
  });
};

SteamTradeOffers.prototype.declineOffer = function(tradeofferid, callback) {
  doAPICall(this, {method: 'DeclineTradeOffer/v1', params: {tradeofferid: tradeofferid}, post: true, callback: callback});
};

SteamTradeOffers.prototype.cancelOffer = function(tradeofferid, callback) {
  doAPICall(this, {method: 'CancelTradeOffer/v1', params: {tradeofferid: tradeofferid}, post: true, callback: callback});
};

SteamTradeOffers.prototype.acceptOffer = function(tradeofferid, callback) {
  if (typeof tradeofferid == 'undefined') {
    if(typeof callback == 'function'){
      callback(new Error('No options'));
    }
  } else {
    this._request.post({
      uri: 'http://steamcommunity.com/tradeoffer/' + tradeofferid + '/accept',
      headers: {
        referer: 'http://steamcommunity.com/tradeoffer/' + tradeofferid + '/'
      },
      form: {
        sessionid: this.sessionID,
        tradeofferid: tradeofferid
      }
    }, function(error, response, body) {
      if (error || response.statusCode != 200) {
        if(typeof callback == 'function'){
          callback(error);
        }
      } else {
        if(typeof callback == 'function'){
          callback(null, {response:{}});
        }
      }
    });
  }
};

function toSteamId(accountId) {
  return new Long(parseInt(accountId, 10), 0x1100001).toString();
}

function toAccountId(steamId) {
  return Long.fromString(steamId).toInt().toString();
}

SteamTradeOffers.prototype.makeOffer = function(partner, message, itemsFromMe, itemsFromThem, callback) {
  var self = this;

  var tradeoffer = {
    "newversion":true,
    "version":2,
    "me":{"assets": itemsFromMe,"currency":[],"ready":false},
    "them":{"assets": itemsFromThem,"currency":[],"ready":false}
  };

  this._request.post({
    uri: 'http://steamcommunity.com/tradeoffer/new/send',
    headers: {
      referer: 'http://steamcommunity.com/tradeoffer/new/?partner=' + toAccountId(partner)
    },
    form: {
      sessionid: this.sessionID,
      partner: partner,
      tradeoffermessage: message,
      json_tradeoffer: JSON.stringify(tradeoffer)
    }
  }, function(error, response, body) {
    if (error || response.statusCode != 200) {
      self.emit('debug', 'making an offer: ' + (error || response.statusCode));
      if(typeof callback == 'function'){
        callback(error || new Error(response.statusCode));
      }
    } else {
      if(typeof callback == 'function'){
        callback(null, JSON.parse(body));
      }
    }
  });
};
<|endoftext|>


<commit_before>'use strict';
var findup = require('findup-sync');
var gutil = require('gulp-util');
var casperPath = findup('node_modules/sheut/casper.js') || './casper.js';
var pathsPath = findup('node_modules/sheut/paths/index.js') || './paths/index.js';
var configPath = findup('./sheut.config.js');
if (!configPath) {
    console.log('Please add a sheut.config.js file in your root.');
    process.exit(1);
}

var config = require(configPath);
var mkdirp = require('mkdirp');
var del = require('del');
var fs = require('fs');
var fse = require('fs-extra');
var child_process = require('child_process');
var spawn = child_process.spawn;
var exec = child_process.exec;
var execFile = child_process.execFile;
var resemble = require('./wrappers/resemble');
var nodeCasper = require('./wrappers/casper');
var server = require('./wrappers/server');
var paths = require(pathsPath)(config);

function capture(cb){
    return clean(function(){
        if (config.server){
            var testServer = server.start(config.server.dir, config.server.port);
        }
        nodeCasper([casperPath, '--config=' + configPath, '--paths=' + pathsPath]).then(function(result){
            testServer && testServer.close();
            cb && cb();
        });
    });
}

function accept(cb){
    return fse.copy(paths.new, paths.reference, function(err){
        if (err) return console.error(err)
        cb && cb();
    })
}

function clean(cb){
    return del([paths.new, paths.different], cb);
}

function compare(cb){
    execFile('find', [ paths.reference ], function(err, stdout, stderr) {
        var errors = [];
        var file_list = stdout.split('\n');
        file_list.shift();
        file_list.pop();
        file_list.forEach(function(file){
            var img1 = fs.readFileSync(file);
            var img2 = fs.readFileSync(file.replace('/reference/', '/new/'));
            var imgDiff = file.replace('/reference/', '/different/');
            var api = resemble(img2).compareTo(img1).onComplete(function(data){
                if (!data.isSameDimensions) {
                    if (data.dimensionDifference.width !== 0) {
                        errors.push('the new image is wider/smaller: ' + data.dimensionDifference.width + 'px different');
                    }
                    if (data.dimensionDifference.height !== 0) {
                        errors.push('the new image is taller/smaller: ' + data.dimensionDifference.height + 'px different');
                    }
                    errors.push(file)
                }
                if (data.misMatchPercentage > 0) {
                    errors.push('The new image content has changed: ' + data.misMatchPercentage + '% different');
                    errors.push(file)
                }
                if (data.misMatchPercentage > 0 || !data.isSameDimensions){
                    mkdirp(paths.different, function saveDifference(){
                        var base64 = data.getImageDataUrl().replace(/^data:image\/png;base64,/, "");
                        fs.writeFile(imgDiff, base64, {encoding:'base64'}, function(){
                            //process.exit(1);
                        });
                    });
                }
            });
        });

        if (err){
            var existingError = new Error(err);
            err = new gutil.PluginError('Sheut: ', existingError, {showStack: true});
        }
        if (errors.length > 0) {
            var existingError = new Error(errors.join('\n'));
            err = new gutil.PluginError('Sheut: ', existingError, {showStack: true});
        } else {
            console.log('All reference shots match the new images');
        }
        cb && cb(err);
    });
}

module.exports = {
    capture: capture,
    accept: accept,
    compare: compare
};<commit_msg>refactor + add promises<commit_after>'use strict';
var Promise = require('es6-promise').Promise;
var findup = require('findup-sync');
var gutil = require('gulp-util');
var casperPath = findup('node_modules/sheut/casper.js') || './casper.js';
var pathsPath = findup('node_modules/sheut/paths/index.js') || './paths/index.js';
var configPath = findup('./sheut.config.js');
if (!configPath) {
    console.log('Please add a sheut.config.js file in your root.');
    process.exit(1);
}

var config = require(configPath);
var mkdirp = require('mkdirp');
var del = require('del');
var fs = require('fs');
var fse = require('fs-extra');
var child_process = require('child_process');
var spawn = child_process.spawn;
var exec = child_process.exec;
var execFile = child_process.execFile;
var resemble = require('./wrappers/resemble');
var nodeCasper = require('./wrappers/casper');
var server = require('./wrappers/server');
var paths = require(pathsPath)(config);

function capture(){
    return clean().then(function(){
        return new Promise(function(resolve, reject){
            if (config.server){
                var testServer = server.start(config.server.dir, config.server.port);
            }
            nodeCasper([casperPath, '--config=' + configPath, '--paths=' + pathsPath]).then(function(result){
                testServer && testServer.close();
                resolve();
            });
        });
    });
};

function accept(cb){
    return new Promise(function(resolve, reject){
        fse.copy(paths.new, paths.reference, function(err){
            if (err) return reject(err)
            resolve();
        })
    });
}

function clean(){
    return new Promise(function(resolve, reject){
        del([paths.new, paths.different], function(){
            resolve()
        });
    });
}

function findFiles(dir){
    return new Promise(function(resolve, reject){
        execFile('find', [ dir ], function(err, stdout, stderr) {
            resolve(stdout);
        });
    });
};

function saveDifference(file, data){
    return new Promise(function(resolve, reject){
        mkdirp(paths.different, function (){
            var base64 = data.getImageDataUrl().replace(/^data:image\/png;base64,/, "");
            fs.writeFile(file, base64, {encoding:'base64'}, function(){
                resolve()
            });
        });
    });
};

function compareAndSaveDifference(file){
    return new Promise(function(resolve, reject){
        var errors = [];
        var img1 = fs.readFileSync(file);
        var img2 = fs.readFileSync(file.replace('/reference/', '/new/'));
        var imgDiff = file.replace('/reference/', '/different/');
        var api = resemble(img2).compareTo(img1).onComplete(function(data){
            var errors = imageErrors(imgDiff, data);
            if (errors.length){
                saveDifference(imgDiff, data).then(function(){
                    reject(errors);
                });
            } else {
                resolve();
            }
        });
    });
}

function imageErrors(file, data){
    var errors = [];
    if (!data.isSameDimensions) {
        if (data.dimensionDifference.width !== 0) {
            errors.push('the new image is wider/smaller: ' + data.dimensionDifference.width + 'px different');
        }
        if (data.dimensionDifference.height !== 0) {
            errors.push('the new image is taller/smaller: ' + data.dimensionDifference.height + 'px different');
        }
        errors.push(file)
    }
    if (data.misMatchPercentage > 0) {
        errors.push('The new image content has changed: ' + data.misMatchPercentage + '% different');
        errors.push(file)
    }
    return errors;
}

function compare(){
    return findFiles(paths.reference).then(function(files){
        var promises = [];
        var file_list = files.split('\n');
        file_list.shift();
        file_list.pop();
        file_list.forEach(function(file){
            promises.push(compareAndSaveDifference(file));
        });

        if (promises.length > 0) {
            return Promise.all(promises)
        } else {
            console.log('All reference shots match the new images');
            return new Promise;
        }
    });
}

module.exports = {
    capture: capture,
    accept: accept,
    compare: compare
};<|endoftext|>


<commit_before>'use strict';

var Cache = require('cache-swap'),
    File = require('vinyl'),
    objectAssign = require('object-assign'),
    objectOmit = require('object.omit'),
    PluginError = require('gulp-util').PluginError,
    through = require('through2'),
    TaskProxy = require('./lib/TaskProxy'),
    pkg = require('./package.json');

var fileCache = new Cache({
    cacheDirName: 'gulp-cache'
});

function defaultKey (file) {
    return [pkg.version, file.contents.toString('base64')].join('');
}

var defaultOptions = {
    fileCache: fileCache,
    name: 'default',
    key: defaultKey,
    restore: function (restored) {
        if (restored.contents) {
            // Handle node 0.11 buffer to JSON as object with { type: 'buffer', data: [...] }
            if (restored && restored.contents && Array.isArray(restored.contents.data)) {
                restored.contents = new Buffer(restored.contents.data);
            } else if (Array.isArray(restored.contents)) {
                restored.contents = new Buffer(restored.contents);
            } else if (typeof restored.contents === 'string') {
                restored.contents = new Buffer(restored.contents, 'base64');
            }
        }

        var restoredFile = new File(restored),
            extraTaskProperties = objectOmit(restored, Object.keys(restoredFile));

        // Restore any properties that the original task put on the file;
        // but omit the normal properties of the file
        return objectAssign(restoredFile, extraTaskProperties);
    },
    success: true,
    value: function (file) {
        // Convert from a File object (from vinyl) into a plain object
        return ['cwd', 'base', 'contents', 'stat', 'history'].reduce(function(obj, propName) {
          obj[propName] = file[propName];
          return obj;
        }, {});
    }
};

var cacheTask = function (task, opts) {
    // Check for required task option
    if (!task) {
        throw new PluginError('gulp-cache', 'Must pass a task to cache()');
    }

    // Check if this task participates in the cacheable contract
    if (task.cacheable) {
        // Use the cacheable options, but allow the user to override them
        opts = objectAssign({}, task.cacheable, opts);
    }

    // Make sure we have some sane defaults
    opts = objectAssign({}, cacheTask.defaultOptions, opts);

    return through.obj(function (file, enc, cb) {
        // Indicate clearly that we do not support Streams
        if (file.isStream()) {
            cb(new PluginError('gulp-cache', 'Cannot operate on stream sources'));
            return;
        }

        // Create a TaskProxy object and start up processFile().

        var taskProxy = new TaskProxy({
            task: task,
            file: file,
            opts: opts
        });

        taskProxy.processFile().then(function (result) {
            cb(null, result);
        }, function (err) {
            cb(new PluginError('gulp-cache', err));
        });
    });
};

cacheTask.clear = function (opts) {
    opts = objectAssign({}, cacheTask.defaultOptions, opts);

    return through.obj(function (file, enc, cb) {
        // Indicate clearly that we do not support Streams
        if (file.isStream()) {
            cb(new PluginError('gulp-cache', 'Can not operate on stream sources'));
            return;
        }

        var taskProxy = new TaskProxy({
            task: null,
            file: file,
            opts: opts
        });

        taskProxy.removeCachedResult().then(function () {
            cb(null, file);
        }).catch(function (err) {
            cb(new PluginError('gulp-cache', err));
        });
    });
};

cacheTask.clearAll = function (done) {
    fileCache.clear(null, function (err) {
        if (err) {
            throw new PluginError('gulp-cache', 'Problem clearing the cache: ' + err.message);
        }

        if (done) {
          done();
        }
    });
};

cacheTask.fileCache = fileCache;
cacheTask.defaultOptions = defaultOptions;
cacheTask.Cache = Cache;

module.exports = cacheTask;
<commit_msg>Pass through the directories to the stream<commit_after>'use strict';

var Cache = require('cache-swap'),
    File = require('vinyl'),
    objectAssign = require('object-assign'),
    objectOmit = require('object.omit'),
    PluginError = require('gulp-util').PluginError,
    through = require('through2'),
    TaskProxy = require('./lib/TaskProxy'),
    pkg = require('./package.json');

var fileCache = new Cache({
    cacheDirName: 'gulp-cache'
});

function defaultKey (file) {
    return [pkg.version, file.contents.toString('base64')].join('');
}

var defaultOptions = {
    fileCache: fileCache,
    name: 'default',
    key: defaultKey,
    restore: function (restored) {
        if (restored.contents) {
            // Handle node 0.11 buffer to JSON as object with { type: 'buffer', data: [...] }
            if (restored && restored.contents && Array.isArray(restored.contents.data)) {
                restored.contents = new Buffer(restored.contents.data);
            } else if (Array.isArray(restored.contents)) {
                restored.contents = new Buffer(restored.contents);
            } else if (typeof restored.contents === 'string') {
                restored.contents = new Buffer(restored.contents, 'base64');
            }
        }

        var restoredFile = new File(restored),
            extraTaskProperties = objectOmit(restored, Object.keys(restoredFile));

        // Restore any properties that the original task put on the file;
        // but omit the normal properties of the file
        return objectAssign(restoredFile, extraTaskProperties);
    },
    success: true,
    value: function (file) {
        // Convert from a File object (from vinyl) into a plain object
        return ['cwd', 'base', 'contents', 'stat', 'history'].reduce(function(obj, propName) {
          obj[propName] = file[propName];
          return obj;
        }, {});
    }
};

var cacheTask = function (task, opts) {
    // Check for required task option
    if (!task) {
        throw new PluginError('gulp-cache', 'Must pass a task to cache()');
    }

    // Check if this task participates in the cacheable contract
    if (task.cacheable) {
        // Use the cacheable options, but allow the user to override them
        opts = objectAssign({}, task.cacheable, opts);
    }

    // Make sure we have some sane defaults
    opts = objectAssign({}, cacheTask.defaultOptions, opts);

    return through.obj(function (file, enc, cb) {
        if (file.isNull()) {
            cb(null, file);
            return;
        }

        // Indicate clearly that we do not support Streams
        if (file.isStream()) {
            cb(new PluginError('gulp-cache', 'Cannot operate on stream sources'));
            return;
        }

        // Create a TaskProxy object and start up processFile().

        var taskProxy = new TaskProxy({
            task: task,
            file: file,
            opts: opts
        });

        taskProxy.processFile().then(function (result) {
            cb(null, result);
        }, function (err) {
            cb(new PluginError('gulp-cache', err));
        });
    });
};

cacheTask.clear = function (opts) {
    opts = objectAssign({}, cacheTask.defaultOptions, opts);

    return through.obj(function (file, enc, cb) {
        if (file.isNull()) {
            cb(null, file);
            return;
        }

        // Indicate clearly that we do not support Streams
        if (file.isStream()) {
            cb(new PluginError('gulp-cache', 'Cannot operate on stream sources'));
            return;
        }

        var taskProxy = new TaskProxy({
            task: null,
            file: file,
            opts: opts
        });

        taskProxy.removeCachedResult().then(function () {
            cb(null, file);
        }).catch(function (err) {
            cb(new PluginError('gulp-cache', err));
        });
    });
};

cacheTask.clearAll = function (done) {
    fileCache.clear(null, function (err) {
        if (err) {
            throw new PluginError('gulp-cache', 'Problem clearing the cache: ' + err.message);
        }

        if (done) {
          done();
        }
    });
};

cacheTask.fileCache = fileCache;
cacheTask.defaultOptions = defaultOptions;
cacheTask.Cache = Cache;

module.exports = cacheTask;
<|endoftext|>


<commit_before>'use strict';
var vm = require('vm');
var fs = require('fs');
var _ = require('lodash');
var Promise = require('bluebird');
var path = require('path');
Promise.promisifyAll(fs);

var webpack = require('webpack');
var NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin');
var NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
var LoaderTargetPlugin = require('webpack/lib/LoaderTargetPlugin');
var LibraryTemplatePlugin = require('webpack/lib/LibraryTemplatePlugin');
var SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');

function HtmlWebpackPlugin(options) {
  // Default options
  this.options = _.extend({
    template: __dirname + '/default_index.html',
    filename: 'index.html',
    hash: false,
    inject: true,
    compile: true,
    favicon: false,
    minify: false,
    chunks: 'all',
    excludeChunks: [],
    title: 'Webpack App'
  }, options);
  // If the template doesn't use a loader use the blueimp template loader
  if(this.options.template.indexOf('!') === -1) {
    this.options.template = require.resolve('./loader.js') + '!' + path.resolve(this.options.template);
  }
  // Resolve template path
  this.options.template = this.options.template.replace(
    /(\!)([^\/\\][^\!\?]+|[^\/\\!?])($|\?.+$)/,
    function(match, prefix, filepath, postfix) {
      return prefix + path.resolve(filepath) + postfix;
    });
}

HtmlWebpackPlugin.prototype.apply = function(compiler) {
  var self = this;
  var compilationPromise;
  self.context = compiler.context;

  compiler.plugin('make', function(compilation, callback) {
    // Compile the template
    compilationPromise = self.compileTemplate(self.options.template, self.options.filename, compilation)
      .catch(function(err) {
        // In case anything went wrong the promise is resolved
        // with the error message and an error is logged
        var errorMessage = "HtmlWebpackPlugin Error: " + err;
        compilation.errors.push(new Error(errorMessage));
        return errorMessage;
      });
    compilationPromise.finally(callback);
  });

  compiler.plugin('emit', function(compilation, callback) {
    // Get all chunks
    var chunks = self.filterChunks(compilation.getStats().toJson(), self.options.chunks, self.options.excludeChunks);
    // Get assets
    var assets = self.htmlWebpackPluginAssets(compilation, chunks);
    Promise.resolve()
      // Favicon
      .then(function() {
        if (self.options.favicon) {
          return self.addFileToAssets(self.options.favicon, compilation)
            .then(function(faviconBasename){
              assets.favicon = faviconBasename;
            });
        }
      })
      // Wait for the compilation to finish
      .then(function() {
        return compilationPromise;
      })
      .then(function(resultAsset) {
        // Allow to use a custom function / string instead
        if (self.options.templateContent) {
          return self.options.templateContent;
        }
        // Once everything is compiled evaluate the html factory
        // and replace it with its content
        return self.evaluateCompilationResult(compilation, resultAsset);
      })
      // Execute the template
      .then(function(compilationResult) {
        // If the loader result is a function execute it to retreive the html
        // otherwise use the returned html
        return typeof compilationResult !== 'function' ? compilationResult :
          self.executeTemplate(compilationResult, chunks, assets, compilation);
      })
      .then(function(html) {
        // Add the stylesheets, scripts and so on to the resulting html
        return self.postProcessHtml(html, assets);
      })
      .catch(function(err) {
        // In case anything went wrong the promise is resolved
        // with the error message and an error is logged
        var errorMessage = "HtmlWebpackPlugin Error: " + err;
        compilation.errors.push(new Error(errorMessage));
        return errorMessage;
      })
      .then(function(html) {
        // Replace the compilation result with the evaluated html code
        compilation.assets[self.options.filename] = {
          source: function() {
            return html;
          },
          size: function() {
            return html.length;
          }
        };
        callback();
      });
    });
};

/**
 * Returns the child compiler name
 */
HtmlWebpackPlugin.prototype.getCompilerName = function() {
  var absolutePath = path.resolve(this.context, this.options.filename);
  var relativePath = path.relative(this.context, absolutePath);
  return 'html-webpack-plugin for "' + (absolutePath.length < relativePath.length ? absolutePath : relativePath) + '"';
};

/**
 * Compiles the template into a nodejs factory, adds its to the compilation.assets
 * and returns a promise of the result asset object.
 */
HtmlWebpackPlugin.prototype.compileTemplate = function(template, outputFilename, compilation) {
  // The entry file is just an empty helper as the dynamic template
  // require is added in "loader.js"
  var outputOptions = {
    filename: outputFilename,
    publicPath: compilation.outputOptions.publicPath
  };
  // Create an additional child compiler which takes the template
  // and turns it into an Node.JS html factory.
  // This allows us to use loaders during the compilation
  var compilerName = this.getCompilerName();
  var childCompiler = compilation.createChildCompiler(compilerName, outputOptions);
  childCompiler.apply(
    new NodeTemplatePlugin(outputOptions),
    new NodeTargetPlugin(),
    new LibraryTemplatePlugin('result', 'var'),
    new SingleEntryPlugin(this.context, template),
    new LoaderTargetPlugin('node'),
    new webpack.DefinePlugin({ HTML_WEBPACK_PLUGIN : 'true' })
  );

  // Create a subCache (copied from https://github.com/SanderSpies/extract-text-webpack-plugin/blob/master/loader.js)
  childCompiler.plugin('compilation', function(compilation) {
    if(compilation.cache) {
      if(!compilation.cache[compilerName]) {
        compilation.cache[compilerName] = {};
      }
      compilation.cache = compilation.cache[compilerName];
    }
  });
  // Compile and return a promise
  return new Promise(function (resolve, reject) {
    childCompiler.runAsChild(function(err, entries, childCompilation) {
      // Resolve / reject the promise
      if (childCompilation.errors.length) {
        var errorDetails = childCompilation.errors.map(function(error) {
            return error.message + (error.error ? ':\n' + error.error: '');
          }).join('\n');
        reject(new Error('Child compilation failed:\n' + errorDetails));
      } else {
        resolve(compilation.assets[outputFilename]);
      }
    });
  });
};

/**
 * Evaluates the child compilation result
 * Returns a promise
 */
HtmlWebpackPlugin.prototype.evaluateCompilationResult = function(compilation, compilationResult) {
  if(!compilationResult) {
    return Promise.reject('The child compilation didn\'t provide a result');
  }
  if(typeof source === 'string') {
    // If the compilation result is already evaluted return it
    return Promise.resolve(source);
  }
  // Strip the leading 'var '
  var source = compilationResult.source().substr(3);
  // Evaluate code and cast to string
  var newSource;
  try {
    newSource = vm.runInThisContext(source);
  } catch (e) {
    // Log syntax error
    var syntaxError = require('syntax-error')(source);
    var errorMessage = 'Template compilation failed: ' + e +
      (syntaxError ? '\n' + syntaxError + '\n\n\n' + source.split('\n').map(function(row, i) {
        return (1 + i) + '  - ' + row;
      }).join('\n') : '');
    compilation.errors.push(new Error(errorMessage));
    return Promise.reject(e);
  }
  return typeof newSource === 'string' || typeof newSource === 'function' ?
    Promise.resolve(newSource) :
    Promise.reject('The loader "' + this.options.template + '" didn\'t return html.');
};

/**
 * Html post processing
 *
 * Returns a promise
 */
HtmlWebpackPlugin.prototype.executeTemplate = function(templateFunction, chunks, assets, compilation) {
  var self = this;
  return Promise.resolve()
    // Template processing
    .then(function() {
      var templateParams = {
        webpack: compilation.getStats().toJson(),
        webpackConfig: compilation.options,
        htmlWebpackPlugin: {
          files: assets,
          options: self.options,
        }
      };
      var html = '';
      try {
        html = templateFunction(templateParams);
      } catch (e) {
        compilation.errors.push(new Error('Template execution failed: ' + e));
        return Promise.reject(e);
      }
      return html;
    });
};

/**
 * Html post processing
 *
 * Returns a promise
 */
HtmlWebpackPlugin.prototype.postProcessHtml = function(html, assets) {
  var self = this;
  return Promise.resolve()
    // Inject
    .then(function() {
      if (self.options.inject) {
        return self.injectAssetsIntoHtml(html, assets);
      } else {
        return html;
      }
    })
    // Minify
    .then(function(html) {
      if (self.options.minify) {
        var minify = require('html-minifier').minify;
        // If `options.minify` is set to true use the default minify options
        var minifyOptions = _.isObject(self.options.minify) ? self.options.minify : {};
        try {
          minify(html, minifyOptions);
        } catch(e) {
          Promise.reject(e);
        }
      }
      return html;
    });
};

/*
 * Pushes the content of the given filename to the compilation assets
 */
HtmlWebpackPlugin.prototype.addFileToAssets = function(filename, compilation) {
  return Promise.props({
    size: fs.statAsync(filename),
    source: fs.readFileAsync(filename)
  })
  .catch(function() {
    return Promise.reject(new Error('HtmlWebpackPlugin: could not load file ' + filename));
  })
  .then(function(results) {
    var basename = path.basename(filename);
    compilation.fileDependencies.push(filename);
    compilation.assets[basename] = {
      source: function() {
        return results.source;
      },
      size: function() {
        return results.size.size;
      }
    };
    return basename;
  });
};

/**
 * Return all chunks from the compilation result which match the exclude and include filters
 */
HtmlWebpackPlugin.prototype.filterChunks = function (webpackStatsJson, includedChunks, excludedChunks) {
  var chunks = webpackStatsJson.chunks.filter(function(chunk){
    var chunkName = chunk.names[0];
    // This chunk doesn't have a name. This script can't handled it.
    if (chunkName === undefined) {
      return false;
    }
    // Skip if the chunks should be filtered and the given chunk was not added explicity
    if (Array.isArray(includedChunks) && includedChunks.indexOf(chunkName) === -1) {
      return false;
    }
    // Skip if the chunks should be filtered and the given chunk was excluded explicity
    if (Array.isArray(excludedChunks) && excludedChunks.indexOf(chunkName) !== -1) {
      return false;
    }
    // Add otherwise
    return true;
  });
  return chunks.sort(function orderEntryLast(a, b) {
    if (a.entry !== b.entry) {
      return b.entry ? 1 : -1;
    } else {
      return b.id - a.id;
    }
  });
};

HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, chunks) {
  var self = this;
  var webpackStatsJson = compilation.getStats().toJson();

  // Use the configured public path or build a relative path
  var publicPath = typeof compilation.options.output.publicPath !== 'undefined' ?
      compilation.options.output.publicPath :
      path.relative(path.dirname(self.options.filename), '.');

  if (publicPath.length && publicPath.substr(-1, 1) !== '/') {
    publicPath += '/';
  }

  var assets = {
    // Will contain all js & css files by chunk
    chunks: {},
    // Will contain all js files
    js: [],
    // Will contain all css files
    css: [],
    // Will contain the html5 appcache manifest files if it exists
    manifest: Object.keys(compilation.assets).filter(function(assetFile){
      return path.extname(assetFile) === '.appcache';
    })[0]
  };

  // Append a hash for cache busting
  if (this.options.hash) {
    assets.manifest = self.appendHash(assets.manifest, webpackStatsJson.hash);
    assets.favicon = self.appendHash(assets.favicon, webpackStatsJson.hash);
  }

  for (var i = 0; i < chunks.length; i++) {
    var chunk = chunks[i];
    var chunkName = chunk.names[0];

    assets.chunks[chunkName] = {};

    // Prepend the public path to all chunk files
    var chunkFiles = [].concat(chunk.files).map(function(chunkFile) {
      return publicPath + chunkFile;
    });

    // Append a hash for cache busting
    if (this.options.hash) {
      chunkFiles = chunkFiles.map(function(chunkFile) {
        return self.appendHash(chunkFile, webpackStatsJson.hash);
      });
    }

    // Webpack outputs an array for each chunk when using sourcemaps
    // But we need only the entry file
    var entry = chunkFiles[0];
    assets.chunks[chunkName].size = chunk.size;
    assets.chunks[chunkName].entry = entry;
    assets.js.push(entry);

    // Gather all css files
    var css = chunkFiles.filter(function(chunkFile){
      // Some chunks may contain content hash in their names, for ex. 'main.css?1e7cac4e4d8b52fd5ccd2541146ef03f'.
      // We must proper handle such cases, so we use regexp testing here
      return /^.css($|\?)/.test(path.extname(chunkFile));
    });
    assets.chunks[chunkName].css = css;
    assets.css = assets.css.concat(css);
  }

  // Duplicate css assets can occur on occasion if more than one chunk
  // requires the same css.
  assets.css = _.uniq(assets.css);

  return assets;
};

/**
 * Injects the assets into the given html string
 */
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, assets) {
  var chunks = Object.keys(assets.chunks);

  // Gather all css and script files
  var styles = [];
  var scripts = [];
  chunks.forEach(function(chunkName) {
    styles = styles.concat(assets.chunks[chunkName].css);
    scripts.push(assets.chunks[chunkName].entry);
  });
  // Turn script files into script tags
  scripts = scripts.map(function(scriptPath) {
    return '<script src="' + scriptPath + '"></script>';
  });
  // Turn css files into link tags
  styles = styles.map(function(stylePath) {
    return '<link href="' + stylePath + '" rel="stylesheet">';
  });
  // Injections
  var head = [];
  var body = [];

  // If there is a favicon present, add it to the head
  if (assets.favicon) {
    head.push('<link rel="shortcut icon" href="' + assets.favicon + '">');
  }
  // Add styles to the head
  head = head.concat(styles);
  // Add scripts to body or head
  if (this.options.inject === 'head') {
    head = body.concat(scripts);
  } else {
    body = body.concat(scripts);
  }
  // Append assets to head element
  html = html.replace(/(<\/head>)/i, function (match) {
    return head.join('') + match;
  });
  // Append assets to body element
    html = html.replace(/(<\/body>)/i, function (match) {
      return body.join('') + match;
    });
  // Inject manifest into the opening html tag
  if (assets.manifest) {
    html = html.replace(/(<html[^>]*)(>)/i, function (match, start, end) {
      // Append the manifest only if no manifest was specified
      if (/\smanifest\s*=/.test(match)) {
        return match;
      }
      return start + ' manifest="' + assets.manifest + '"' + end;
    });
  }
  return html;
};

/**
 * Appends a cache busting hash
 */
HtmlWebpackPlugin.prototype.appendHash = function (url, hash) {
  if (!url) {
    return url;
  }
  return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
};

module.exports = HtmlWebpackPlugin;
<commit_msg>Return minified html from promise<commit_after>'use strict';
var vm = require('vm');
var fs = require('fs');
var _ = require('lodash');
var Promise = require('bluebird');
var path = require('path');
Promise.promisifyAll(fs);

var webpack = require('webpack');
var NodeTemplatePlugin = require('webpack/lib/node/NodeTemplatePlugin');
var NodeTargetPlugin = require('webpack/lib/node/NodeTargetPlugin');
var LoaderTargetPlugin = require('webpack/lib/LoaderTargetPlugin');
var LibraryTemplatePlugin = require('webpack/lib/LibraryTemplatePlugin');
var SingleEntryPlugin = require('webpack/lib/SingleEntryPlugin');

function HtmlWebpackPlugin(options) {
  // Default options
  this.options = _.extend({
    template: __dirname + '/default_index.html',
    filename: 'index.html',
    hash: false,
    inject: true,
    compile: true,
    favicon: false,
    minify: false,
    chunks: 'all',
    excludeChunks: [],
    title: 'Webpack App'
  }, options);
  // If the template doesn't use a loader use the blueimp template loader
  if(this.options.template.indexOf('!') === -1) {
    this.options.template = require.resolve('./loader.js') + '!' + path.resolve(this.options.template);
  }
  // Resolve template path
  this.options.template = this.options.template.replace(
    /(\!)([^\/\\][^\!\?]+|[^\/\\!?])($|\?.+$)/,
    function(match, prefix, filepath, postfix) {
      return prefix + path.resolve(filepath) + postfix;
    });
}

HtmlWebpackPlugin.prototype.apply = function(compiler) {
  var self = this;
  var compilationPromise;
  self.context = compiler.context;

  compiler.plugin('make', function(compilation, callback) {
    // Compile the template
    compilationPromise = self.compileTemplate(self.options.template, self.options.filename, compilation)
      .catch(function(err) {
        // In case anything went wrong the promise is resolved
        // with the error message and an error is logged
        var errorMessage = "HtmlWebpackPlugin Error: " + err;
        compilation.errors.push(new Error(errorMessage));
        return errorMessage;
      });
    compilationPromise.finally(callback);
  });

  compiler.plugin('emit', function(compilation, callback) {
    // Get all chunks
    var chunks = self.filterChunks(compilation.getStats().toJson(), self.options.chunks, self.options.excludeChunks);
    // Get assets
    var assets = self.htmlWebpackPluginAssets(compilation, chunks);
    Promise.resolve()
      // Favicon
      .then(function() {
        if (self.options.favicon) {
          return self.addFileToAssets(self.options.favicon, compilation)
            .then(function(faviconBasename){
              assets.favicon = faviconBasename;
            });
        }
      })
      // Wait for the compilation to finish
      .then(function() {
        return compilationPromise;
      })
      .then(function(resultAsset) {
        // Allow to use a custom function / string instead
        if (self.options.templateContent) {
          return self.options.templateContent;
        }
        // Once everything is compiled evaluate the html factory
        // and replace it with its content
        return self.evaluateCompilationResult(compilation, resultAsset);
      })
      // Execute the template
      .then(function(compilationResult) {
        // If the loader result is a function execute it to retreive the html
        // otherwise use the returned html
        return typeof compilationResult !== 'function' ? compilationResult :
          self.executeTemplate(compilationResult, chunks, assets, compilation);
      })
      .then(function(html) {
        // Add the stylesheets, scripts and so on to the resulting html
        return self.postProcessHtml(html, assets);
      })
      .catch(function(err) {
        // In case anything went wrong the promise is resolved
        // with the error message and an error is logged
        var errorMessage = "HtmlWebpackPlugin Error: " + err;
        compilation.errors.push(new Error(errorMessage));
        return errorMessage;
      })
      .then(function(html) {
        // Replace the compilation result with the evaluated html code
        compilation.assets[self.options.filename] = {
          source: function() {
            return html;
          },
          size: function() {
            return html.length;
          }
        };
        callback();
      });
    });
};

/**
 * Returns the child compiler name
 */
HtmlWebpackPlugin.prototype.getCompilerName = function() {
  var absolutePath = path.resolve(this.context, this.options.filename);
  var relativePath = path.relative(this.context, absolutePath);
  return 'html-webpack-plugin for "' + (absolutePath.length < relativePath.length ? absolutePath : relativePath) + '"';
};

/**
 * Compiles the template into a nodejs factory, adds its to the compilation.assets
 * and returns a promise of the result asset object.
 */
HtmlWebpackPlugin.prototype.compileTemplate = function(template, outputFilename, compilation) {
  // The entry file is just an empty helper as the dynamic template
  // require is added in "loader.js"
  var outputOptions = {
    filename: outputFilename,
    publicPath: compilation.outputOptions.publicPath
  };
  // Create an additional child compiler which takes the template
  // and turns it into an Node.JS html factory.
  // This allows us to use loaders during the compilation
  var compilerName = this.getCompilerName();
  var childCompiler = compilation.createChildCompiler(compilerName, outputOptions);
  childCompiler.apply(
    new NodeTemplatePlugin(outputOptions),
    new NodeTargetPlugin(),
    new LibraryTemplatePlugin('result', 'var'),
    new SingleEntryPlugin(this.context, template),
    new LoaderTargetPlugin('node'),
    new webpack.DefinePlugin({ HTML_WEBPACK_PLUGIN : 'true' })
  );

  // Create a subCache (copied from https://github.com/SanderSpies/extract-text-webpack-plugin/blob/master/loader.js)
  childCompiler.plugin('compilation', function(compilation) {
    if(compilation.cache) {
      if(!compilation.cache[compilerName]) {
        compilation.cache[compilerName] = {};
      }
      compilation.cache = compilation.cache[compilerName];
    }
  });
  // Compile and return a promise
  return new Promise(function (resolve, reject) {
    childCompiler.runAsChild(function(err, entries, childCompilation) {
      // Resolve / reject the promise
      if (childCompilation.errors.length) {
        var errorDetails = childCompilation.errors.map(function(error) {
            return error.message + (error.error ? ':\n' + error.error: '');
          }).join('\n');
        reject(new Error('Child compilation failed:\n' + errorDetails));
      } else {
        resolve(compilation.assets[outputFilename]);
      }
    });
  });
};

/**
 * Evaluates the child compilation result
 * Returns a promise
 */
HtmlWebpackPlugin.prototype.evaluateCompilationResult = function(compilation, compilationResult) {
  if(!compilationResult) {
    return Promise.reject('The child compilation didn\'t provide a result');
  }
  if(typeof source === 'string') {
    // If the compilation result is already evaluted return it
    return Promise.resolve(source);
  }
  // Strip the leading 'var '
  var source = compilationResult.source().substr(3);
  // Evaluate code and cast to string
  var newSource;
  try {
    newSource = vm.runInThisContext(source);
  } catch (e) {
    // Log syntax error
    var syntaxError = require('syntax-error')(source);
    var errorMessage = 'Template compilation failed: ' + e +
      (syntaxError ? '\n' + syntaxError + '\n\n\n' + source.split('\n').map(function(row, i) {
        return (1 + i) + '  - ' + row;
      }).join('\n') : '');
    compilation.errors.push(new Error(errorMessage));
    return Promise.reject(e);
  }
  return typeof newSource === 'string' || typeof newSource === 'function' ?
    Promise.resolve(newSource) :
    Promise.reject('The loader "' + this.options.template + '" didn\'t return html.');
};

/**
 * Html post processing
 *
 * Returns a promise
 */
HtmlWebpackPlugin.prototype.executeTemplate = function(templateFunction, chunks, assets, compilation) {
  var self = this;
  return Promise.resolve()
    // Template processing
    .then(function() {
      var templateParams = {
        webpack: compilation.getStats().toJson(),
        webpackConfig: compilation.options,
        htmlWebpackPlugin: {
          files: assets,
          options: self.options,
        }
      };
      var html = '';
      try {
        html = templateFunction(templateParams);
      } catch (e) {
        compilation.errors.push(new Error('Template execution failed: ' + e));
        return Promise.reject(e);
      }
      return html;
    });
};

/**
 * Html post processing
 *
 * Returns a promise
 */
HtmlWebpackPlugin.prototype.postProcessHtml = function(html, assets) {
  var self = this;
  return Promise.resolve()
    // Inject
    .then(function() {
      if (self.options.inject) {
        return self.injectAssetsIntoHtml(html, assets);
      } else {
        return html;
      }
    })
    // Minify
    .then(function(html) {
      if (self.options.minify) {
        var minify = require('html-minifier').minify;
        // If `options.minify` is set to true use the default minify options
        var minifyOptions = _.isObject(self.options.minify) ? self.options.minify : {};
        try {
          return minify(html, minifyOptions);
        } catch(e) {
          Promise.reject(e);
        }
      }
      return html;
    });
};

/*
 * Pushes the content of the given filename to the compilation assets
 */
HtmlWebpackPlugin.prototype.addFileToAssets = function(filename, compilation) {
  return Promise.props({
    size: fs.statAsync(filename),
    source: fs.readFileAsync(filename)
  })
  .catch(function() {
    return Promise.reject(new Error('HtmlWebpackPlugin: could not load file ' + filename));
  })
  .then(function(results) {
    var basename = path.basename(filename);
    compilation.fileDependencies.push(filename);
    compilation.assets[basename] = {
      source: function() {
        return results.source;
      },
      size: function() {
        return results.size.size;
      }
    };
    return basename;
  });
};

/**
 * Return all chunks from the compilation result which match the exclude and include filters
 */
HtmlWebpackPlugin.prototype.filterChunks = function (webpackStatsJson, includedChunks, excludedChunks) {
  var chunks = webpackStatsJson.chunks.filter(function(chunk){
    var chunkName = chunk.names[0];
    // This chunk doesn't have a name. This script can't handled it.
    if (chunkName === undefined) {
      return false;
    }
    // Skip if the chunks should be filtered and the given chunk was not added explicity
    if (Array.isArray(includedChunks) && includedChunks.indexOf(chunkName) === -1) {
      return false;
    }
    // Skip if the chunks should be filtered and the given chunk was excluded explicity
    if (Array.isArray(excludedChunks) && excludedChunks.indexOf(chunkName) !== -1) {
      return false;
    }
    // Add otherwise
    return true;
  });
  return chunks.sort(function orderEntryLast(a, b) {
    if (a.entry !== b.entry) {
      return b.entry ? 1 : -1;
    } else {
      return b.id - a.id;
    }
  });
};

HtmlWebpackPlugin.prototype.htmlWebpackPluginAssets = function(compilation, chunks) {
  var self = this;
  var webpackStatsJson = compilation.getStats().toJson();

  // Use the configured public path or build a relative path
  var publicPath = typeof compilation.options.output.publicPath !== 'undefined' ?
      compilation.options.output.publicPath :
      path.relative(path.dirname(self.options.filename), '.');

  if (publicPath.length && publicPath.substr(-1, 1) !== '/') {
    publicPath += '/';
  }

  var assets = {
    // Will contain all js & css files by chunk
    chunks: {},
    // Will contain all js files
    js: [],
    // Will contain all css files
    css: [],
    // Will contain the html5 appcache manifest files if it exists
    manifest: Object.keys(compilation.assets).filter(function(assetFile){
      return path.extname(assetFile) === '.appcache';
    })[0]
  };

  // Append a hash for cache busting
  if (this.options.hash) {
    assets.manifest = self.appendHash(assets.manifest, webpackStatsJson.hash);
    assets.favicon = self.appendHash(assets.favicon, webpackStatsJson.hash);
  }

  for (var i = 0; i < chunks.length; i++) {
    var chunk = chunks[i];
    var chunkName = chunk.names[0];

    assets.chunks[chunkName] = {};

    // Prepend the public path to all chunk files
    var chunkFiles = [].concat(chunk.files).map(function(chunkFile) {
      return publicPath + chunkFile;
    });

    // Append a hash for cache busting
    if (this.options.hash) {
      chunkFiles = chunkFiles.map(function(chunkFile) {
        return self.appendHash(chunkFile, webpackStatsJson.hash);
      });
    }

    // Webpack outputs an array for each chunk when using sourcemaps
    // But we need only the entry file
    var entry = chunkFiles[0];
    assets.chunks[chunkName].size = chunk.size;
    assets.chunks[chunkName].entry = entry;
    assets.js.push(entry);

    // Gather all css files
    var css = chunkFiles.filter(function(chunkFile){
      // Some chunks may contain content hash in their names, for ex. 'main.css?1e7cac4e4d8b52fd5ccd2541146ef03f'.
      // We must proper handle such cases, so we use regexp testing here
      return /^.css($|\?)/.test(path.extname(chunkFile));
    });
    assets.chunks[chunkName].css = css;
    assets.css = assets.css.concat(css);
  }

  // Duplicate css assets can occur on occasion if more than one chunk
  // requires the same css.
  assets.css = _.uniq(assets.css);

  return assets;
};

/**
 * Injects the assets into the given html string
 */
HtmlWebpackPlugin.prototype.injectAssetsIntoHtml = function(html, assets) {
  var chunks = Object.keys(assets.chunks);

  // Gather all css and script files
  var styles = [];
  var scripts = [];
  chunks.forEach(function(chunkName) {
    styles = styles.concat(assets.chunks[chunkName].css);
    scripts.push(assets.chunks[chunkName].entry);
  });
  // Turn script files into script tags
  scripts = scripts.map(function(scriptPath) {
    return '<script src="' + scriptPath + '"></script>';
  });
  // Turn css files into link tags
  styles = styles.map(function(stylePath) {
    return '<link href="' + stylePath + '" rel="stylesheet">';
  });
  // Injections
  var head = [];
  var body = [];

  // If there is a favicon present, add it to the head
  if (assets.favicon) {
    head.push('<link rel="shortcut icon" href="' + assets.favicon + '">');
  }
  // Add styles to the head
  head = head.concat(styles);
  // Add scripts to body or head
  if (this.options.inject === 'head') {
    head = body.concat(scripts);
  } else {
    body = body.concat(scripts);
  }
  // Append assets to head element
  html = html.replace(/(<\/head>)/i, function (match) {
    return head.join('') + match;
  });
  // Append assets to body element
    html = html.replace(/(<\/body>)/i, function (match) {
      return body.join('') + match;
    });
  // Inject manifest into the opening html tag
  if (assets.manifest) {
    html = html.replace(/(<html[^>]*)(>)/i, function (match, start, end) {
      // Append the manifest only if no manifest was specified
      if (/\smanifest\s*=/.test(match)) {
        return match;
      }
      return start + ' manifest="' + assets.manifest + '"' + end;
    });
  }
  return html;
};

/**
 * Appends a cache busting hash
 */
HtmlWebpackPlugin.prototype.appendHash = function (url, hash) {
  if (!url) {
    return url;
  }
  return url + (url.indexOf('?') === -1 ? '?' : '&') + hash;
};

module.exports = HtmlWebpackPlugin;
<|endoftext|>


<commit_before>module.exports = {
  parser: 'babel-eslint',
  env: {
    browser: true,
    node: true,
    es6: true
  },
  ecmaFeatures: {
    modules: true
  },

  globals: {},
  rules: {
    //
    // Possible Errors
    //
    // disallow or enforce trailing commas
    'comma-dangle': 1,
    // disallow assignment in conditional expressions
    'no-cond-assign': 1,
    // disallow use of console in the node environment
    'no-console': 1,
    // disallow use of constant expressions in conditions
    'no-constant-condition': 1,
    // disallow control characters in regular expressions
    'no-control-regex': 1,
    // disallow use of debugger
    'no-debugger': 1,
    // disallow duplicate arguments in functions
    'no-dupe-args': 1,
    // disallow duplicate keys when creating object literals
    'no-dupe-keys': 1,
    // disallow a duplicate case label.
    'no-duplicate-case': 1,
    // disallow the use of empty character classes in regular expressions
    'no-empty-character-class': 1,
    // disallow empty statements
    'no-empty': 1,
    // disallow assigning to the exception in a catch block
    'no-ex-assign': 1,
    // disallow double-negation boolean casts in a boolean context
    'no-extra-boolean-cast': 1,
    // disallow unnecessary parentheses
    'no-extra-parens': 1,
    // disallow unnecessary semicolons
    'no-extra-semi': 1,
    // disallow overwriting functions written as function declarations
    'no-func-assign': 1,
    // disallow function or variable declarations in nested blocks
    'no-inner-declarations': [1, 'both'],
    // disallow invalid regular expression strings in the RegExp constructor
    'no-invalid-regexp': 1,
    // disallow irregular whitespace outside of strings and comments
    'no-irregular-whitespace': 1,
    // disallow negation of the left operand of an in expression
    'no-negated-in-lhs': 1,
    // disallow the use of object properties of the global object (Math and JSON) as functions
    'no-obj-calls': 1,
    // disallow multiple spaces in a regular expression literal
    'no-regex-spaces': 1,
    // disallow sparse arrays
    'no-sparse-arrays': 1,
    // Avoid code that looks like two expressions but is actually one
    'no-unexpected-multiline': 1,
    // disallow unreachable statements after a return, throw, continue, or break statement
    'no-unreachable': 1,
    // disallow comparisons with the value NaN
    'use-isnan': 1,
    // Ensure JSDoc comments are valid
    'valid-jsdoc': 1,
    // Ensure that the results of typeof are compared against a valid string
    'valid-typeof': 1,

    //
    // Best Practices
    //
    // Enforces getter/setter pairs in objects
    'accessor-pairs': 1,
    // treat var statements as if they were block scoped
    'block-scoped-var': 1,
    // specify the maximum cyclomatic complexity allowed in a program
    'complexity': [1, 2],
    // require return statements to either always or never specify values
    'consistent-return': 1,
    // specify curly brace conventions for all control statements
    'curly': [1, 'multi-line'],
    // require default case in switch statements
    'default-case': 1,
    // enforces consistent newlines before or after dots
    'dot-location': [1, 'property'],
    // encourages use of dot notation whenever possible
    'dot-notation': 1,
    // require the use of === and !==
    'eqeqeq': 1,
    // make sure for-in loops have an if statement
    'guard-for-in': 1,
    // disallow the use of alert, confirm, and prompt
    'no-alert': 1,
    // disallow use of arguments.caller or arguments.callee
    'no-caller': 1,
    // disallow lexical declarations in case clauses
    'no-case-declarations': 1,
    // disallow division operators explicitly at beginning of regular expression
    'no-div-regex': 1,
    // disallow else after a return in an if
    'no-else-return': 1,
    // disallow use of labels for anything other than loops and switches
    'no-empty-label': 1,
    // disallow use of empty destructuring patterns
    'no-empty-pattern': 1,
    // disallow comparisons to null without a type-checking operator
    'no-eq-null': 1,
    // disallow use of eval()
    'no-eval': 1,
    // disallow adding to native types
    'no-extend-native': 1,
    // disallow unnecessary function binding
    'no-extra-bind': 1,
    // disallow fallthrough of case statements
    'no-fallthrough': 1,
    // disallow the use of leading or trailing decimal points in numeric literals
    'no-floating-decimal': 1,
    // disallow the type conversions with shorter notations
    'no-implicit-coercion': 1,
    // disallow use of eval()-like methods
    'no-implied-eval': 1,
    // disallow this keywords outside of classes or class-like objects
    'no-invalid-this': 1,
    // disallow usage of __iterator__ property
    'no-iterator': 1,
    // disallow use of labeled statements
    'no-labels': 1,
    // disallow unnecessary nested blocks
    'no-lone-blocks': 1,
    // disallow creation of functions within loops
    'no-loop-func': 1,
    // disallow the use of magic numbers
    'no-magic-numbers': 0,
    // disallow use of multiple spaces
    'no-multi-spaces': 1,
    // disallow use of multiline strings
    'no-multi-str': 1,
    // disallow reassignments of native objects
    'no-native-reassign': 1,
    // disallow use of new operator for Function object
    'no-new-func': 1,
    // disallows creating new instances of String,Number, and Boolean
    'no-new-wrappers': 1,
    // disallow use of the new operator when not part of an assignment or comparison
    'no-new': 1,
    // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251";
    'no-octal-escape': 1,
    // disallow use of octal literals
    'no-octal': 1,
    // disallow reassignment of function parameters
    'no-param-reassign': 1,
    // disallow use of process.env
    'no-process-env': 1,
    // disallow usage of __proto__ property
    'no-proto': 1,
    // disallow declaring the same variable more than once
    'no-redeclare': 1,
    // disallow use of assignment in return statement
    'no-return-assign': 1,
    // disallow use of javascript: urls.
    'no-script-url': 1,
    // disallow comparisons where both sides are exactly the same
    'no-self-compare': 1,
    // disallow use of the comma operator
    'no-sequences': 1,
    // restrict what can be thrown as an exception
    'no-throw-literal': 1,
    // disallow usage of expressions in statement position
    'no-unused-expressions': 1,
    // disallow unnecessary .call() and .apply()
    'no-useless-call': 1,
    // disallow unnecessary concatenation of literals or template literals
    'no-useless-concat': 1,
    // disallow use of the void operator
    'no-void': 1,
    // disallow usage of configurable warning terms in comments - e.g. TODO or FIXME
    'no-warning-comments': 1,
    // disallow use of the with statement
    'no-with': 1,
    // require use of the second argument for parseInt()
    'radix': 1,
    // require declaration of all vars at the top of their containing scope
    'vars-on-top': 1,
    // require immediate function invocation to be wrapped in parentheses
    'wrap-iife': 1,
    // require or disallow Yoda conditions
    'yoda': 1
  }
};
<commit_msg>Add rule for strict mode<commit_after>module.exports = {
  parser: 'babel-eslint',
  env: {
    browser: true,
    node: true,
    es6: true
  },
  ecmaFeatures: {
    modules: true
  },

  globals: {},
  rules: {
    //
    // Possible Errors
    //
    // disallow or enforce trailing commas
    'comma-dangle': 1,
    // disallow assignment in conditional expressions
    'no-cond-assign': 1,
    // disallow use of console in the node environment
    'no-console': 1,
    // disallow use of constant expressions in conditions
    'no-constant-condition': 1,
    // disallow control characters in regular expressions
    'no-control-regex': 1,
    // disallow use of debugger
    'no-debugger': 1,
    // disallow duplicate arguments in functions
    'no-dupe-args': 1,
    // disallow duplicate keys when creating object literals
    'no-dupe-keys': 1,
    // disallow a duplicate case label.
    'no-duplicate-case': 1,
    // disallow the use of empty character classes in regular expressions
    'no-empty-character-class': 1,
    // disallow empty statements
    'no-empty': 1,
    // disallow assigning to the exception in a catch block
    'no-ex-assign': 1,
    // disallow double-negation boolean casts in a boolean context
    'no-extra-boolean-cast': 1,
    // disallow unnecessary parentheses
    'no-extra-parens': 1,
    // disallow unnecessary semicolons
    'no-extra-semi': 1,
    // disallow overwriting functions written as function declarations
    'no-func-assign': 1,
    // disallow function or variable declarations in nested blocks
    'no-inner-declarations': [1, 'both'],
    // disallow invalid regular expression strings in the RegExp constructor
    'no-invalid-regexp': 1,
    // disallow irregular whitespace outside of strings and comments
    'no-irregular-whitespace': 1,
    // disallow negation of the left operand of an in expression
    'no-negated-in-lhs': 1,
    // disallow the use of object properties of the global object (Math and JSON) as functions
    'no-obj-calls': 1,
    // disallow multiple spaces in a regular expression literal
    'no-regex-spaces': 1,
    // disallow sparse arrays
    'no-sparse-arrays': 1,
    // Avoid code that looks like two expressions but is actually one
    'no-unexpected-multiline': 1,
    // disallow unreachable statements after a return, throw, continue, or break statement
    'no-unreachable': 1,
    // disallow comparisons with the value NaN
    'use-isnan': 1,
    // Ensure JSDoc comments are valid
    'valid-jsdoc': 1,
    // Ensure that the results of typeof are compared against a valid string
    'valid-typeof': 1,

    //
    // Best Practices
    //
    // Enforces getter/setter pairs in objects
    'accessor-pairs': 1,
    // treat var statements as if they were block scoped
    'block-scoped-var': 1,
    // specify the maximum cyclomatic complexity allowed in a program
    'complexity': [1, 2],
    // require return statements to either always or never specify values
    'consistent-return': 1,
    // specify curly brace conventions for all control statements
    'curly': [1, 'multi-line'],
    // require default case in switch statements
    'default-case': 1,
    // enforces consistent newlines before or after dots
    'dot-location': [1, 'property'],
    // encourages use of dot notation whenever possible
    'dot-notation': 1,
    // require the use of === and !==
    'eqeqeq': 1,
    // make sure for-in loops have an if statement
    'guard-for-in': 1,
    // disallow the use of alert, confirm, and prompt
    'no-alert': 1,
    // disallow use of arguments.caller or arguments.callee
    'no-caller': 1,
    // disallow lexical declarations in case clauses
    'no-case-declarations': 1,
    // disallow division operators explicitly at beginning of regular expression
    'no-div-regex': 1,
    // disallow else after a return in an if
    'no-else-return': 1,
    // disallow use of labels for anything other than loops and switches
    'no-empty-label': 1,
    // disallow use of empty destructuring patterns
    'no-empty-pattern': 1,
    // disallow comparisons to null without a type-checking operator
    'no-eq-null': 1,
    // disallow use of eval()
    'no-eval': 1,
    // disallow adding to native types
    'no-extend-native': 1,
    // disallow unnecessary function binding
    'no-extra-bind': 1,
    // disallow fallthrough of case statements
    'no-fallthrough': 1,
    // disallow the use of leading or trailing decimal points in numeric literals
    'no-floating-decimal': 1,
    // disallow the type conversions with shorter notations
    'no-implicit-coercion': 1,
    // disallow use of eval()-like methods
    'no-implied-eval': 1,
    // disallow this keywords outside of classes or class-like objects
    'no-invalid-this': 1,
    // disallow usage of __iterator__ property
    'no-iterator': 1,
    // disallow use of labeled statements
    'no-labels': 1,
    // disallow unnecessary nested blocks
    'no-lone-blocks': 1,
    // disallow creation of functions within loops
    'no-loop-func': 1,
    // disallow the use of magic numbers
    'no-magic-numbers': 0,
    // disallow use of multiple spaces
    'no-multi-spaces': 1,
    // disallow use of multiline strings
    'no-multi-str': 1,
    // disallow reassignments of native objects
    'no-native-reassign': 1,
    // disallow use of new operator for Function object
    'no-new-func': 1,
    // disallows creating new instances of String,Number, and Boolean
    'no-new-wrappers': 1,
    // disallow use of the new operator when not part of an assignment or comparison
    'no-new': 1,
    // disallow use of octal escape sequences in string literals, such as var foo = "Copyright \251";
    'no-octal-escape': 1,
    // disallow use of octal literals
    'no-octal': 1,
    // disallow reassignment of function parameters
    'no-param-reassign': 1,
    // disallow use of process.env
    'no-process-env': 1,
    // disallow usage of __proto__ property
    'no-proto': 1,
    // disallow declaring the same variable more than once
    'no-redeclare': 1,
    // disallow use of assignment in return statement
    'no-return-assign': 1,
    // disallow use of javascript: urls.
    'no-script-url': 1,
    // disallow comparisons where both sides are exactly the same
    'no-self-compare': 1,
    // disallow use of the comma operator
    'no-sequences': 1,
    // restrict what can be thrown as an exception
    'no-throw-literal': 1,
    // disallow usage of expressions in statement position
    'no-unused-expressions': 1,
    // disallow unnecessary .call() and .apply()
    'no-useless-call': 1,
    // disallow unnecessary concatenation of literals or template literals
    'no-useless-concat': 1,
    // disallow use of the void operator
    'no-void': 1,
    // disallow usage of configurable warning terms in comments - e.g. TODO or FIXME
    'no-warning-comments': 1,
    // disallow use of the with statement
    'no-with': 1,
    // require use of the second argument for parseInt()
    'radix': 1,
    // require declaration of all vars at the top of their containing scope
    'vars-on-top': 1,
    // require immediate function invocation to be wrapped in parentheses
    'wrap-iife': 1,
    // require or disallow Yoda conditions
    'yoda': 1,

    //
    // Strict Mode
    //
    // controls location of Use Strict Directives
    'strict': [1, 'never']
  }
};
<|endoftext|>


<commit_before>'use strict'

var filter = require('lodash.filter')
var map = require('lodash.map')
var forEach = require('lodash.foreach')

var keyPrefix = 'reduxPersistStore:'

function persistStore(store, config, cb){
  //defaults
  config = config || {}
  var blacklist = config.blacklist || []
  var actionCreator = config.actionCreator || defaultActionCreator
  var storage = config.storage || defaultStorage

  //initialize values
  let timeIterator = null
  let lastState = store.getState()

  //rehydrate
  let restoreCount = 0
  let completionCount = 0
  forEach(lastState, function(s, key){
    if(blacklist.indexOf(key) !== -1){ return }
    restoreCount += 1
    setImmediate(function(){
      rehydrate(key, function(){
        completionCount += 1
        if(completionCount === restoreCount){
          cb && cb()
        }
      })
    })
  })

  //store state to disk
  var unsub = store.subscribe(function(){
    //Clear unfinished timeIterator if exists
    if(timeIterator !== null){
      clearInterval(timeIterator)
    }

    let state = store.getState()
    let storesToProcess = filter(map(state, function(subState, key){
      if(blacklist.indexOf(key) !== -1){ return }
      //only store keys that have changed
      return lastState[key] !== state[key] ? key : false
    }))

    //time iterator runs every 33ms (30fps)
    let i = 0
    timeIterator = setInterval(function(){
      if(i === storesToProcess.length){
        clearInterval(timeIterator)
        return
      }
      storage.setItem(createStorageKey(storesToProcess[i]), JSON.stringify(state[storesToProcess[i]]), warnIfSetError)
      i += 1
    }, 33)

    lastState = state
  })

  function rehydrate(key, cb){
    storage.getItem(createStorageKey(key), function(err, serialized){
      try{
        if(err){ throw err }
        let data = JSON.parse(serialized)
        store.dispatch(actionCreator(key, data))
      }
      catch(e){
        console.warn('Error restoring data for key:', key, e)
        storage.removeItem(key, warnIfRemoveError)
      }
      cb()
    })
  }

  return {
    purge: function(keys){
      forEach(keys, function(key){
        storage.removeItem(createStorageKey(key), warnIfError)
      })
    },
    purgeAll: function(keys){
      storage.getAllKeys(function(err, keys){
        forEach(keys, function(key){
          if(key.indexOf(keyPrefix) === 0){
            storage.removeItem(key, warnIfError)
          }
        })
      })
    }
  }
}

function warnIfRemoveError(err){
  if(err){ console.warn('Error removing data for key:', key, err) }
}

function warnIfSetError(err){
  if(err){ console.warn('Error storing data for key:', key, err) }
}

function createStorageKey(key){
  return keyPrefix+key
}

function defaultActionCreator(key, data){
  return {
    type: 'REHYDRATE',
    reducer: key,
    data: data,
  }
}

var defaultStorage = {
  getItem: function(key, cb){
    try{
      var s = localStorage.getItem(key)
      cb(null, s)
    }
    catch(e){
      cb(e)
    }
  },
  setItem: function(key, string, cb){
    try{
      localStorage.setItem(key, string)
      cb(null)
    }
    catch(e){
      cb(e)
    }
  },
  removeItem: function(key, cb){
    try{
      localStorage.removeItem(key)
      cb(null)
    }
    catch(e){
      cb(e)
    }
  },
  getAllKeys: function(cb){
    let keys = map(localStorage, function(value, key){ return key })
    return keys
  }
}

module.exports = persistStore
<commit_msg>fix getAllKeys implementation<commit_after>'use strict'

var filter = require('lodash.filter')
var map = require('lodash.map')
var forEach = require('lodash.foreach')

var keyPrefix = 'reduxPersistStore:'

function persistStore(store, config, cb){
  //defaults
  config = config || {}
  var blacklist = config.blacklist || []
  var actionCreator = config.actionCreator || defaultActionCreator
  var storage = config.storage || defaultStorage

  //initialize values
  let timeIterator = null
  let lastState = store.getState()

  //rehydrate
  let restoreCount = 0
  let completionCount = 0
  forEach(lastState, function(s, key){
    if(blacklist.indexOf(key) !== -1){ return }
    restoreCount += 1
    setImmediate(function(){
      rehydrate(key, function(){
        completionCount += 1
        if(completionCount === restoreCount){
          cb && cb()
        }
      })
    })
  })

  //store state to disk
  var unsub = store.subscribe(function(){
    //Clear unfinished timeIterator if exists
    if(timeIterator !== null){
      clearInterval(timeIterator)
    }

    let state = store.getState()
    let storesToProcess = filter(map(state, function(subState, key){
      if(blacklist.indexOf(key) !== -1){ return }
      //only store keys that have changed
      return lastState[key] !== state[key] ? key : false
    }))

    //time iterator runs every 33ms (30fps)
    let i = 0
    timeIterator = setInterval(function(){
      if(i === storesToProcess.length){
        clearInterval(timeIterator)
        return
      }
      storage.setItem(createStorageKey(storesToProcess[i]), JSON.stringify(state[storesToProcess[i]]), warnIfSetError)
      i += 1
    }, 33)

    lastState = state
  })

  function rehydrate(key, cb){
    storage.getItem(createStorageKey(key), function(err, serialized){
      try{
        if(err){ throw err }
        let data = JSON.parse(serialized)
        store.dispatch(actionCreator(key, data))
      }
      catch(e){
        console.warn('Error restoring data for key:', key, e)
        storage.removeItem(key, warnIfRemoveError)
      }
      cb()
    })
  }

  return {
    purge: function(keys){
      forEach(keys, function(key){
        storage.removeItem(createStorageKey(key), warnIfRemoveError)
      })
    },
    purgeAll: function(){
      storage.getAllKeys(function(err, keys){
        forEach(keys, function(key){
          if(key.indexOf(keyPrefix) === 0){
            storage.removeItem(key, warnIfRemoveError)
          }
        })
      })
    }
  }
}

function warnIfRemoveError(err){
  if(err){ console.warn('Error removing data for key:', key, err) }
}

function warnIfSetError(err){
  if(err){ console.warn('Error storing data for key:', key, err) }
}

function createStorageKey(key){
  return keyPrefix+key
}

function defaultActionCreator(key, data){
  return {
    type: 'REHYDRATE',
    reducer: key,
    data: data,
  }
}

var defaultStorage = {
  getItem: function(key, cb){
    try{
      var s = localStorage.getItem(key)
      cb(null, s)
    }
    catch(e){
      cb(e)
    }
  },
  setItem: function(key, string, cb){
    try{
      localStorage.setItem(key, string)
      cb(null)
    }
    catch(e){
      cb(e)
    }
  },
  removeItem: function(key, cb){
    try{
      localStorage.removeItem(key)
      cb(null)
    }
    catch(e){
      cb(e)
    }
  },
  getAllKeys: function(cb){
    try{
      var keys = []
      for ( var i = 0, len = localStorage.length; i < len; ++i ) {
        keys.push(localStorage.key(i))
      }
      cb(null, keys)
    }
    catch(e){
      cb(err)
    }
  }
}

module.exports = persistStore
<|endoftext|>