1 var util = require('util');
2 var arrayDiffer = require('array-differ');
3 var arrayUniq = require('array-uniq');
4 var chalk = require('chalk');
5 var objectAssign = require('object-assign');
7 var nonEnumberableProperties = ['name', 'message', 'stack'];
8 var propertiesNotToDisplay = nonEnumberableProperties.concat(['plugin', 'showStack', 'showProperties', '__safety', '_stack']);
10 // wow what a clusterfuck
11 var parseOptions = function(plugin, message, opt) {
13 if (typeof plugin === 'object') {
16 if (message instanceof Error) {
18 } else if (typeof message === 'object') {
21 opt.message = message;
32 function PluginError(plugin, message, opt) {
33 if (!(this instanceof PluginError)) throw new Error('Call PluginError using new');
37 var options = parseOptions(plugin, message, opt);
40 // if options has an error, grab details from it
42 // These properties are not enumerable, so we have to add them explicitly.
43 arrayUniq(Object.keys(options.error).concat(nonEnumberableProperties))
44 .forEach(function(prop) {
45 self[prop] = options.error[prop];
49 var properties = ['name', 'message', 'fileName', 'lineNumber', 'stack', 'showStack', 'showProperties', 'plugin'];
51 // options object can override
52 properties.forEach(function(prop) {
53 if (prop in options) this[prop] = options[prop];
57 if (!this.name) this.name = 'Error';
60 // Error.captureStackTrace appends a stack property which relies on the toString method of the object it is applied to.
61 // Since we are using our own toString method which controls when to display the stack trace if we don't go through this
62 // safety object, then we'll get stack overflow problems.
64 toString: function() {
65 return this._messageWithDetails() + '\nStack:';
68 Error.captureStackTrace(safety, arguments.callee || this.constructor);
69 this.__safety = safety;
72 if (!this.plugin) throw new Error('Missing plugin name');
73 if (!this.message) throw new Error('Missing error message');
76 util.inherits(PluginError, Error);
78 PluginError.prototype._messageWithDetails = function() {
79 var messageWithDetails = 'Message:\n ' + this.message;
80 var details = this._messageDetails();
83 messageWithDetails += '\n' + details;
86 return messageWithDetails;
89 PluginError.prototype._messageDetails = function() {
90 if (!this.showProperties) {
94 var properties = arrayDiffer(Object.keys(this), propertiesNotToDisplay);
96 if (properties.length === 0) {
101 properties = properties.map(function stringifyProperty(prop) {
102 return ' ' + prop + ': ' + self[prop];
105 return 'Details:\n' + properties.join('\n');
108 PluginError.prototype.toString = function () {
109 var sig = chalk.red(this.name) + ' in plugin \'' + chalk.cyan(this.plugin) + '\'';
110 var detailsWithStack = function(stack) {
111 return this._messageWithDetails() + '\nStack:\n' + stack;
115 if (this.showStack) {
116 if (this.__safety) { // There is no wrapped error, use the stack captured in the PluginError ctor
117 msg = this.__safety.stack;
118 } else if (this._stack) {
119 msg = detailsWithStack(this._stack);
120 } else { // Stack from wrapped error
121 msg = detailsWithStack(this.stack);
124 msg = this._messageWithDetails();
127 return sig + '\n' + msg;
130 module.exports = PluginError;