summaryrefslogtreecommitdiff
path: root/node_modules/gulp-tar/node_modules/gulp-util/lib/PluginError.js
blob: d60159ab1ed77e3d166ff23f2d6e6cd384343691 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
var util = require('util');
var arrayDiffer = require('array-differ');
var arrayUniq = require('array-uniq');
var chalk = require('chalk');
var objectAssign = require('object-assign');

var nonEnumberableProperties = ['name', 'message', 'stack'];
var propertiesNotToDisplay = nonEnumberableProperties.concat(['plugin', 'showStack', 'showProperties', '__safety', '_stack']);

// wow what a clusterfuck
var parseOptions = function(plugin, message, opt) {
  opt = opt || {};
  if (typeof plugin === 'object') {
    opt = plugin;
  } else {
    if (message instanceof Error) {
      opt.error = message;
    } else if (typeof message === 'object') {
      opt = message;
    } else {
      opt.message = message;
    }
    opt.plugin = plugin;
  }

  return objectAssign({
    showStack: false,
    showProperties: true
  }, opt);
};

function PluginError(plugin, message, opt) {
  if (!(this instanceof PluginError)) throw new Error('Call PluginError using new');

  Error.call(this);

  var options = parseOptions(plugin, message, opt);
  var self = this;

  // if options has an error, grab details from it
  if (options.error) {
    // These properties are not enumerable, so we have to add them explicitly.
    arrayUniq(Object.keys(options.error).concat(nonEnumberableProperties))
      .forEach(function(prop) {
        self[prop] = options.error[prop];
      });
  }

  var properties = ['name', 'message', 'fileName', 'lineNumber', 'stack', 'showStack', 'showProperties', 'plugin'];

  // options object can override
  properties.forEach(function(prop) {
    if (prop in options) this[prop] = options[prop];
  }, this);

  // defaults
  if (!this.name) this.name = 'Error';

  if (!this.stack) {
    // Error.captureStackTrace appends a stack property which relies on the toString method of the object it is applied to.
    // Since we are using our own toString method which controls when to display the stack trace if we don't go through this
    // safety object, then we'll get stack overflow problems.
    var safety = {
      toString: function() {
        return this._messageWithDetails() + '\nStack:';
      }.bind(this)
    };
    Error.captureStackTrace(safety, arguments.callee || this.constructor);
    this.__safety = safety;
  }

  if (!this.plugin) throw new Error('Missing plugin name');
  if (!this.message) throw new Error('Missing error message');
}

util.inherits(PluginError, Error);

PluginError.prototype._messageWithDetails = function() {
  var messageWithDetails = 'Message:\n    ' + this.message;
  var details = this._messageDetails();

  if (details !== '') {
    messageWithDetails += '\n' + details;
  }

  return messageWithDetails;
};

PluginError.prototype._messageDetails = function() {
  if (!this.showProperties) {
    return '';
  }

  var properties = arrayDiffer(Object.keys(this), propertiesNotToDisplay);

  if (properties.length === 0) {
    return '';
  }

  var self = this;
  properties = properties.map(function stringifyProperty(prop) {
    return '    ' + prop + ': ' + self[prop];
  });

  return 'Details:\n' + properties.join('\n');
};

PluginError.prototype.toString = function () {
  var sig = chalk.red(this.name) + ' in plugin \'' + chalk.cyan(this.plugin) + '\'';
  var detailsWithStack = function(stack) {
    return this._messageWithDetails() + '\nStack:\n' + stack;
  }.bind(this);

  var msg;
  if (this.showStack) {
    if (this.__safety) { // There is no wrapped error, use the stack captured in the PluginError ctor
      msg = this.__safety.stack;
    } else if (this._stack) {
      msg = detailsWithStack(this._stack);
    } else { // Stack from wrapped error
      msg = detailsWithStack(this.stack);
    }
  } else {
    msg = this._messageWithDetails();
  }

  return sig + '\n' + msg;
};

module.exports = PluginError;