1
- interface ClideErrorOptions {
2
- cause ?: unknown ;
1
+ export interface ClideErrorOptions extends ErrorOptions {
2
+ /**
3
+ * A custom prefix to use in place of {@linkcode DriftError.prefix}.
4
+ */
5
+ prefix ?: string ;
6
+
7
+ /**
8
+ * A custom name to use in place of {@linkcode DriftError.name}.
9
+ */
10
+ name ?: string ;
3
11
}
4
12
5
13
/**
6
14
* An error thrown by the CLI engine.
15
+ *
16
+ * This error is designed to ensure clean stack trace formatting even when
17
+ * minified and can be extended to create other error types with the same
18
+ * behavior.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * class FooCliError extends ClideError {
23
+ * constructor(message: string, options?: ErrorOptions) {
24
+ * super(message, {
25
+ * ...options,
26
+ * prefix: "🚨 ",
27
+ * name: "Foo CLI Error",
28
+ * });
29
+ * }
30
+ * }
31
+ *
32
+ * throw new FooCliError("Something went wrong");
33
+ * // 🚨 Foo CLI Error: Something went wrong
34
+ * // at ...
35
+ * ```
36
+ *
7
37
* @group Errors
8
38
*/
9
39
export class ClideError extends Error {
10
40
static prefix = '✖ ' ;
41
+ static name = 'CLI Error' as const ;
11
42
12
43
constructor ( error : any , options ?: ClideErrorOptions ) {
13
44
// Coerce the error to a string, or throw the original error if unable.
@@ -19,7 +50,7 @@ export class ClideError extends Error {
19
50
}
20
51
21
52
super ( message ) ;
22
- this . name = 'CLI Error' ;
53
+ this . name = options ?. name ?? ClideError . name ;
23
54
24
55
// Minification can mangle the stack traces of custom errors by obfuscating
25
56
// the class name and including large chunks of minified code in the output.
@@ -43,28 +74,38 @@ export class ClideError extends Error {
43
74
customName = error . constructor . name ;
44
75
}
45
76
46
- // Doing this in constructor prevents the need to add custom properties to
47
- // the prototype, which would be displayed in the stack trace. The getter
77
+ // Doing this in the constructor prevents the need to add custom properties
78
+ // to the prototype, which would be displayed in the stack trace. The getter
48
79
// ensures the name and message are up-to-date when accessed (e.g., after
49
80
// subclassing and changing the name).
50
81
Object . defineProperty ( this , 'stack' , {
51
82
get ( ) : string {
52
- let stack = `${ ClideError . prefix } ${ this . name } ` ;
83
+ let stack = `${ options ?. prefix ?? ClideError . prefix } ${ this . name } ` ;
53
84
54
85
if ( customName ) {
55
86
stack += ` [${ customName } ]` ;
56
87
}
57
88
58
89
if ( this . message ) {
59
- stack += `: ${ this . message } ` ;
90
+ stack += `: ${ this . message . replaceAll ( '\n' , '\n ' ) } ` ;
60
91
}
61
92
62
93
if ( stackTarget . stack ) {
63
- stack += `\n${ stackTarget . stack . replace ( / ^ .* \n / , '' ) } ` ;
94
+ const stackLines = stackTarget . stack
95
+ . replace ( this . message , '' )
96
+ . split ( '\n' )
97
+ . slice ( 1 )
98
+ . join ( '\n' ) ;
99
+ if ( stackLines ) {
100
+ stack += `\n${ stackLines } ` ;
101
+ }
64
102
}
65
103
66
104
if ( cause ) {
67
- stack += `\n Caused by: ${ cause . stack || cause } ` ;
105
+ stack += `\nCaused by: ${ cause . stack || cause } ` . replaceAll (
106
+ '\n' ,
107
+ '\n ' ,
108
+ ) ;
68
109
}
69
110
70
111
return stack . trim ( ) ;
0 commit comments