<commit_before>var path = require('path');
var fs = require('fs');
var builder = require('xmlbuilder');

var TRXReporter = function (baseReporterDecorator, config, emitter, logger, helper, formatError) {
    var outputFile = config.outputFile;
    var log = logger.create('reporter.trx');
    var hostName = require('os').hostname();
    var testRun;
    var resultSummary;
    var counters;
    var testDefinitions;
    var testListIdNotInAList;
    var testEntries;
    var results;
    var times;

    var getTimestamp = function () {
        // todo: use local time ?
        return (new Date()).toISOString().substr(0, 19);
    }

    var s4 = function () {
        return Math.floor((1 + Math.random()) * 0x10000)
            .toString(16)
            .substring(1);
    };

    var newGuid = function () {
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    };

    var formatDuration = function (duration) {
        duration = duration | 0;
        var ms = duration % 1000;
        duration -= ms;
        var s = (duration / 1000) % 60;
        duration -= s * 1000;
        var m = (duration / 60000) % 60;
        duration -= m * 60000;
        var h = (duration / 3600000) % 24;
        duration -= h * 3600000;
        var d = duration / 86400000;

        return (d > 0 ? d + '.' : '') +
            (h < 10 ? '0' + h : h) + ':' +
            (m < 10 ? '0' + m : m) + ':' +
            (s < 10 ? '0' + s : s) + '.' +
            (ms < 10 ? '00' + ms : ms < 100 ? '0' + ms : ms);
    };

    baseReporterDecorator(this);

    this.onRunStart = function () {
        var userName = process.env['USERNAME'];
        var runStartTimestamp = getTimestamp();
        testRun = builder.create("TestRun", {version: '1.0', encoding: 'UTF-8'})
            .att('id', newGuid())
            .att('name', userName + '@' + hostName + ' ' + runStartTimestamp)
            .att('runUser', userName)
            .att('xmlns', 'http://microsoft.com/schemas/VisualStudio/TeamTest/2010');

        testRun.ele('TestSettings')
            .att('name', 'Karma Test Run')
            .att('id', newGuid());

        times = testRun.ele('Times')
        times.att('creation', runStartTimestamp)
        times.att('queuing', runStartTimestamp)
        times.att('start', runStartTimestamp);

        resultSummary = testRun.ele('ResultSummary');
        counters = resultSummary.ele('Counters');
        testDefinitions = testRun.ele('TestDefinitions');

        testListIdNotInAList = "8c84fa94-04c1-424b-9868-57a2d4851a1d";
        var testLists = testRun.ele('TestLists');

        testLists.ele('TestList')
            .att('name', 'Results Not in a List')
            .att('id', testListIdNotInAList);

        // seems to be VS is expecting that exact id
        testLists.ele('TestList')
            .att('name', 'All Loaded Results')
            .att('id', "19431567-8539-422a-85d7-44ee4e166bda");

        testEntries = testRun.ele('TestEntries');
        results = testRun.ele('Results');
    };

    this.onBrowserStart = function(browser) {
    };

    this.onBrowserComplete = function (browser) {
        var result = browser.lastResult;

        var passed = result.failed <= 0 && !result.error;
        resultSummary.att('outcome', passed ? 'Passed' : 'Failed');

        // todo: checkout if all theses numbers map well
        counters.att('total', result.total)
            .att('executed', result.total)
            .att('passed', result.total - result.failed)
            .att('error', result.error ? 1 : 0)
            .att('failed', result.failed);

        // possible useful info:
        // todo: result.disconnected => this seems to happen occasionally? => Possibly handle it!
        // (result.netTime || 0) / 1000)
    };

    this.onRunComplete = function () {
        times.att('finish', getTimestamp());
        var xmlToOutput = testRun;

        helper.mkdirIfNotExists(path.dirname(outputFile), function () {
            fs.writeFile(outputFile, xmlToOutput.end({pretty: true}), function (err) {
                if (err) {
                    log.warn('Cannot write TRX testRun\n\t' + err.message);
                } else {
                    log.debug('TRX results written to "%s".', outputFile);
                }
            });
        });
    };

    this.specSuccess = this.specSkipped = this.specFailure = function (browser, result) {
        var unitTestId = newGuid();
        var unitTestName = browser.name + '_' + result.description;
        var className = result.suite.join('.');
        var codeBase = className + '.' + unitTestName;

        var unitTest = testDefinitions.ele('UnitTest')
            .att('name', unitTestName)
            .att('id', unitTestId);
        var executionId = newGuid();
        unitTest.ele('Execution')
            .att('id', executionId);
        unitTest.ele('TestMethod')
            .att('codeBase', codeBase)
            .att('name', unitTestName)
            .att('className', className);

        testEntries.ele('TestEntry')
            .att('testId', unitTestId)
            .att('executionId', executionId)
            .att('testListId', testListIdNotInAList);

        var unitTestResult = results.ele('UnitTestResult')
            .att('executionId', executionId)
            .att('testId', unitTestId)
            .att('testName', unitTestName)
            .att('computerName', hostName)
            .att('duration', formatDuration(result.time || 0))
            .att('startTime', getTimestamp())
            .att('endTime', getTimestamp())
            // todo: are there other test types?
            .att('testType', '13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b') // that guid seems to represent 'unit test'
            // todo: possibly write result.skipped also => Check out how this could happen.
            .att('outcome', result.success ? 'Passed' : 'Failed')
            .att('testListId', testListIdNotInAList);

        if (!result.success) {
            unitTestResult.ele('Output')
                .ele('ErrorInfo')
                .ele('Message', formatError(result.log[0]))
        }
    };
};

TRXReporter.$inject = ['baseReporterDecorator', 'config.trxReporter', 'emitter', 'logger',
    'helper', 'formatError'];

// PUBLISH DI MODULE
module.exports = {
    'reporter:trx': ['type', TRXReporter]
};
<commit_msg>Handled skipped tests<commit_after>var path = require('path');
var fs = require('fs');
var builder = require('xmlbuilder');

var TRXReporter = function (baseReporterDecorator, config, emitter, logger, helper, formatError) {
    var outputFile = config.outputFile;
    var log = logger.create('reporter.trx');
    var hostName = require('os').hostname();
    var testRun;
    var resultSummary;
    var counters;
    var testDefinitions;
    var testListIdNotInAList;
    var testEntries;
    var results;
    var times;

    var getTimestamp = function () {
        // todo: use local time ?
        return (new Date()).toISOString().substr(0, 19);
    }

    var s4 = function () {
        return Math.floor((1 + Math.random()) * 0x10000)
            .toString(16)
            .substring(1);
    };

    var newGuid = function () {
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    };

    var formatDuration = function (duration) {
        duration = duration | 0;
        var ms = duration % 1000;
        duration -= ms;
        var s = (duration / 1000) % 60;
        duration -= s * 1000;
        var m = (duration / 60000) % 60;
        duration -= m * 60000;
        var h = (duration / 3600000) % 24;
        duration -= h * 3600000;
        var d = duration / 86400000;

        return (d > 0 ? d + '.' : '') +
            (h < 10 ? '0' + h : h) + ':' +
            (m < 10 ? '0' + m : m) + ':' +
            (s < 10 ? '0' + s : s) + '.' +
            (ms < 10 ? '00' + ms : ms < 100 ? '0' + ms : ms);
    };

    baseReporterDecorator(this);

    this.onRunStart = function () {
        var userName = process.env['USERNAME'];
        var runStartTimestamp = getTimestamp();
        testRun = builder.create("TestRun", {version: '1.0', encoding: 'UTF-8'})
            .att('id', newGuid())
            .att('name', userName + '@' + hostName + ' ' + runStartTimestamp)
            .att('runUser', userName)
            .att('xmlns', 'http://microsoft.com/schemas/VisualStudio/TeamTest/2010');

        testRun.ele('TestSettings')
            .att('name', 'Karma Test Run')
            .att('id', newGuid());

        times = testRun.ele('Times')
        times.att('creation', runStartTimestamp)
        times.att('queuing', runStartTimestamp)
        times.att('start', runStartTimestamp);

        resultSummary = testRun.ele('ResultSummary');
        counters = resultSummary.ele('Counters');
        testDefinitions = testRun.ele('TestDefinitions');

        testListIdNotInAList = "8c84fa94-04c1-424b-9868-57a2d4851a1d";
        var testLists = testRun.ele('TestLists');

        testLists.ele('TestList')
            .att('name', 'Results Not in a List')
            .att('id', testListIdNotInAList);

        // seems to be VS is expecting that exact id
        testLists.ele('TestList')
            .att('name', 'All Loaded Results')
            .att('id', "19431567-8539-422a-85d7-44ee4e166bda");

        testEntries = testRun.ele('TestEntries');
        results = testRun.ele('Results');
    };

    this.onBrowserStart = function(browser) {
    };

    this.onBrowserComplete = function (browser) {
        var result = browser.lastResult;

        var passed = result.failed <= 0 && !result.error;
        resultSummary.att('outcome', passed ? 'Passed' : 'Failed');

        // todo: checkout if all theses numbers map well
        counters.att('total', result.total)
            .att('executed', result.total - result.skipped)
            .att('passed', result.success)
            .att('error', result.error ? 1 : 0)
            .att('failed', result.failed);

        // possible useful info:
        // todo: result.disconnected => this seems to happen occasionally? => Possibly handle it!
        // (result.netTime || 0) / 1000)
    };

    this.onRunComplete = function () {
        times.att('finish', getTimestamp());
        var xmlToOutput = testRun;

        helper.mkdirIfNotExists(path.dirname(outputFile), function () {
            fs.writeFile(outputFile, xmlToOutput.end({pretty: true}), function (err) {
                if (err) {
                    log.warn('Cannot write TRX testRun\n\t' + err.message);
                } else {
                    log.debug('TRX results written to "%s".', outputFile);
                }
            });
        });
    };

    this.specSuccess = this.specSkipped = this.specFailure = function (browser, result) {
        var unitTestId = newGuid();
        var unitTestName = browser.name + '_' + result.description;
        var className = result.suite.join('.');
        var codeBase = className + '.' + unitTestName;

        var unitTest = testDefinitions.ele('UnitTest')
            .att('name', unitTestName)
            .att('id', unitTestId);
        var executionId = newGuid();
        unitTest.ele('Execution')
            .att('id', executionId);
        unitTest.ele('TestMethod')
            .att('codeBase', codeBase)
            .att('name', unitTestName)
            .att('className', className);

        testEntries.ele('TestEntry')
            .att('testId', unitTestId)
            .att('executionId', executionId)
            .att('testListId', testListIdNotInAList);

        var unitTestResult = results.ele('UnitTestResult')
            .att('executionId', executionId)
            .att('testId', unitTestId)
            .att('testName', unitTestName)
            .att('computerName', hostName)
            .att('duration', formatDuration(result.time || 0))
            // todo: are there other test types?
            .att('testType', '13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b') // that guid seems to represent 'unit test'
            .att('outcome', result.skipped ? 'NotExecuted' : (result.success ? 'Passed' : 'Failed'))
            .att('testListId', testListIdNotInAList);

        if (!result.success) {
            unitTestResult.ele('Output')
                .ele('ErrorInfo')
                .ele('Message', formatError(result.log[0]))
        }
    };
};

TRXReporter.$inject = ['baseReporterDecorator', 'config.trxReporter', 'emitter', 'logger',
    'helper', 'formatError'];

// PUBLISH DI MODULE
module.exports = {
    'reporter:trx': ['type', TRXReporter]
};
<|endoftext|>


<commit_before>var UserAgentParser = require('ua-parser-js');
var languageMessages = require('./languages.json');

var DEFAULTS = {
  'Chrome': 57, // Includes Chrome for mobile devices
  'Edge': 39,
  'Safari': 10,
  'Mobile Safari': 10,
  'Firefox': 50,
  'IE': false
};

var EDGEHTML_VS_EDGE_VERSIONS = {
  12: 0.1,
  13: 21,
  14: 31,
  15: 39,
  16: 41
};

module.exports = function (options) {

  var main = function () {

    // Despite the docs, UA needs to be provided to constructor explicitly:
    // https://github.com/faisalman/ua-parser-js/issues/90
    var parsedUserAgent = new UserAgentParser(window.navigator.userAgent).getResult();

    // Variable definition (before ajax)
    var outdatedUI = document.getElementById('outdated');

    options = options || {};

    var browserLocale = window.navigator.language || window.navigator.userLanguage; // Everyone else, IE

    // Set default options
    var browserSupport = options.browserSupport || DEFAULTS;
    // CSS property to check for. You may also like 'borderSpacing', 'boxShadow', 'transform', 'borderImage';
    var	requiredCssProperty = options.requiredCssProperty || false;
    var	backgroundColor = options.backgroundColor || '#f25648'; // Salmon
    var	textColor = options.textColor || 'white';
    var	language = options.language || browserLocale.slice(0, 2); // Language code

    var updateSource = 'web'; // Other possible values are 'googlePlay' or 'appStore'. Determines where we tell users to go for upgrades.

    // Chrome mobile is still Chrome (unlike Safari which is 'Mobile Safari')
    var isAndroid = parsedUserAgent.os.name === 'Android';
    if (isAndroid) {
      updateSource = 'googlePlay';
    }

    var isAndroidButNotChrome;
    if (options.requireChromeOnAndroid) {
      isAndroidButNotChrome = (isAndroid) && (parsedUserAgent.browser.name !== 'Chrome');
    }

    if (parsedUserAgent.os.name === 'iOS') {
      updateSource = 'appStore';
    }

    var done = true;

    var changeOpacity = function (opacityValue) {
      outdatedUI.style.opacity = opacityValue / 100;
      outdatedUI.style.filter = 'alpha(opacity=' + opacityValue + ')';
    };

    var fadeIn = function (opacityValue) {
      changeOpacity(opacityValue);
      if (opacityValue === 1) {
        outdatedUI.style.display = 'block';
      }
      if (opacityValue === 100) {
        done = true;
      }
    };

    var isBrowserOutOfDate = function () {
      var browserName = parsedUserAgent.browser.name;
      var browserMajorVersion = parsedUserAgent.browser.major;
      if ( browserName === 'Edge' ) {
        browserMajorVersion = EDGEHTML_VS_EDGE_VERSIONS[browserMajorVersion]
      }
      var isOutOfDate = false;
      if ( ! browserSupport[browserName] ) {
        isOutOfDate = true
      } else if (browserMajorVersion < browserSupport[browserName]) {
        isOutOfDate = true;
      }
      return isOutOfDate;
    };

    // Returns true if a browser supports a css3 property
    var isPropertySupported = function (prop) {
      if (!prop) {
        return true;
      }
      var div = document.createElement('div');
      var vendorPrefixes = ['khtml', 'ms', 'o', 'moz', 'webkit'];
      var count = vendorPrefixes.length;

      if (div.style[prop]) {
        return true;
      }

      prop = prop.replace(/^[a-z]/, function (val) {
        return val.toUpperCase();
      });

      while (count--) {
        if (div.style[vendorPrefixes[count] + prop]) {
          return true;
        }
      }
      return false;
    };

    var makeFadeInFunction = function(opacityValue) {
      return function () {
        fadeIn(opacityValue);
      };
    };

    // Style element explicitly - TODO: investigate and delete if not needed
    var startStylesAndEvents = function () {
      var buttonClose = document.getElementById('buttonCloseUpdateBrowser');
      var buttonUpdate = document.getElementById('buttonUpdateBrowser');

      //check settings attributes
      outdatedUI.style.backgroundColor = backgroundColor;
      //way too hard to put !important on IE6
      outdatedUI.style.color = textColor;
      outdatedUI.children[0].style.color = textColor;
      outdatedUI.children[1].style.color = textColor;

      // Update button is desktop only
      if (buttonUpdate) {
        buttonUpdate.style.color = textColor;
        if (buttonUpdate.style.borderColor) {
          buttonUpdate.style.borderColor = textColor;
        }

        // Override the update button color to match the background color
        buttonUpdate.onmouseover = function () {
          this.style.color = backgroundColor;
          this.style.backgroundColor = textColor;
        };

        buttonUpdate.onmouseout = function () {
          this.style.color = textColor;
          this.style.backgroundColor = backgroundColor;
        };
      }

      buttonClose.style.color = textColor;

      buttonClose.onmousedown = function () {
        outdatedUI.style.display = 'none';
        return false;
      };
    };

    var getmessage = function (lang) {
      var messages = languageMessages[lang] || languageMessages.en;

      var updateMessages = {
        'web': '
' + messages.update.web + '' + messages.callToAction + '
',
        'googlePlay': '
' + messages.update.googlePlay +
        '' + messages.callToAction + '
',
        'appStore': '
' + messages.update[updateSource] + '
'
      };

      var updateMessage = updateMessages[updateSource];

      return '
' + messages.outOfDate + '
' + updateMessage +
        '
×
';
    };

    // Check if browser is supported
    if (isBrowserOutOfDate() || ! isPropertySupported(requiredCssProperty) || isAndroidButNotChrome) {

      // This is an outdated browser
      if (done && outdatedUI.style.opacity !== '1') {
        done = false;

        for (var opacity = 1; opacity <= 100; opacity++) {
          setTimeout(makeFadeInFunction(opacity), opacity * 8);
        }
      }

      var insertContentHere = document.getElementById('outdated');
      insertContentHere.innerHTML = getmessage(language);
      startStylesAndEvents();
    }
  };

  // Load main when DOM ready.
  var oldOnload = window.onload;
  if (typeof window.onload !== 'function') {
    window.onload = main;
  }
  else {
    window.onload = function () {
      if (oldOnload) {
        oldOnload();
      }
      main();
    };
  }
};
<commit_msg>add rel="nofollow" to the outbound links<commit_after>var UserAgentParser = require('ua-parser-js');
var languageMessages = require('./languages.json');

var DEFAULTS = {
  'Chrome': 57, // Includes Chrome for mobile devices
  'Edge': 39,
  'Safari': 10,
  'Mobile Safari': 10,
  'Firefox': 50,
  'IE': false
};

var EDGEHTML_VS_EDGE_VERSIONS = {
  12: 0.1,
  13: 21,
  14: 31,
  15: 39,
  16: 41
};

module.exports = function (options) {

  var main = function () {

    // Despite the docs, UA needs to be provided to constructor explicitly:
    // https://github.com/faisalman/ua-parser-js/issues/90
    var parsedUserAgent = new UserAgentParser(window.navigator.userAgent).getResult();

    // Variable definition (before ajax)
    var outdatedUI = document.getElementById('outdated');

    options = options || {};

    var browserLocale = window.navigator.language || window.navigator.userLanguage; // Everyone else, IE

    // Set default options
    var browserSupport = options.browserSupport || DEFAULTS;
    // CSS property to check for. You may also like 'borderSpacing', 'boxShadow', 'transform', 'borderImage';
    var	requiredCssProperty = options.requiredCssProperty || false;
    var	backgroundColor = options.backgroundColor || '#f25648'; // Salmon
    var	textColor = options.textColor || 'white';
    var	language = options.language || browserLocale.slice(0, 2); // Language code

    var updateSource = 'web'; // Other possible values are 'googlePlay' or 'appStore'. Determines where we tell users to go for upgrades.

    // Chrome mobile is still Chrome (unlike Safari which is 'Mobile Safari')
    var isAndroid = parsedUserAgent.os.name === 'Android';
    if (isAndroid) {
      updateSource = 'googlePlay';
    }

    var isAndroidButNotChrome;
    if (options.requireChromeOnAndroid) {
      isAndroidButNotChrome = (isAndroid) && (parsedUserAgent.browser.name !== 'Chrome');
    }

    if (parsedUserAgent.os.name === 'iOS') {
      updateSource = 'appStore';
    }

    var done = true;

    var changeOpacity = function (opacityValue) {
      outdatedUI.style.opacity = opacityValue / 100;
      outdatedUI.style.filter = 'alpha(opacity=' + opacityValue + ')';
    };

    var fadeIn = function (opacityValue) {
      changeOpacity(opacityValue);
      if (opacityValue === 1) {
        outdatedUI.style.display = 'block';
      }
      if (opacityValue === 100) {
        done = true;
      }
    };

    var isBrowserOutOfDate = function () {
      var browserName = parsedUserAgent.browser.name;
      var browserMajorVersion = parsedUserAgent.browser.major;
      if ( browserName === 'Edge' ) {
        browserMajorVersion = EDGEHTML_VS_EDGE_VERSIONS[browserMajorVersion]
      }
      var isOutOfDate = false;
      if ( ! browserSupport[browserName] ) {
        isOutOfDate = true
      } else if (browserMajorVersion < browserSupport[browserName]) {
        isOutOfDate = true;
      }
      return isOutOfDate;
    };

    // Returns true if a browser supports a css3 property
    var isPropertySupported = function (prop) {
      if (!prop) {
        return true;
      }
      var div = document.createElement('div');
      var vendorPrefixes = ['khtml', 'ms', 'o', 'moz', 'webkit'];
      var count = vendorPrefixes.length;

      if (div.style[prop]) {
        return true;
      }

      prop = prop.replace(/^[a-z]/, function (val) {
        return val.toUpperCase();
      });

      while (count--) {
        if (div.style[vendorPrefixes[count] + prop]) {
          return true;
        }
      }
      return false;
    };

    var makeFadeInFunction = function(opacityValue) {
      return function () {
        fadeIn(opacityValue);
      };
    };

    // Style element explicitly - TODO: investigate and delete if not needed
    var startStylesAndEvents = function () {
      var buttonClose = document.getElementById('buttonCloseUpdateBrowser');
      var buttonUpdate = document.getElementById('buttonUpdateBrowser');

      //check settings attributes
      outdatedUI.style.backgroundColor = backgroundColor;
      //way too hard to put !important on IE6
      outdatedUI.style.color = textColor;
      outdatedUI.children[0].style.color = textColor;
      outdatedUI.children[1].style.color = textColor;

      // Update button is desktop only
      if (buttonUpdate) {
        buttonUpdate.style.color = textColor;
        if (buttonUpdate.style.borderColor) {
          buttonUpdate.style.borderColor = textColor;
        }

        // Override the update button color to match the background color
        buttonUpdate.onmouseover = function () {
          this.style.color = backgroundColor;
          this.style.backgroundColor = textColor;
        };

        buttonUpdate.onmouseout = function () {
          this.style.color = textColor;
          this.style.backgroundColor = backgroundColor;
        };
      }

      buttonClose.style.color = textColor;

      buttonClose.onmousedown = function () {
        outdatedUI.style.display = 'none';
        return false;
      };
    };

    var getmessage = function (lang) {
      var messages = languageMessages[lang] || languageMessages.en;

      var updateMessages = {
        'web': '
' + messages.update.web + '' + messages.callToAction + '
',
        'googlePlay': '
' + messages.update.googlePlay +
        '' + messages.callToAction + '
',
        'appStore': '
' + messages.update[updateSource] + '
'
      };

      var updateMessage = updateMessages[updateSource];

      return '
' + messages.outOfDate + '
' + updateMessage +
        '
×
';
    };

    // Check if browser is supported
    if (isBrowserOutOfDate() || ! isPropertySupported(requiredCssProperty) || isAndroidButNotChrome) {

      // This is an outdated browser
      if (done && outdatedUI.style.opacity !== '1') {
        done = false;

        for (var opacity = 1; opacity <= 100; opacity++) {
          setTimeout(makeFadeInFunction(opacity), opacity * 8);
        }
      }

      var insertContentHere = document.getElementById('outdated');
      insertContentHere.innerHTML = getmessage(language);
      startStylesAndEvents();
    }
  };

  // Load main when DOM ready.
  var oldOnload = window.onload;
  if (typeof window.onload !== 'function') {
    window.onload = main;
  }
  else {
    window.onload = function () {
      if (oldOnload) {
        oldOnload();
      }
      main();
    };
  }
};
<|endoftext|>


<commit_before>/**
 * logger.js: TODO: add file header description.
 *
 * (C) 2010 Charlie Robbins
 * MIT LICENCE
 */

'use strict';

const stream = require('readable-stream');
const asyncForEach = require('async/forEach');
const { LEVEL, SPLAT } = require('triple-beam');
const isStream = require('is-stream');
const ExceptionHandler = require('./exception-handler');
const LegacyTransportStream = require('winston-transport/legacy');
const Profiler = require('./profiler');
const { clone, warn } = require('./common');
const config = require('./config');

/**
 * TODO: add class description.
 * @type {Logger}
 * @extends {stream.Transform}
 */
class Logger extends stream.Transform {
  /**
   * Constructor function for the Logger object responsible for persisting log
   * messages and metadata to one or more transports.
   * @param {!Object} options - foo
   */
  constructor(options) {
    super({
      objectMode: true
    });
    this.configure(options);
  }

  /**
   * This will wholesale reconfigure this instance by:
   * 1. Resetting all transports. Older transports will be removed implicitly.
   * 2. Set all other options including levels, colors, rewriters, filters,
   *    exceptionHandlers, etc.
   * @param {!Object} options - TODO: add param description.
   * @returns {undefined}
   */
  configure({
    silent,
    format,
    levels,
    level = 'info',
    exitOnError = true,
    transports,
    colors,
    emitErrs,
    formatters,
    padLevels,
    rewriters,
    stripColors,
    exceptionHandlers
  } = {}) {
    // Reset transports if we already have them
    if (this.transports.length) {
      this.clear();
    }

    this.silent = silent;
    this.format = format || this.format || require('logform/json')();

    // Hoist other options onto this instance.
    this.levels = levels || this.levels || config.npm.levels;
    this.level = level;
    this.exceptions = new ExceptionHandler(this);
    this.profilers = {};
    this.exitOnError = exitOnError;

    // Add all transports we have been provided.
    if (transports) {
      transports = Array.isArray(transports) ? transports : [transports];
      transports.forEach(transport => this.add(transport));
    }

    if (
      colors || emitErrs || formatters ||
      padLevels || rewriters || stripColors
    ) {
      throw new Error([
        '{ colors, emitErrs, formatters, padLevels, rewriters, stripColors } were removed in winston@3.0.0.',
        'Use a custom winston.format(function) instead.',
        'See: https://github.com/winstonjs/winston/tree/master/UPGRADE-3.0.md'
      ].join('\n'));
    }

    if (exceptionHandlers) {
      this.exceptions.handle(exceptionHandlers);
    }
  }

  /* eslint-disable valid-jsdoc */
  /**
   * Ensure backwards compatibility with a `log` method
   * @param {mixed} level - Level the log message is written at.
   * @param {mixed} msg - TODO: add param description.
   * @param {mixed} meta - TODO: add param description.
   * @returns {Logger} - TODO: add return description.
   *
   * @example
   *    // Supports the existing API:
   *    logger.log('info', 'Hello world', { custom: true });
   *    logger.log('info', new Error('Yo, it\'s on fire'));
   *    logger.log('info', '%s %d%%', 'A string', 50, { thisIsMeta: true });
   *
   *    // And the new API with a single JSON literal:
   *    logger.log({ level: 'info', message: 'Hello world', custom: true });
   *    logger.log({ level: 'info', message: new Error('Yo, it\'s on fire') });
   *    logger.log({
   *      level: 'info',
   *      message: '%s %d%%',
   *      [SPLAT]: ['A string', 50],
   *      meta: { thisIsMeta: true }
   *    });
   *
   */
  /* eslint-enable valid-jsdoc */
  log(level, msg, ...splat) { // eslint-disable-line max-params
    // Optimize for the hotpath of logging JSON literals
    if (arguments.length === 1) {
      // Yo dawg, I heard you like levels ... seriously ...
      // In this context the LHS `level` here is actually the `info` so read
      // this as: info[LEVEL] = info.level;
      level[LEVEL] = level.level;
      this.write(level);
      return this;
    }

    // Slightly less hotpath, but worth optimizing for.
    if (arguments.length === 2) {
      if (msg && typeof msg === 'object') {
        msg[LEVEL] = msg.level = level;
        this.write(msg);
        return this;
      }

      this.write({ [LEVEL]: level, level, message: msg });
      return this;
    }

    const [meta] = splat;
    if (typeof meta === 'object' && meta !== null) {
      this.write(Object.assign({}, meta, {
        [LEVEL]: level,
        [SPLAT]: splat.slice(0),
        level,
        message: msg
      }));
    } else {
      this.write(Object.assign({}, {
        [LEVEL]: level,
        [SPLAT]: splat,
        level,
        message: msg
      }));
    }

    return this;
  }

  /**
   * Pushes data so that it can be picked up by all of our pipe targets.
   * @param {mixed} info - TODO: add param description.
   * @param {mixed} enc - TODO: add param description.
   * @param {mixed} callback - Continues stream processing.
   * @returns {undefined}
   * @private
   */
  _transform(info, enc, callback) {
    if (this.silent) {
      return callback();
    }

    // [LEVEL] is only soft guaranteed to be set here since we are a proper
    // stream. It is likely that `info` came in through `.log(info)` or
    // `.info(info)`. If it is not defined, however, define it.
    // This LEVEL symbol is provided by `triple-beam` and also used in:
    // - logform
    // - winston-transport
    // - abstract-winston-transport
    if (!info[LEVEL]) {
      info[LEVEL] = info.level;
    }

    // Remark: really not sure what to do here, but this has been reported as
    // very confusing by pre winston@2.0.0 users as quite confusing when using
    // custom levels.
    if (!this.levels[info[LEVEL]] && this.levels[info[LEVEL]] !== 0) {
      // eslint-disable-next-line no-console
      console.error('[winston] Unknown logger level: %s', info[LEVEL]);
    }

    // Remark: not sure if we should simply error here.
    if (!this._readableState.pipes) {
      // eslint-disable-next-line no-console
      console.error('[winston] Attempt to write logs with no transports %j', info);
    }

    // Here we write to the `format` pipe-chain, which on `readable` above will
    // push the formatted `info` Object onto the buffer for this instance. We trap
    // (and re-throw) any errors generated by the user-provided format, but also
    // guarantee that the streams callback is invoked so that we can continue flowing.
    try {
      this.push(this.format.transform(info, this.format.options));
    } catch (ex) {
      throw ex;
    } finally {
      // eslint-disable-next-line callback-return
      callback();
    }
  }

  /**
   * Delays the 'finish' event until all transport pipe targets have
   * also emitted 'finish' or are already finished.
   * @param {mixed} callback - Continues stream processing.
   */
  _final(callback) {
    const transports = this.transports.slice();
    asyncForEach(transports, (transport, next) => {
      if (!transport || transport.finished) return setImmediate(next);
      transport.once('finish', next);
      transport.end();
    }, callback);
  }

  /**
   * Adds the transport to this logger instance by piping to it.
   * @param {mixed} transport - TODO: add param description.
   * @returns {Logger} - TODO: add return description.
   */
  add(transport) {
    // Support backwards compatibility with all existing `winston < 3.x.x`
    // transports which meet one of two criteria:
    // 1. They inherit from winston.Transport in  < 3.x.x which is NOT a stream.
    // 2. They expose a log method which has a length greater than 2 (i.e. more then
    //    just `log(info, callback)`.
    const target = !isStream(transport) || transport.log.length > 2
      ? new LegacyTransportStream({ transport })
      : transport;

    if (!target._writableState || !target._writableState.objectMode) {
      throw new Error('Transports must WritableStreams in objectMode. Set { objectMode: true }.');
    }

    // Listen for the `error` event on the new Transport.
    this._onError(target);
    this.pipe(target);

    if (transport.handleExceptions) {
      this.exceptions.handle();
    }

    return this;
  }

  /**
   * Removes the transport from this logger instance by unpiping from it.
   * @param {mixed} transport - TODO: add param description.
   * @returns {Logger} - TODO: add return description.
   */
  remove(transport) {
    let target = transport;
    if (!isStream(transport) || transport.log.length > 2) {
      target = this.transports
        .filter(match => match.transport === transport)[0];
    }

    if (target) { this.unpipe(target); }
    return this;
  }

  /**
   * Removes all transports from this logger instance.
   * @returns {Logger} - TODO: add return description.
   */
  clear() {
    this.unpipe();
    return this;
  }

  /**
   * Cleans up resources (streams, event listeners) for all transports
   * associated with this instance (if necessary).
   * @returns {Logger} - TODO: add return description.
   */
  close() {
    this.clear();
    this.emit('close');
    return this;
  }

  /**
   * Sets the `target` levels specified on this instance.
   * @param {Object} Target levels to use on this instance.
   */
  setLevels() {
    warn.deprecated('setLevels');
  }

  /**
   * Queries the all transports for this instance with the specified `options`.
   * This will aggregate each transport's results into one object containing
   * a property per transport.
   * @param {Object} options - Query options for this instance.
   * @param {function} callback - Continuation to respond to when complete.
   * @retruns {mixed} - TODO: add return description.
   */
  query(options, callback) {
    if (typeof options === 'function') {
      callback = options;
      options = {};
    }

    options = options || {};
    const results = {};
    const queryObject = clone(options.query) || {};

    // Helper function to query a single transport
    function queryTransport(transport, next) {
      if (options.query) {
        options.query = transport.formatQuery(queryObject);
      }

      transport.query(options, (err, res) => {
        if (err) {
          return next(err);
        }

        next(null, transport.formatResults(res, options.format));
      });
    }

    // Helper function to accumulate the results from `queryTransport` into
    // the `results`.
    function addResults(transport, next) {
      queryTransport(transport, (err, result) => {
        // queryTransport could potentially invoke the callback multiple times
        // since Transport code can be unpredictable.
        if (next) {
          result = err || result;
          if (result) {
            results[transport.name] = result;
          }

          // eslint-disable-next-line callback-return
          next();
        }

        next = null;
      });
    }

    // Iterate over the transports in parallel setting the appropriate key in
    // the `results`.
    asyncForEach(
      this.transports.filter(transport => !!transport.query),
      addResults,
      () => callback(null, results)
    );
  }

  /**
   * Returns a log stream for all transports. Options object is optional.
   * @param{Object} options={} - Stream options for this instance.
   * @returns {Stream} - TODO: add return description.
   */
  stream(options = {}) {
    const out = new stream.Stream();
    const streams = [];

    out._streams = streams;
    out.destroy = () => {
      let i = streams.length;
      while (i--) {
        streams[i].destroy();
      }
    };

    // Create a list of all transports for this instance.
    this.transports
      .filter(transport => !!transport.stream)
      .forEach(transport => {
        const str = transport.stream(options);
        if (!str) {
          return;
        }

        streams.push(str);

        str.on('log', log => {
          log.transport = log.transport || [];
          log.transport.push(transport.name);
          out.emit('log', log);
        });

        str.on('error', err => {
          err.transport = err.transport || [];
          err.transport.push(transport.name);
          out.emit('error', err);
        });
      });

    return out;
  }

  /**
   * Returns an object corresponding to a specific timing. When done is called
   * the timer will finish and log the duration. e.g.:
   * @returns {Profile} - TODO: add return description.
   * @example
   *    const timer = winston.startTimer()
   *    setTimeout(() => {
   *      timer.done({
   *        message: 'Logging message'
   *      });
   *    }, 1000);
   */
  startTimer() {
    return new Profiler(this);
  }

  /**
   * Tracks the time inbetween subsequent calls to this method with the same
   * `id` parameter. The second call to this method will log the difference in
   * milliseconds along with the message.
   * @param {string} id Unique id of the profiler
   * @returns {Logger} - TODO: add return description.
   */
  profile(id, ...args) {
    const time = Date.now();
    if (this.profilers[id]) {
      const timeEnd = this.profilers[id];
      delete this.profilers[id];

      // Attempt to be kind to users if they are still using older APIs.
      if (typeof args[args.length - 2] === 'function') {
        // eslint-disable-next-line no-console
        console.warn('Callback function no longer supported as of winston@3.0.0');
        args.pop();
      }

      // Set the duration property of the metadata
      const info = typeof args[args.length - 1] === 'object' ? args.pop() : {};
      info.level = info.level || 'info';
      info.durationMs = time - timeEnd;
      info.message = info.message || id;
      return this.write(info);
    }

    this.profilers[id] = time;
    return this;
  }

  /**
   * Backwards compatibility to `exceptions.handle` in winston < 3.0.0.
   * @returns {undefined}
   * @deprecated
   */
  handleExceptions(...args) {
    // eslint-disable-next-line no-console
    console.warn('Deprecated: .handleExceptions() will be removed in winston@4. Use .exceptions.handle()');
    this.exceptions.handle(...args);
  }

  /**
   * Backwards compatibility to `exceptions.handle` in winston < 3.0.0.
   * @returns {undefined}
   * @deprecated
   */
  unhandleExceptions(...args) {
    // eslint-disable-next-line no-console
    console.warn('Deprecated: .unhandleExceptions() will be removed in winston@4. Use .unexceptions.handle()');
    this.exceptions.unhandle(...args);
  }

  /**
   * Throw a more meaningful deprecation notice
   * @throws {Error} - TODO: add throws description.
   */
  cli() {
    throw new Error([
      'Logger.cli() was removed in winston@3.0.0',
      'Use a custom winston.formats.cli() instead.',
      'See: https://github.com/winstonjs/winston/tree/master/UPGRADE-3.0.md'
    ].join('\n'));
  }

  /**
   * Bubbles the error, `err`, that occured on the specified `transport` up
   * from this instance if `emitErrs` has been set.
   * @param {Object} transport - Transport on which the error occured
   * @throws {Error} - Error that occurred on the transport
   * @private
   */
  _onError(transport) {
    function transportError(err) {
      this.emit('error', err, transport);
    }

    if (!transport.__winstonError) {
      transport.__winstonError = transportError.bind(this);
      transport.on('error', transport.__winstonError);
    }
  }
}

/**
 * Represents the current readableState pipe targets for this Logger instance.
 * @type {Array|Object}
 */
Object.defineProperty(Logger.prototype, 'transports', {
  configurable: false,
  enumerable: true,
  get() {
    const { pipes } = this._readableState;
    return !Array.isArray(pipes) ? [pipes].filter(Boolean) : pipes;
  }
});

module.exports = Logger;
<commit_msg>fix deprecation warning typo (#1359)<commit_after>/**
 * logger.js: TODO: add file header description.
 *
 * (C) 2010 Charlie Robbins
 * MIT LICENCE
 */

'use strict';

const stream = require('readable-stream');
const asyncForEach = require('async/forEach');
const { LEVEL, SPLAT } = require('triple-beam');
const isStream = require('is-stream');
const ExceptionHandler = require('./exception-handler');
const LegacyTransportStream = require('winston-transport/legacy');
const Profiler = require('./profiler');
const { clone, warn } = require('./common');
const config = require('./config');

/**
 * TODO: add class description.
 * @type {Logger}
 * @extends {stream.Transform}
 */
class Logger extends stream.Transform {
  /**
   * Constructor function for the Logger object responsible for persisting log
   * messages and metadata to one or more transports.
   * @param {!Object} options - foo
   */
  constructor(options) {
    super({
      objectMode: true
    });
    this.configure(options);
  }

  /**
   * This will wholesale reconfigure this instance by:
   * 1. Resetting all transports. Older transports will be removed implicitly.
   * 2. Set all other options including levels, colors, rewriters, filters,
   *    exceptionHandlers, etc.
   * @param {!Object} options - TODO: add param description.
   * @returns {undefined}
   */
  configure({
    silent,
    format,
    levels,
    level = 'info',
    exitOnError = true,
    transports,
    colors,
    emitErrs,
    formatters,
    padLevels,
    rewriters,
    stripColors,
    exceptionHandlers
  } = {}) {
    // Reset transports if we already have them
    if (this.transports.length) {
      this.clear();
    }

    this.silent = silent;
    this.format = format || this.format || require('logform/json')();

    // Hoist other options onto this instance.
    this.levels = levels || this.levels || config.npm.levels;
    this.level = level;
    this.exceptions = new ExceptionHandler(this);
    this.profilers = {};
    this.exitOnError = exitOnError;

    // Add all transports we have been provided.
    if (transports) {
      transports = Array.isArray(transports) ? transports : [transports];
      transports.forEach(transport => this.add(transport));
    }

    if (
      colors || emitErrs || formatters ||
      padLevels || rewriters || stripColors
    ) {
      throw new Error([
        '{ colors, emitErrs, formatters, padLevels, rewriters, stripColors } were removed in winston@3.0.0.',
        'Use a custom winston.format(function) instead.',
        'See: https://github.com/winstonjs/winston/tree/master/UPGRADE-3.0.md'
      ].join('\n'));
    }

    if (exceptionHandlers) {
      this.exceptions.handle(exceptionHandlers);
    }
  }

  /* eslint-disable valid-jsdoc */
  /**
   * Ensure backwards compatibility with a `log` method
   * @param {mixed} level - Level the log message is written at.
   * @param {mixed} msg - TODO: add param description.
   * @param {mixed} meta - TODO: add param description.
   * @returns {Logger} - TODO: add return description.
   *
   * @example
   *    // Supports the existing API:
   *    logger.log('info', 'Hello world', { custom: true });
   *    logger.log('info', new Error('Yo, it\'s on fire'));
   *    logger.log('info', '%s %d%%', 'A string', 50, { thisIsMeta: true });
   *
   *    // And the new API with a single JSON literal:
   *    logger.log({ level: 'info', message: 'Hello world', custom: true });
   *    logger.log({ level: 'info', message: new Error('Yo, it\'s on fire') });
   *    logger.log({
   *      level: 'info',
   *      message: '%s %d%%',
   *      [SPLAT]: ['A string', 50],
   *      meta: { thisIsMeta: true }
   *    });
   *
   */
  /* eslint-enable valid-jsdoc */
  log(level, msg, ...splat) { // eslint-disable-line max-params
    // Optimize for the hotpath of logging JSON literals
    if (arguments.length === 1) {
      // Yo dawg, I heard you like levels ... seriously ...
      // In this context the LHS `level` here is actually the `info` so read
      // this as: info[LEVEL] = info.level;
      level[LEVEL] = level.level;
      this.write(level);
      return this;
    }

    // Slightly less hotpath, but worth optimizing for.
    if (arguments.length === 2) {
      if (msg && typeof msg === 'object') {
        msg[LEVEL] = msg.level = level;
        this.write(msg);
        return this;
      }

      this.write({ [LEVEL]: level, level, message: msg });
      return this;
    }

    const [meta] = splat;
    if (typeof meta === 'object' && meta !== null) {
      this.write(Object.assign({}, meta, {
        [LEVEL]: level,
        [SPLAT]: splat.slice(0),
        level,
        message: msg
      }));
    } else {
      this.write(Object.assign({}, {
        [LEVEL]: level,
        [SPLAT]: splat,
        level,
        message: msg
      }));
    }

    return this;
  }

  /**
   * Pushes data so that it can be picked up by all of our pipe targets.
   * @param {mixed} info - TODO: add param description.
   * @param {mixed} enc - TODO: add param description.
   * @param {mixed} callback - Continues stream processing.
   * @returns {undefined}
   * @private
   */
  _transform(info, enc, callback) {
    if (this.silent) {
      return callback();
    }

    // [LEVEL] is only soft guaranteed to be set here since we are a proper
    // stream. It is likely that `info` came in through `.log(info)` or
    // `.info(info)`. If it is not defined, however, define it.
    // This LEVEL symbol is provided by `triple-beam` and also used in:
    // - logform
    // - winston-transport
    // - abstract-winston-transport
    if (!info[LEVEL]) {
      info[LEVEL] = info.level;
    }

    // Remark: really not sure what to do here, but this has been reported as
    // very confusing by pre winston@2.0.0 users as quite confusing when using
    // custom levels.
    if (!this.levels[info[LEVEL]] && this.levels[info[LEVEL]] !== 0) {
      // eslint-disable-next-line no-console
      console.error('[winston] Unknown logger level: %s', info[LEVEL]);
    }

    // Remark: not sure if we should simply error here.
    if (!this._readableState.pipes) {
      // eslint-disable-next-line no-console
      console.error('[winston] Attempt to write logs with no transports %j', info);
    }

    // Here we write to the `format` pipe-chain, which on `readable` above will
    // push the formatted `info` Object onto the buffer for this instance. We trap
    // (and re-throw) any errors generated by the user-provided format, but also
    // guarantee that the streams callback is invoked so that we can continue flowing.
    try {
      this.push(this.format.transform(info, this.format.options));
    } catch (ex) {
      throw ex;
    } finally {
      // eslint-disable-next-line callback-return
      callback();
    }
  }

  /**
   * Delays the 'finish' event until all transport pipe targets have
   * also emitted 'finish' or are already finished.
   * @param {mixed} callback - Continues stream processing.
   */
  _final(callback) {
    const transports = this.transports.slice();
    asyncForEach(transports, (transport, next) => {
      if (!transport || transport.finished) return setImmediate(next);
      transport.once('finish', next);
      transport.end();
    }, callback);
  }

  /**
   * Adds the transport to this logger instance by piping to it.
   * @param {mixed} transport - TODO: add param description.
   * @returns {Logger} - TODO: add return description.
   */
  add(transport) {
    // Support backwards compatibility with all existing `winston < 3.x.x`
    // transports which meet one of two criteria:
    // 1. They inherit from winston.Transport in  < 3.x.x which is NOT a stream.
    // 2. They expose a log method which has a length greater than 2 (i.e. more then
    //    just `log(info, callback)`.
    const target = !isStream(transport) || transport.log.length > 2
      ? new LegacyTransportStream({ transport })
      : transport;

    if (!target._writableState || !target._writableState.objectMode) {
      throw new Error('Transports must WritableStreams in objectMode. Set { objectMode: true }.');
    }

    // Listen for the `error` event on the new Transport.
    this._onError(target);
    this.pipe(target);

    if (transport.handleExceptions) {
      this.exceptions.handle();
    }

    return this;
  }

  /**
   * Removes the transport from this logger instance by unpiping from it.
   * @param {mixed} transport - TODO: add param description.
   * @returns {Logger} - TODO: add return description.
   */
  remove(transport) {
    let target = transport;
    if (!isStream(transport) || transport.log.length > 2) {
      target = this.transports
        .filter(match => match.transport === transport)[0];
    }

    if (target) { this.unpipe(target); }
    return this;
  }

  /**
   * Removes all transports from this logger instance.
   * @returns {Logger} - TODO: add return description.
   */
  clear() {
    this.unpipe();
    return this;
  }

  /**
   * Cleans up resources (streams, event listeners) for all transports
   * associated with this instance (if necessary).
   * @returns {Logger} - TODO: add return description.
   */
  close() {
    this.clear();
    this.emit('close');
    return this;
  }

  /**
   * Sets the `target` levels specified on this instance.
   * @param {Object} Target levels to use on this instance.
   */
  setLevels() {
    warn.deprecated('setLevels');
  }

  /**
   * Queries the all transports for this instance with the specified `options`.
   * This will aggregate each transport's results into one object containing
   * a property per transport.
   * @param {Object} options - Query options for this instance.
   * @param {function} callback - Continuation to respond to when complete.
   * @retruns {mixed} - TODO: add return description.
   */
  query(options, callback) {
    if (typeof options === 'function') {
      callback = options;
      options = {};
    }

    options = options || {};
    const results = {};
    const queryObject = clone(options.query) || {};

    // Helper function to query a single transport
    function queryTransport(transport, next) {
      if (options.query) {
        options.query = transport.formatQuery(queryObject);
      }

      transport.query(options, (err, res) => {
        if (err) {
          return next(err);
        }

        next(null, transport.formatResults(res, options.format));
      });
    }

    // Helper function to accumulate the results from `queryTransport` into
    // the `results`.
    function addResults(transport, next) {
      queryTransport(transport, (err, result) => {
        // queryTransport could potentially invoke the callback multiple times
        // since Transport code can be unpredictable.
        if (next) {
          result = err || result;
          if (result) {
            results[transport.name] = result;
          }

          // eslint-disable-next-line callback-return
          next();
        }

        next = null;
      });
    }

    // Iterate over the transports in parallel setting the appropriate key in
    // the `results`.
    asyncForEach(
      this.transports.filter(transport => !!transport.query),
      addResults,
      () => callback(null, results)
    );
  }

  /**
   * Returns a log stream for all transports. Options object is optional.
   * @param{Object} options={} - Stream options for this instance.
   * @returns {Stream} - TODO: add return description.
   */
  stream(options = {}) {
    const out = new stream.Stream();
    const streams = [];

    out._streams = streams;
    out.destroy = () => {
      let i = streams.length;
      while (i--) {
        streams[i].destroy();
      }
    };

    // Create a list of all transports for this instance.
    this.transports
      .filter(transport => !!transport.stream)
      .forEach(transport => {
        const str = transport.stream(options);
        if (!str) {
          return;
        }

        streams.push(str);

        str.on('log', log => {
          log.transport = log.transport || [];
          log.transport.push(transport.name);
          out.emit('log', log);
        });

        str.on('error', err => {
          err.transport = err.transport || [];
          err.transport.push(transport.name);
          out.emit('error', err);
        });
      });

    return out;
  }

  /**
   * Returns an object corresponding to a specific timing. When done is called
   * the timer will finish and log the duration. e.g.:
   * @returns {Profile} - TODO: add return description.
   * @example
   *    const timer = winston.startTimer()
   *    setTimeout(() => {
   *      timer.done({
   *        message: 'Logging message'
   *      });
   *    }, 1000);
   */
  startTimer() {
    return new Profiler(this);
  }

  /**
   * Tracks the time inbetween subsequent calls to this method with the same
   * `id` parameter. The second call to this method will log the difference in
   * milliseconds along with the message.
   * @param {string} id Unique id of the profiler
   * @returns {Logger} - TODO: add return description.
   */
  profile(id, ...args) {
    const time = Date.now();
    if (this.profilers[id]) {
      const timeEnd = this.profilers[id];
      delete this.profilers[id];

      // Attempt to be kind to users if they are still using older APIs.
      if (typeof args[args.length - 2] === 'function') {
        // eslint-disable-next-line no-console
        console.warn('Callback function no longer supported as of winston@3.0.0');
        args.pop();
      }

      // Set the duration property of the metadata
      const info = typeof args[args.length - 1] === 'object' ? args.pop() : {};
      info.level = info.level || 'info';
      info.durationMs = time - timeEnd;
      info.message = info.message || id;
      return this.write(info);
    }

    this.profilers[id] = time;
    return this;
  }

  /**
   * Backwards compatibility to `exceptions.handle` in winston < 3.0.0.
   * @returns {undefined}
   * @deprecated
   */
  handleExceptions(...args) {
    // eslint-disable-next-line no-console
    console.warn('Deprecated: .handleExceptions() will be removed in winston@4. Use .exceptions.handle()');
    this.exceptions.handle(...args);
  }

  /**
   * Backwards compatibility to `exceptions.handle` in winston < 3.0.0.
   * @returns {undefined}
   * @deprecated
   */
  unhandleExceptions(...args) {
    // eslint-disable-next-line no-console
    console.warn('Deprecated: .unhandleExceptions() will be removed in winston@4. Use .exceptions.unhandle()');
    this.exceptions.unhandle(...args);
  }

  /**
   * Throw a more meaningful deprecation notice
   * @throws {Error} - TODO: add throws description.
   */
  cli() {
    throw new Error([
      'Logger.cli() was removed in winston@3.0.0',
      'Use a custom winston.formats.cli() instead.',
      'See: https://github.com/winstonjs/winston/tree/master/UPGRADE-3.0.md'
    ].join('\n'));
  }

  /**
   * Bubbles the error, `err`, that occured on the specified `transport` up
   * from this instance if `emitErrs` has been set.
   * @param {Object} transport - Transport on which the error occured
   * @throws {Error} - Error that occurred on the transport
   * @private
   */
  _onError(transport) {
    function transportError(err) {
      this.emit('error', err, transport);
    }

    if (!transport.__winstonError) {
      transport.__winstonError = transportError.bind(this);
      transport.on('error', transport.__winstonError);
    }
  }
}

/**
 * Represents the current readableState pipe targets for this Logger instance.
 * @type {Array|Object}
 */
Object.defineProperty(Logger.prototype, 'transports', {
  configurable: false,
  enumerable: true,
  get() {
    const { pipes } = this._readableState;
    return !Array.isArray(pipes) ? [pipes].filter(Boolean) : pipes;
  }
});

module.exports = Logger;
<|endoftext|>


<commit_before>/**
 * Module dependencies
 */

var angular = window.angular;
var name = 'head-extend';

var mod = module.exports = window.angular.module(name, []);

mod.name = name;

mod.directive('headExtend', [
  '$compile',
  function($compile) {
    var head = angular.element(document.head);

    var origMetas = Array.prototype.reduce.call(head.find('meta'), function(acc, meta) {
      var name = meta.name || meta.property;
      if (!name) return acc;
      acc[name] = angular.element(meta);
      return acc;
    }, {});

    function replace(prev, tmpl, $scope) {
      if (!tmpl) return;
      var el = tmpl($scope);

      prev ? prev.replaceWith(el) : head.append(el);

      $scope.$on('$destroy', function() {
        prev ? el.replaceWith(prev) : el.remove();
      });
    }

    return {
      restrict: 'AE',
      compile: function(tEl, tAttrs) {
        tEl.remove();

        var titleEl = tEl.find('title')[0];
        var title = titleEl ? $compile(titleEl.outerHTML) : null;

        var metas = Array.prototype.map.call(tEl.find('meta'), function(meta) {
          var name = meta.name;
          return {tmpl: $compile(meta.outerHTML), orig: origMetas[name]};
        });

        var links = Array.prototype.map.call(tEl.find('link'), $compile);

        tEl.html('');
        var html = $compile(tEl[0].outerHTML.replace('head-extend', 'div'));

        return function($scope, el, attrs) {
          html($scope);
          replace(head.find('title'), title, $scope);
          metas.forEach(function(meta) {
            replace(meta.orig, meta.tmpl, $scope);
          });
          links.forEach(function(link) {
            replace(null, link, $scope);
          });
        };
      }
    };
  }
]);
<commit_msg>verify prev has a parent<commit_after>/**
 * Module dependencies
 */

var angular = window.angular;
var name = 'head-extend';

var mod = module.exports = window.angular.module(name, []);

mod.name = name;

mod.directive('headExtend', [
  '$compile',
  function($compile) {
    var head = angular.element(document.head);

    var origMetas = Array.prototype.reduce.call(head.find('meta'), function(acc, meta) {
      var name = meta.name || meta.property;
      if (!name) return acc;
      acc[name] = angular.element(meta);
      return acc;
    }, {});

    function replace(prev, tmpl, $scope) {
      if (!tmpl) return;
      var el = tmpl($scope);

      prev && prev.parent().length ? prev.replaceWith(el) : head.append(el);

      $scope.$on('$destroy', function() {
        prev && prev.parent().length ? el.replaceWith(prev) : el.remove();
      });
    }

    return {
      restrict: 'AE',
      compile: function(tEl, tAttrs) {
        tEl.remove();

        var titleEl = tEl.find('title')[0];
        var title = titleEl ? $compile(titleEl.outerHTML) : null;

        var metas = Array.prototype.map.call(tEl.find('meta'), function(meta) {
          var name = meta.name;
          return {tmpl: $compile(meta.outerHTML), orig: origMetas[name]};
        });

        var links = Array.prototype.map.call(tEl.find('link'), $compile);

        tEl.html('');
        var html = $compile(tEl[0].outerHTML.replace('head-extend', 'div'));

        return function($scope, el, attrs) {
          html($scope);
          replace(head.find('title'), title, $scope);
          metas.forEach(function(meta) {
            replace(meta.orig, meta.tmpl, $scope);
          });
          links.forEach(function(link) {
            replace(null, link, $scope);
          });
        };
      }
    };
  }
]);
<|endoftext|>


<commit_before>'use strict';

var debug = require('diagnostics')('shrinkwrap')
  , semver = require('npm-registry/semver')
  , Registry = require('npm-registry')
  , Module = require('./module')
  , fuse = require('fusing');

//
// Variable cache.
//
var toString = Object.prototype.toString;

/**
 * Ensure that items in the array are unique.
 *
 * @param {Mixed} value A value in the array.
 * @param {Number} index The index of the item in the array.
 * @param {Array} arr Reference to the array we're filtering.
 * @returns {Boolean}
 */
function unique(value, index, arr) {
  return arr.indexOf(value) === index;
}

/**
 * Generate a new Shrinkwrap from a given package or module.
 *
 * Options:
 *
 * - registry: URL of the npm registry we should use to read package information.
 * - production: Should we only include production packages.
 * - limit: Amount of parallel processing tasks we could use to retrieve data.
 * - optimize: Should we attempt to optimize the data structure in the same way
 *   that npm would have done it.
 * - mirrors: A list of npm mirrors to be used with our registry.
 * - githulk: Optional preconfigured GitHulk.
 *
 * @constructor
 * @param {Object} options Options.
 * @api public
 */
function Shrinkwrap(options) {
  this.fuse();

  options = options || {};

  options.registry = 'registry' in options
    ? options.registry
    : 'http://registry.nodejitsu.com/';

  options.production = 'production' in options
    ? options.production
    : process.NODE_ENV === 'production';

  options.optimize = 'optimize' in options
    ? options.optimize
    : true;

  options.limit = 'limit' in options
    ? options.limit
    : 10;

  options.mirrors = 'mirrors' in options
    ? options.mirrors
    : false;

  this.registry = 'string' !== typeof options.registry
    ? options.registry
    : new Registry({
    registry: options.registry || Registry.mirrors.nodejitsu,
    githulk: options.githulk,
    mirrors: options.mirrors
  });

  this.production = options.production;     // Don't include devDependencies.
  this.limit = options.limit;               // Maximum concurrency.
  this.cache = Object.create(null);         // Dependency cache.
}

fuse(Shrinkwrap, require('eventemitter3'));

/**
 * No previous package, resolve one for us instead.
 *
 * @param {String} name Package name.
 * @param {String} range Version range.
 * @param {Function} fn The completion callback.
 * @api public
 */
Shrinkwrap.prototype.get = function get(name, range, fn) {
  if ('function' === typeof range) {
    fn = range;
    range = 'latest';
  }

  var shrinkwrap = this;

  this.registry.packages.release(name, range, function release(err, pkg) {
    if (err) return fn(err);

    debug('successfully resolved %s@%s', name, range);
    shrinkwrap.resolve(pkg, fn);
  });
};

/**
 * Get accurate type information for the given JavaScript class.
 *
 * @param {Mixed} of The thing who's type class we want to figure out.
 * @returns {String} lowercase variant of the name.
 * @api private
 */
Shrinkwrap.prototype.type = function type(of) {
  return toString.call(of).slice(8, -1).toLowerCase();
};

/**
 * Resolve all dependencies and their versions for the given root package.
 *
 * @param {Object} pkg  Package data from npm.
 * @param {Function} fn Callback
 * @api public
 */
Shrinkwrap.prototype.resolve = function resolve(source, fn) {
  source = Array.isArray(source) ? source[0] : source;

  var shrinkwrap = this;

  /**
   * Scan the given package.json like structure for possible dependency
   * locations which will be automatically queued for fetching and processing.
   *
   * @param {Object} packages The packages.json body.
   * @param {Object} ref The location of the new packages in the tree.
   * @param {Number} depth How deep was this package nested
   * @api private
   */
  function queue(packages, ref, depth) {
    packages = shrinkwrap.dedupe(packages);

    Shrinkwrap.dependencies.forEach(function each(key) {
      if (this.production && 'devDependencies' === key) return;
      if ('object' !== this.type(packages[key])) return;

      Object.keys(packages[key]).forEach(function each(name) {
        var range = packages[key][name]
          , _id = name +'@'+ range;

        ref.dependencies = ref.dependencies || {};
        queue.push({
          name: name,       // Name of the module
          range: range,     // Semver range
          _id: _id,         // Semi unique id.
          parents: [ref],   // Reference to the parent module.
          depth: depth      // The depth of the reference.
        });
      });
    }, shrinkwrap);

    return queue;
  }

  //
  // Our internal data structures that make it possible to search for packages.
  //
  queue.dependencytree = Object.create(null);
  queue.todolist = [];
  queue.errors = [];

  //
  // The original root that get's resolved.
  //
  queue.data = Object.create(null);
  queue.data.name = source.name;
  queue.data.version = source.version;

  /**
   * Check if we've already processed the specification before processing.
   *
   * @param {Object} data Processing specification for the worker.
   * @api private
   */
  queue.push = function push(data) {
    //
    // Optimization: prevent queueing the same module lookup.
    //
    if (queue.todolist.some(function some(todo) {
      if (todo._id !== data._id) return false;

      todo.parents = todo.parents.concat(data.parents).filter(unique);

      return true;
    })) return;

    //
    // Optimization: It has already been processed and listed before.
    //
    if (data._id in queue.dependencytree) {
      queue.dependencytree[data._id].dependent = (
        queue.dependencytree[data._id].dependent
      ).concat(data.parents).filter(unique);

      return shrinkwrap.optimize(queue.dependencytree[data._id]);
    }

    queue.todolist.push(data);
  };

  /**
   * Take an item from the todo list and process it's specification.
   *
   * @api private
   */
  queue.worker = function worker(err) {
    if (err) queue.errors.push(err);

    var spec = queue.todolist.shift();

    //
    // We've successfully processed all requests.
    //
    if (!spec) {
      fn(undefined, queue.dependencytree, queue.errors);

      shrinkwrap.destroy();
      return [
        'data',
        'errors',
        'dependencytree'
      ].forEach(function cleanup(remove) {
        delete queue[remove];
      });
    }

    debug('processing %s. %s left to process', spec.name, queue.todolist.length);

    shrinkwrap.release(spec.name, spec.range, function release(err, data, cached) {
      if (err || !data) {
        if (err) debug('failed to resolve %s due to error: ', spec.name, err);
        return worker(err);
      }

      var clone = queue.dependencytree[spec._id] = data.module.clone({
        parents: spec.parents,
        depth: spec.depth
      });

      spec.parents.forEach(function each(parent) {
        parent.dependencies[spec.name] = queue.dependencytree[spec._id];
      });

      if (!cached) queue(data, queue.dependencytree[spec._id], spec.depth + 1);
      worker();
    });
  };

  queue(source, queue.data, 0).worker();
};

/**
 * Get all releases for a given module name.
 *
 * @param {String} name The name of the module we should get
 * @param {Function} fn Callback
 * @api private
 */
Shrinkwrap.prototype.releases = function releases(name, fn) {
  if (this.cache && name in this.cache) {
    debug('CACHEHIT: Retrieving `%s` from cache', name);
    return fn(undefined, this.cache[name]);
  }

  var shrinkwrap = this;

  this.registry.packages.releases(name, function details(err, versions) {
    if (err) return fn(err);

    var keys = Object.keys(versions)
      , latest = keys.filter(function valid(ver) {
          return !!semver.valid(ver);
        }).sort(semver.rcompare)[0]
      , result = { latest: latest };

    //
    // Map the data to something smaller as we don't need all the package info.
    //
    result.releases = keys.reduce(function reduce(memo, key) {
      var data = versions[key];

      memo[key] = {
        released: data.time[key],
        licenses: data.licenses,
        _npmUser: data._npmUser,
        version: data.version,
        name: data.name,
        latest: latest,
      };

      Shrinkwrap.dependencies.forEach(function (dep) {
        memo[key][dep] = data[dep];
      });

      return memo;
    }, Object.create(null));

    result.versions = keys;

    if (shrinkwrap.cache) shrinkwrap.cache[name] = result;
    fn(err, result);
  });
};

/**
 * Get a single release.
 *
 * @param {String} name The name of the module.
 * @param {String} range The version range.
 * @param {Function} fn The callback.
 * @api private
 */
Shrinkwrap.prototype.release = function release(name, range, fn) {
  var key = name +'@'+ range
    , shrinkwrap = this;

  if (this.cache && key in this.cache) {
    debug('CACHEHIT: Retrieving `%s` from cache', key);
    return fn(undefined, this.cache[key], true);
  }

  this.releases(name, function releases(err, result) {
    if (err) return fn(err);

    var version = semver.maxSatisfying(result.versions, range)
      , data = result.releases[version];

    //
    // No matching version for the given module. It could be that the user has
    // set the range to git dependency instead.
    //
    if (!data) {
      debug('Couldnt find the matching version %s in the returned releases for %s', range, name);
      debug('Only found: %s', result.versions.join(', '));
      return fn();
    }

    //
    // Transform to a Module instance.
    //
    var cache = { module: new Module(data, range) };
    Shrinkwrap.dependencies.forEach(function (dep) {
      cache[dep] = data[dep];
    });

    if (shrinkwrap.cache) shrinkwrap.cache[key] = cache;

    fn(err, cache, false);
  });
};

/**
 * Optimize the dependency tree so we're not installing duplicate dependencies
 * in every module when they can be properly resolved by placing it upwards in
 * our dependency tree.
 *
 * @param {Object} dependency The dependency that has multiple dependents.
 * @api private
 */
Shrinkwrap.prototype.optimize = function optimize(dependency) {
  var dependent = dependency.dependent
    , version = dependency.version
    , name = dependency.name
    , common;

  /**
   * Find suitable parent nodes which can hold this module without creating
   * a possible conflict because there two different versions of the module in
   * the dependency tree.
   *
   * @param {Object} dependent A dependent of a module.
   * @returns {Array} parents.
   * @api private
   */
  function parent(dependent) {
    var node = dependent
      , result = [];

    while (node.parent) {
      if (!available(node.parent)) break;

      result.push(node.parent);
      node = node.parent;
    }

    return result;
  }

  /**
   * Checks if the dependency tree does not already contain a different version
   * of this module.
   *
   * @param {Object} dependencies The dependencies of a module.
   * @returns {Boolean} Available as module location.
   * @api private
   */
  function available(dependencies) {
    if (!dependencies) return false;

    return Object.keys(dependencies).every(function every(key) {
      var dependency = dependencies[key];

      if (!dependency) return false;

      if (dependency.name !== name) return true;
      if (dependency.version === version) return true;

      return false;
    });
  }

  var parents = dependent.map(function (dep) {
    var parents = parent(dep);

    //
    // No parents, this means we cannot move this module to a new location as it
    // will most likely conflict with other version. The only solution is to
    // keep it as duplicate as it's own dependencies. The way to do this is to
    // remove out of
    //
    if (!parents.length) {
      dependency.dependent.splice(dependency.dependent.indexOf(dep), 1);
    }

    return parents;
  }).filter(function filter(results) {
    return !!results.length;
  });

  //
  // Detect if all parents have a common root element where we can optimize the
  // module to.
  //
};

/**
 * It could be that a package has dependency added as devDependency as well as
 * a regular dependency. We want to make sure that we don't filter out this
 * dependency when we're resolving packages so we're going to remove it from the
 * devDependencies if they are exactly the same.
 *
 * @param {Object} pkg The package.
 * @returns {Object} The package.
 * @api private
 */
Shrinkwrap.prototype.dedupe = function dedupe(pkg) {
  if (!pkg) return pkg;
  if (!pkg.dependencies) return pkg;
  if (!pkg.devDependencies || this.production) return pkg;

  Object.keys(pkg.dependencies).forEach(function searchanddestroy(name) {
    if (!(name in pkg.devDependencies)) return;
    try {
      if (!semver.eq(pkg.devDependencies[name], pkg.dependencies[name])) return;
    } catch (e) { return; }

    //
    // Only remove when we have an exact match on the version number.
    //
    delete pkg.devDependencies[name];
  });

  return pkg;
};

/**
 * Clean up any cache of data structures that we might have had laying around.
 * Making this instance ready for garbage collection.
 *
 * @public
 */
Shrinkwrap.prototype.destroy = function destroy() {
  this.registry = this.cache = null;
};

/**
 * The various of locations where dependencies for a given module can be
 * defined.
 *
 * @type {Array}
 * @private
 */
Shrinkwrap.dependencies = [
  'dependencies',
  'devDependencies',
  'peerDependencies',
  'optionalDependencies'
];

//
// Expose the module interface.
//
module.exports = Shrinkwrap;
<commit_msg>[fix] Correctly destroy things with boolean return<commit_after>'use strict';

var debug = require('diagnostics')('shrinkwrap')
  , semver = require('npm-registry/semver')
  , Registry = require('npm-registry')
  , Module = require('./module')
  , fuse = require('fusing');

//
// Variable cache.
//
var toString = Object.prototype.toString;

/**
 * Ensure that items in the array are unique.
 *
 * @param {Mixed} value A value in the array.
 * @param {Number} index The index of the item in the array.
 * @param {Array} arr Reference to the array we're filtering.
 * @returns {Boolean}
 */
function unique(value, index, arr) {
  return arr.indexOf(value) === index;
}

/**
 * Generate a new Shrinkwrap from a given package or module.
 *
 * Options:
 *
 * - registry: URL of the npm registry we should use to read package information.
 * - production: Should we only include production packages.
 * - limit: Amount of parallel processing tasks we could use to retrieve data.
 * - optimize: Should we attempt to optimize the data structure in the same way
 *   that npm would have done it.
 * - mirrors: A list of npm mirrors to be used with our registry.
 * - githulk: Optional preconfigured GitHulk.
 *
 * @constructor
 * @param {Object} options Options.
 * @api public
 */
function Shrinkwrap(options) {
  this.fuse();

  options = options || {};

  options.registry = 'registry' in options
    ? options.registry
    : 'http://registry.nodejitsu.com/';

  options.production = 'production' in options
    ? options.production
    : process.NODE_ENV === 'production';

  options.optimize = 'optimize' in options
    ? options.optimize
    : true;

  options.limit = 'limit' in options
    ? options.limit
    : 10;

  options.mirrors = 'mirrors' in options
    ? options.mirrors
    : false;

  this.registry = 'string' !== typeof options.registry
    ? options.registry
    : new Registry({
    registry: options.registry || Registry.mirrors.nodejitsu,
    githulk: options.githulk,
    mirrors: options.mirrors
  });

  this.production = options.production;     // Don't include devDependencies.
  this.limit = options.limit;               // Maximum concurrency.
  this.cache = Object.create(null);         // Dependency cache.
}

fuse(Shrinkwrap, require('eventemitter3'));

/**
 * No previous package, resolve one for us instead.
 *
 * @param {String} name Package name.
 * @param {String} range Version range.
 * @param {Function} fn The completion callback.
 * @api public
 */
Shrinkwrap.prototype.get = function get(name, range, fn) {
  if ('function' === typeof range) {
    fn = range;
    range = 'latest';
  }

  var shrinkwrap = this;

  this.registry.packages.release(name, range, function release(err, pkg) {
    if (err) return fn(err);

    debug('successfully resolved %s@%s', name, range);
    shrinkwrap.resolve(pkg, fn);
  });
};

/**
 * Get accurate type information for the given JavaScript class.
 *
 * @param {Mixed} of The thing who's type class we want to figure out.
 * @returns {String} lowercase variant of the name.
 * @api private
 */
Shrinkwrap.prototype.type = function type(of) {
  return toString.call(of).slice(8, -1).toLowerCase();
};

/**
 * Resolve all dependencies and their versions for the given root package.
 *
 * @param {Object} pkg  Package data from npm.
 * @param {Function} fn Callback
 * @api public
 */
Shrinkwrap.prototype.resolve = function resolve(source, fn) {
  source = Array.isArray(source) ? source[0] : source;

  var shrinkwrap = this;

  /**
   * Scan the given package.json like structure for possible dependency
   * locations which will be automatically queued for fetching and processing.
   *
   * @param {Object} packages The packages.json body.
   * @param {Object} ref The location of the new packages in the tree.
   * @param {Number} depth How deep was this package nested
   * @api private
   */
  function queue(packages, ref, depth) {
    packages = shrinkwrap.dedupe(packages);

    Shrinkwrap.dependencies.forEach(function each(key) {
      if (this.production && 'devDependencies' === key) return;
      if ('object' !== this.type(packages[key])) return;

      Object.keys(packages[key]).forEach(function each(name) {
        var range = packages[key][name]
          , _id = name +'@'+ range;

        ref.dependencies = ref.dependencies || {};
        queue.push({
          name: name,       // Name of the module
          range: range,     // Semver range
          _id: _id,         // Semi unique id.
          parents: [ref],   // Reference to the parent module.
          depth: depth      // The depth of the reference.
        });
      });
    }, shrinkwrap);

    return queue;
  }

  //
  // Our internal data structures that make it possible to search for packages.
  //
  queue.dependencytree = Object.create(null);
  queue.todolist = [];
  queue.errors = [];

  //
  // The original root that get's resolved.
  //
  queue.data = Object.create(null);
  queue.data.name = source.name;
  queue.data.version = source.version;

  /**
   * Check if we've already processed the specification before processing.
   *
   * @param {Object} data Processing specification for the worker.
   * @api private
   */
  queue.push = function push(data) {
    //
    // Optimization: prevent queueing the same module lookup.
    //
    if (queue.todolist.some(function some(todo) {
      if (todo._id !== data._id) return false;

      todo.parents = todo.parents.concat(data.parents).filter(unique);

      return true;
    })) return;

    //
    // Optimization: It has already been processed and listed before.
    //
    if (data._id in queue.dependencytree) {
      queue.dependencytree[data._id].dependent = (
        queue.dependencytree[data._id].dependent
      ).concat(data.parents).filter(unique);

      return shrinkwrap.optimize(queue.dependencytree[data._id]);
    }

    queue.todolist.push(data);
  };

  /**
   * Take an item from the todo list and process it's specification.
   *
   * @api private
   */
  queue.worker = function worker(err) {
    if (err) queue.errors.push(err);

    var spec = queue.todolist.shift();

    //
    // We've successfully processed all requests.
    //
    if (!spec) {
      fn(undefined, queue.dependencytree, queue.errors);

      shrinkwrap.destroy();
      return [
        'data',
        'errors',
        'dependencytree'
      ].forEach(function cleanup(remove) {
        delete queue[remove];
      });
    }

    debug('processing %s. %s left to process', spec.name, queue.todolist.length);

    shrinkwrap.release(spec.name, spec.range, function release(err, data, cached) {
      if (err || !data) {
        if (err) debug('failed to resolve %s due to error: ', spec.name, err);
        return worker(err);
      }

      var clone = queue.dependencytree[spec._id] = data.module.clone({
        parents: spec.parents,
        depth: spec.depth
      });

      spec.parents.forEach(function each(parent) {
        parent.dependencies[spec.name] = queue.dependencytree[spec._id];
      });

      if (!cached) queue(data, queue.dependencytree[spec._id], spec.depth + 1);
      worker();
    });
  };

  queue(source, queue.data, 0).worker();
};

/**
 * Get all releases for a given module name.
 *
 * @param {String} name The name of the module we should get
 * @param {Function} fn Callback
 * @api private
 */
Shrinkwrap.prototype.releases = function releases(name, fn) {
  if (this.cache && name in this.cache) {
    debug('CACHEHIT: Retrieving `%s` from cache', name);
    return fn(undefined, this.cache[name]);
  }

  var shrinkwrap = this;

  this.registry.packages.releases(name, function details(err, versions) {
    if (err) return fn(err);

    var keys = Object.keys(versions)
      , latest = keys.filter(function valid(ver) {
          return !!semver.valid(ver);
        }).sort(semver.rcompare)[0]
      , result = { latest: latest };

    //
    // Map the data to something smaller as we don't need all the package info.
    //
    result.releases = keys.reduce(function reduce(memo, key) {
      var data = versions[key];

      memo[key] = {
        released: data.time[key],
        licenses: data.licenses,
        _npmUser: data._npmUser,
        version: data.version,
        name: data.name,
        latest: latest,
      };

      Shrinkwrap.dependencies.forEach(function (dep) {
        memo[key][dep] = data[dep];
      });

      return memo;
    }, Object.create(null));

    result.versions = keys;

    if (shrinkwrap.cache) shrinkwrap.cache[name] = result;
    fn(err, result);
  });
};

/**
 * Get a single release.
 *
 * @param {String} name The name of the module.
 * @param {String} range The version range.
 * @param {Function} fn The callback.
 * @api private
 */
Shrinkwrap.prototype.release = function release(name, range, fn) {
  var key = name +'@'+ range
    , shrinkwrap = this;

  if (this.cache && key in this.cache) {
    debug('CACHEHIT: Retrieving `%s` from cache', key);
    return fn(undefined, this.cache[key], true);
  }

  this.releases(name, function releases(err, result) {
    if (err) return fn(err);

    var version = semver.maxSatisfying(result.versions, range)
      , data = result.releases[version];

    //
    // No matching version for the given module. It could be that the user has
    // set the range to git dependency instead.
    //
    if (!data) {
      debug('Couldnt find the matching version %s in the returned releases for %s', range, name);
      debug('Only found: %s', result.versions.join(', '));
      return fn();
    }

    //
    // Transform to a Module instance.
    //
    var cache = { module: new Module(data, range) };
    Shrinkwrap.dependencies.forEach(function (dep) {
      cache[dep] = data[dep];
    });

    if (shrinkwrap.cache) shrinkwrap.cache[key] = cache;

    fn(err, cache, false);
  });
};

/**
 * Optimize the dependency tree so we're not installing duplicate dependencies
 * in every module when they can be properly resolved by placing it upwards in
 * our dependency tree.
 *
 * @param {Object} dependency The dependency that has multiple dependents.
 * @api private
 */
Shrinkwrap.prototype.optimize = function optimize(dependency) {
  var dependent = dependency.dependent
    , version = dependency.version
    , name = dependency.name
    , common;

  /**
   * Find suitable parent nodes which can hold this module without creating
   * a possible conflict because there two different versions of the module in
   * the dependency tree.
   *
   * @param {Object} dependent A dependent of a module.
   * @returns {Array} parents.
   * @api private
   */
  function parent(dependent) {
    var node = dependent
      , result = [];

    while (node.parent) {
      if (!available(node.parent)) break;

      result.push(node.parent);
      node = node.parent;
    }

    return result;
  }

  /**
   * Checks if the dependency tree does not already contain a different version
   * of this module.
   *
   * @param {Object} dependencies The dependencies of a module.
   * @returns {Boolean} Available as module location.
   * @api private
   */
  function available(dependencies) {
    if (!dependencies) return false;

    return Object.keys(dependencies).every(function every(key) {
      var dependency = dependencies[key];

      if (!dependency) return false;

      if (dependency.name !== name) return true;
      if (dependency.version === version) return true;

      return false;
    });
  }

  var parents = dependent.map(function (dep) {
    var parents = parent(dep);

    //
    // No parents, this means we cannot move this module to a new location as it
    // will most likely conflict with other version. The only solution is to
    // keep it as duplicate as it's own dependencies. The way to do this is to
    // remove out of
    //
    if (!parents.length) {
      dependency.dependent.splice(dependency.dependent.indexOf(dep), 1);
    }

    return parents;
  }).filter(function filter(results) {
    return !!results.length;
  });

  //
  // Detect if all parents have a common root element where we can optimize the
  // module to.
  //
};

/**
 * It could be that a package has dependency added as devDependency as well as
 * a regular dependency. We want to make sure that we don't filter out this
 * dependency when we're resolving packages so we're going to remove it from the
 * devDependencies if they are exactly the same.
 *
 * @param {Object} pkg The package.
 * @returns {Object} The package.
 * @api private
 */
Shrinkwrap.prototype.dedupe = function dedupe(pkg) {
  if (!pkg) return pkg;
  if (!pkg.dependencies) return pkg;
  if (!pkg.devDependencies || this.production) return pkg;

  Object.keys(pkg.dependencies).forEach(function searchanddestroy(name) {
    if (!(name in pkg.devDependencies)) return;
    try {
      if (!semver.eq(pkg.devDependencies[name], pkg.dependencies[name])) return;
    } catch (e) { return; }

    //
    // Only remove when we have an exact match on the version number.
    //
    delete pkg.devDependencies[name];
  });

  return pkg;
};

/**
 * Clean up any cache of data structures that we might have had laying around.
 * Making this instance ready for garbage collection.
 *
 * @returns {Boolean}
 * @public
 */
Shrinkwrap.prototype.destroy = function destroy() {
  this.registry = this.cache = null;

  return true;
};

/**
 * The various of locations where dependencies for a given module can be
 * defined.
 *
 * @type {Array}
 * @private
 */
Shrinkwrap.dependencies = [
  'dependencies',
  'devDependencies',
  'peerDependencies',
  'optionalDependencies'
];

//
// Expose the module interface.
//
module.exports = Shrinkwrap;
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2017-present, Wonday (@wonday.org)
 * All rights reserved.
 *
 * This source code is licensed under the MIT-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';
import React,{ Component, PropTypes } from 'react';
import {
    requireNativeComponent,
} from 'react-native';

const AliyunPushNative = require('react-native').NativeModules.AliyunPush;
const { NativeEventEmitter } = require('react-native');
const LocalEventEmitter =  new NativeEventEmitter(AliyunPushNative);

var listeners = {};

var id = 0;
var META = '__listener_id';

function getKey(listener,type){

    if (!listener.hasOwnProperty(META)){
        if (!Object.isExtensible(listener)) {
            return 'F';
        }
        Object.defineProperty(listener, META, {
            value: 'L' + type + ++id,
        });
    }
    return listener[META];

};

export default class AliyunPush {

    static getDeviceId = (callback) => {
        AliyunPushNative.getDeviceId(function(args) {
            callback(args);
        });
    }

    static getApplicationIconBadgeNumber = (callback) => {
        AliyunPushNative.getApplicationIconBadgeNumber(function(args) {
            callback(args);
        });
    }

    static setApplicationIconBadgeNumber = (num) => {
        AliyunPushNative.setApplicationIconBadgeNumber(num);
    }

    static bindAccount = (account) => {
        return AliyunPushNative.bindAccount(account);
    }

    static unbindAccount = () => {
        return AliyunPushNative.unbindAccount();
    }

    static bindTag = (target, tags, alias) => {
        return AliyunPushNative.bindTag(target, tags, alias);
    }

    static unbindTag = (target, tags, alias) => {
        return AliyunPushNative.unbindTag(target, tags, alias);
    }

    static listTags = (target) => {
        return AliyunPushNative.listTags(target);
    }

    static addAlias = (alias) => {
        return AliyunPushNative.addAlias(alias);
    }

    static removeAlias = (alias) => {
        return AliyunPushNative.removeAlias(alias);
    }

    static listAliases = () => {
        return AliyunPushNative.listAliases();
    }

    static addListener = (callback) => {

        AliyunPush._addListener(callback,"aliyunPushReceived");

    };

    static removeListener = (callback) => {

        AliyunPush._removeListener(callback, "aliyunPushReceived");

    };

    static _addListener = (callback,type) => {

        var key = getKey(callback,type);
        listeners[key] = LocalEventEmitter.addListener(type,
            (e) => {
                // convert json string to obj
                if (e.extraStr) {
                    let extras = JSON.parse(e.extraStr);
                    if (extras) {
                        if (extras.badge) {
                            let badgeNumber = parseInt(extras.badge);
                            if (!isNaN(badgeNumber)) {
                                AliyunPush.setApplicationIconBadgeNumber(badgeNumber);
                            }
                        }
                        e.extras = extras;
                    }
                    delete e.extraStr;
                    callback(e);
                } else {
                    callback(e);
                }

            });

    };

    static _removeListener = (callback,type) => {

        var key = getKey(callback,type);
        if (!listeners[key]) {
            return;
        }
        listeners[key].remove();
        listeners[key] = null;

    };
}
<commit_msg>remove PropTypes import<commit_after>/**
 * Copyright (c) 2017-present, Wonday (@wonday.org)
 * All rights reserved.
 *
 * This source code is licensed under the MIT-style license found in the
 * LICENSE file in the root directory of this source tree.
 */

'use strict';
import React,{ Component } from 'react';
import {
    requireNativeComponent,
} from 'react-native';

const AliyunPushNative = require('react-native').NativeModules.AliyunPush;
const { NativeEventEmitter } = require('react-native');
const LocalEventEmitter =  new NativeEventEmitter(AliyunPushNative);

var listeners = {};

var id = 0;
var META = '__listener_id';

function getKey(listener,type){

    if (!listener.hasOwnProperty(META)){
        if (!Object.isExtensible(listener)) {
            return 'F';
        }
        Object.defineProperty(listener, META, {
            value: 'L' + type + ++id,
        });
    }
    return listener[META];

};

export default class AliyunPush {

    static getDeviceId = (callback) => {
        AliyunPushNative.getDeviceId(function(args) {
            callback(args);
        });
    }

    static getApplicationIconBadgeNumber = (callback) => {
        AliyunPushNative.getApplicationIconBadgeNumber(function(args) {
            callback(args);
        });
    }

    static setApplicationIconBadgeNumber = (num) => {
        AliyunPushNative.setApplicationIconBadgeNumber(num);
    }

    static bindAccount = (account) => {
        return AliyunPushNative.bindAccount(account);
    }

    static unbindAccount = () => {
        return AliyunPushNative.unbindAccount();
    }

    static bindTag = (target, tags, alias) => {
        return AliyunPushNative.bindTag(target, tags, alias);
    }

    static unbindTag = (target, tags, alias) => {
        return AliyunPushNative.unbindTag(target, tags, alias);
    }

    static listTags = (target) => {
        return AliyunPushNative.listTags(target);
    }

    static addAlias = (alias) => {
        return AliyunPushNative.addAlias(alias);
    }

    static removeAlias = (alias) => {
        return AliyunPushNative.removeAlias(alias);
    }

    static listAliases = () => {
        return AliyunPushNative.listAliases();
    }

    static addListener = (callback) => {

        AliyunPush._addListener(callback,"aliyunPushReceived");

    };

    static removeListener = (callback) => {

        AliyunPush._removeListener(callback, "aliyunPushReceived");

    };

    static _addListener = (callback,type) => {

        var key = getKey(callback,type);
        listeners[key] = LocalEventEmitter.addListener(type,
            (e) => {
                // convert json string to obj
                if (e.extraStr) {
                    let extras = JSON.parse(e.extraStr);
                    if (extras) {
                        if (extras.badge) {
                            let badgeNumber = parseInt(extras.badge);
                            if (!isNaN(badgeNumber)) {
                                AliyunPush.setApplicationIconBadgeNumber(badgeNumber);
                            }
                        }
                        e.extras = extras;
                    }
                    delete e.extraStr;
                    callback(e);
                } else {
                    callback(e);
                }

            });

    };

    static _removeListener = (callback,type) => {

        var key = getKey(callback,type);
        if (!listeners[key]) {
            return;
        }
        listeners[key].remove();
        listeners[key] = null;

    };
}
<|endoftext|>


<commit_before>const purify = require('purify-css');
const path = require('path');
const ConcatSource = require('webpack-sources').ConcatSource;

module.exports = function PurifyPlugin(options) {
  // Store the user's options
  this.userOptions = options;
};

module.exports.prototype.apply = function apply(compiler) {
  // Keep a reference to self
  const self = this;

  // Bind the plugin into this compilation.
  compiler.plugin('this-compilation', (compilation) => {
    // webpack options
    const wpOptions = compilation.compiler.options;

    // Purify options
    self.purifyOptions = self.userOptions.purifyOptions || {
      minify: false,
      info: wpOptions.debug || false
    };
    self.purifyOptions.output = false;

    // An array of files to check.
    self.paths = self.userOptions.paths || [];

    // Convert possible string to an array
    if (typeof self.paths === 'string') {
      self.paths = [self.paths];
    }

    // Additional extensions to scan for. This is kept minimal.
    self.resolveExtensions = (
      self.userOptions.resolveExtensions || compiler.options.resolve.extensions
    );

    const files = self.paths;

    compilation.plugin('additional-assets', (cb) => {
      // Check only if there is one chunk and if paths are an array
      if (compilation.chunks.length === 1 || Array.isArray(self.paths)) {
        purifyCSS(
          // Look for additional files
          files.concat(
            searchAdditionalFiles(compilation.fileDependencies, self.resolveExtensions)
          ),
          compilation.assets
        ).forEach((o) => {
          compilation.assets[o.key] = o.value;
        });
      }

      cb();
    });
  });
};

function searchAdditionalFiles(fileDependencies, resolveExtensions) {
  return Object.keys(fileDependencies).map((key) => {
    const file = fileDependencies[key];
    const ext = path.extname(file);

    if (resolveExtensions.indexOf(ext) >= -1) {
      return file;
    }

    return null;
  }).filter(a => a);
}

function purifyCSS(files, assets, purifyOptions) {
  return Object.keys(assets).map((key) => {
    if (/\.css$/i.test(key)) {
      const asset = assets[key];
      const css = asset.source();
      const value = new ConcatSource();

      value.add(purify(files, css, purifyOptions));

      return { key, value };
    }

    return null;
  }).filter(a => a);
}
<commit_msg>Fix additional files check<commit_after>const purify = require('purify-css');
const path = require('path');
const ConcatSource = require('webpack-sources').ConcatSource;

module.exports = function PurifyPlugin(options) {
  // Store the user's options
  this.userOptions = options;
};

module.exports.prototype.apply = function apply(compiler) {
  // Keep a reference to self
  const self = this;

  // Bind the plugin into this compilation.
  compiler.plugin('this-compilation', (compilation) => {
    // webpack options
    const wpOptions = compilation.compiler.options;

    // Purify options
    self.purifyOptions = self.userOptions.purifyOptions || {
      minify: false,
      info: wpOptions.debug || false
    };
    self.purifyOptions.output = false;

    // An array of files to check.
    self.paths = self.userOptions.paths || [];

    // Convert possible string to an array
    if (typeof self.paths === 'string') {
      self.paths = [self.paths];
    }

    // Additional extensions to scan for. This is kept minimal.
    self.resolveExtensions = (
      self.userOptions.resolveExtensions || compiler.options.resolve.extensions
    );

    const files = self.paths;

    compilation.plugin('additional-assets', (cb) => {
      // Check only if there is one chunk and if paths are an array
      if (compilation.chunks.length === 1 || Array.isArray(self.paths)) {
        purifyCSS(
          // Look for additional files
          files.concat(
            searchAdditionalFiles(compilation.fileDependencies, self.resolveExtensions)
          ),
          compilation.assets
        ).forEach((o) => {
          compilation.assets[o.key] = o.value;
        });
      }

      cb();
    });
  });
};

function searchAdditionalFiles(fileDependencies, resolveExtensions) {
  return Object.keys(fileDependencies).map((key) => {
    const file = fileDependencies[key];
    const ext = path.extname(file);

    if (resolveExtensions.indexOf(ext) >= 0) {
      return file;
    }

    return null;
  }).filter(a => a);
}

function purifyCSS(files, assets, purifyOptions) {
  return Object.keys(assets).map((key) => {
    if (/\.css$/i.test(key)) {
      const asset = assets[key];
      const css = asset.source();
      const value = new ConcatSource();

      value.add(purify(files, css, purifyOptions));

      return { key, value };
    }

    return null;
  }).filter(a => a);
}
<|endoftext|>