-
Notifications
You must be signed in to change notification settings - Fork 4k
/
Copy patherrors.ts
145 lines (121 loc) · 3.56 KB
/
errors.ts
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
import { IConstruct } from 'constructs';
import { constructInfoFromConstruct } from './helpers-internal';
const CONSTRUCT_ERROR_SYMBOL = Symbol.for('@aws-cdk/core.SynthesisError');
const VALIDATION_ERROR_SYMBOL = Symbol.for('@aws-cdk/core.ValidationError');
/**
* Helper to check if an error is a SynthesisErrors
*/
export class Errors {
/**
* Test whether the given errors is a ConstructionError
*/
public static isConstructError(x: any): x is ConstructError {
return x !== null && typeof(x) === 'object' && CONSTRUCT_ERROR_SYMBOL in x;
}
/**
* Test whether the given error is a ValidationError
*/
public static isValidationError(x: any): x is ValidationError {
return Errors.isConstructError(x) && VALIDATION_ERROR_SYMBOL in x;
}
}
interface ConstructInfo {
readonly fqn: string;
readonly version: string;
}
/**
* Generic, abstract error that is thrown from the users app during app construction or synth.
*/
abstract class ConstructError extends Error {
#time: string;
#constructPath?: string;
#constructInfo?: ConstructInfo;
/**
* The time the error was thrown.
*/
public get time(): string {
return this.#time;
}
/**
* The level. Always `'error'`.
*/
public get level(): 'error' {
return 'error';
}
/**
* The type of the error.
*/
public abstract get type(): string;
/**
* The path of the construct this error is thrown from, if available.
*/
public get constructPath(): string | undefined {
return this.#constructPath;
}
/**
* Information on the construct this error is thrown from, if available.
*/
public get constructInfo(): ConstructInfo | undefined {
return this.#constructInfo;
}
constructor(msg: string, scope?: IConstruct) {
super(msg);
Object.setPrototypeOf(this, ConstructError.prototype);
Object.defineProperty(this, CONSTRUCT_ERROR_SYMBOL, { value: true });
this.name = new.target.name;
this.#time = new Date().toISOString();
this.#constructPath = scope?.node.path;
if (scope) {
Error.captureStackTrace(this, scope.constructor);
try {
this.#constructInfo = scope ? constructInfoFromConstruct(scope) : undefined;
} catch (_) {
// we don't want to fail if construct info is not available
}
}
const stack = [
`${this.name}: ${this.message}`,
];
if (this.constructInfo) {
stack.push(`in ${this.constructInfo?.fqn} at [${this.constructPath}]`);
} else {
stack.push(`in [${this.constructPath}]`);
}
if (this.stack) {
stack.push(this.stack);
}
this.stack = this.constructStack(this.stack);
}
/**
* Helper message to clean-up the stack and amend with construct information.
*/
private constructStack(prev?: string) {
const indent = ' '.repeat(4);
const stack = [
`${this.name}: ${this.message}`,
];
if (this.constructInfo) {
stack.push(`${indent}at path [${this.constructPath}] in ${this.constructInfo?.fqn}`);
} else {
stack.push(`${indent}at path [${this.constructPath}]`);
}
if (prev) {
stack.push('');
stack.push(...prev.split('\n').slice(1));
}
return stack.join('\n');
}
}
/**
* An Error that is thrown when a construct has validation errors.
*/
export class ValidationError extends ConstructError {
public get type(): 'validation' {
return 'validation';
}
constructor(msg: string, scope: IConstruct) {
super(msg, scope);
Object.setPrototypeOf(this, ValidationError.prototype);
Object.defineProperty(this, VALIDATION_ERROR_SYMBOL, { value: true });
}
}