UNPKG

3.92 kBPlain TextView Raw
1type PotentialError = Errlop | Error | ErrorCodeHolder | string
2interface ErrorCodeHolder {
3 exitCode?: string | number
4 errno?: string | number
5 code?: string | number
6}
7
8/** Only accept codes that are numbers, otherwise discard them */
9function parseCode(code: any): number | null {
10 const number = Number(code)
11 if (isNaN(number)) return null
12 return number
13}
14
15/** Fetch the code from the value */
16function fetchCode(value: any): string | number | null {
17 return (
18 value &&
19 (parseCode(value.exitCode) ||
20 parseCode(value.errno) ||
21 parseCode(value.code))
22 )
23}
24
25/** Prevent [a weird error on node version 4](https://github.com/bevry/errlop/issues/1) and below. */
26function isValid(value: any): boolean {
27 /* eslint no-use-before-define:0 */
28 return value instanceof Error || Errlop.isErrlop(value)
29}
30
31export default class Errlop extends Error {
32 /** Duck typing as node 4 and intanceof does not work for error extensions */
33 public klass: typeof Errlop
34
35 /**
36 * The parent error if it was provided.
37 * If a parent was provided, then use that, otherwise use the input's parent, if it exists.
38 */
39 public parent?: Errlop | Error | null
40
41 /** An array of all the ancestors. From parent, to grand parent, and so on. */
42 public ancestors: Array<Errlop | Error>
43
44 /**
45 * A numeric code to use for the exit status if desired by the consumer.
46 * It cycles through [input, this, ...ancestors] until it finds the first [exitCode, errno, code] that is valid.
47 */
48 public exitCode?: string | number
49
50 /**
51 * The stack for our instance alone, without any parents.
52 * If the input contained a stack, then use that.
53 */
54 public orphanStack: string
55
56 /**
57 * The stack which now contains the accumalated stacks of its ancestors.
58 * This is used instead of an alias like `fullStack` or the like, to ensure existing code that uses `err.stack` doesn't need to be changed to remain functional.
59 */
60 public stack: string
61
62 /**
63 * Syntatic sugar for Errlop class creation.
64 * Enables `Errlop.create(...args)` to achieve `new Errlop(...args)`
65 */
66 static create(
67 input: PotentialError,
68 parent: Errlop | Error | null = null
69 ): Errlop {
70 return new this(input, parent)
71 }
72
73 /**
74 * Create an instance of an error, using a message, as well as an optional parent.
75 * If the parent is provided, then the `fullStack` property will include its stack too
76 */
77 constructor(input: PotentialError, parent: Errlop | Error | null = null) {
78 if (!input) throw new Error('Attempted to create an Errlop without a input')
79
80 // Instantiate with the above
81 super((input as any).message || input)
82
83 // Apply
84 this.klass = Errlop
85 this.parent = parent || (input as Errlop).parent
86 this.ancestors = []
87 let ancestor = this.parent
88 while (ancestor) {
89 this.ancestors.push(ancestor)
90 ancestor = (ancestor as Errlop).parent
91 }
92
93 // this code must support node 0.8, as well as prevent a weird bug in node v4
94 // https://travis-ci.org/bevry/editions/jobs/408828147
95 let exitCode = fetchCode(input)
96 if (exitCode == null) exitCode = fetchCode(this)
97 for (
98 let index = 0;
99 index < this.ancestors.length && exitCode == null;
100 ++index
101 ) {
102 const error = this.ancestors[index]
103 if (isValid(error)) exitCode = fetchCode(error)
104 }
105
106 // Apply
107 if (exitCode != null) {
108 this.exitCode = exitCode
109 }
110 this.orphanStack = ((input as any).stack || (this as any).stack).toString()
111 this.stack = this.ancestors.reduce<string>(
112 (accumulator, error) =>
113 `${accumulator}\n↳ ${
114 (error as Errlop).orphanStack || (error as Error).stack || error
115 }`,
116 this.orphanStack
117 )
118 }
119
120 /** Check whether or not the value is an Errlop instance */
121 static isErrlop(value: any): boolean {
122 return value && (value instanceof this || value.klass === this)
123 }
124
125 /** Ensure that the value is an Errlop instance */
126 static ensure(value: any): Errlop {
127 return this.isErrlop(value) ? value : this.create(value)
128 }
129}