makeerror.js
 1  var tmpl = require('tmpl')
 2  
 3  module.exports = makeError
 4  
 5  function BaseError() {}
 6  BaseError.prototype = new Error()
 7  BaseError.prototype.toString = function() {
 8    return this.message
 9  }
10  
11  
12  /**
13   * Makes an Error function with the signature:
14   *
15   *   function(message, data)
16   *
17   * You'll typically do something like:
18   *
19   *   var UnknownFileTypeError = makeError(
20   *     'UnknownFileTypeError',
21   *     'The specified type is not known.'
22   *   )
23   *   var er = UnknownFileTypeError()
24   *
25   * `er` will have a prototype chain that ensures:
26   *
27   *   er instanceof Error
28   *   er instanceof UnknownFileTypeError
29   *
30   * You can also do `var er = new UnknownFileTypeError()` if you really like the
31   * `new` keyword.
32   *
33   * @param String  The name of the error.
34   * @param String  The default message string.
35   * @param Object  The default data object, merged with per instance data.
36   */
37  function makeError(name, defaultMessage, defaultData) {
38    defaultMessage = tmpl(defaultMessage || '')
39    defaultData = defaultData || {}
40    if (defaultData.proto && !(defaultData.proto instanceof BaseError))
41      throw new Error('The custom "proto" must be an Error created via makeError')
42  
43    var CustomError = function(message, data) {
44      if (!(this instanceof CustomError)) return new CustomError(message, data)
45  
46      if (typeof message !== 'string' && !data) {
47        data = message
48        message = null
49      }
50  
51      this.name = name
52      this.data = data || defaultData
53  
54      if (typeof message === 'string') {
55        this.message = tmpl(message, this.data)
56      } else {
57        this.message = defaultMessage(this.data)
58      }
59  
60      var er = new Error()
61      this.stack = er.stack
62      if (this.stack) {
63        // remove TWO stack level:
64        if (typeof Components !== 'undefined') {
65          // Mozilla:
66          this.stack = this.stack.substring(this.stack.indexOf('\n') + 2)
67        } else if (typeof chrome !== 'undefined' || typeof process !== 'undefined') {
68          // Google Chrome/Node.js:
69          this.stack = this.stack.replace(/\n[^\n]*/, '')
70          this.stack = this.stack.replace(/\n[^\n]*/, '')
71          this.stack = (
72            this.name +
73            (this.message ? (': ' + this.message) : '') +
74            this.stack.substring(5)
75          )
76        }
77      }
78  
79      if ('fileName' in er) this.fileName = er.fileName
80      if ('lineNumber' in er) this.lineNumber = er.lineNumber
81    }
82  
83    CustomError.prototype = defaultData.proto || new BaseError()
84    delete defaultData.proto
85  
86    return CustomError
87  }