10
10
11
11
const astUtils = require ( "./utils/ast-utils" ) ;
12
12
13
- //----------------------------------------------------------------------
13
+ //------------------------------------------------------------------------------
14
+ // Types
15
+ //------------------------------------------------------------------------------
16
+
17
+ /** @typedef {import("estree").Node } ASTNode */
18
+
19
+ //------------------------------------------------------------------------------
14
20
// Helpers
15
- //----------------------------------------------------------------------
21
+ //------------------------------------------------------------------------------
16
22
17
23
/*
18
24
* This is an indicator of an error cause node, that is too complicated to be detected and fixed.
@@ -32,9 +38,11 @@ const BUILT_IN_ERROR_TYPES = new Set([
32
38
] ) ;
33
39
34
40
/**
35
- * Finds and returns the ASTNode that is used as the `cause` of the Error being thrown
41
+ * Finds and returns information about the `cause` property of an error being thrown.
36
42
* @param {ASTNode } throwStatement `ThrowStatement` to be checked.
37
- * @returns {ASTNode | UNKNOWN_CAUSE | null } The `cause` of `Error` being thrown, `null` if not set.
43
+ * @returns {{ value: ASTNode; multipleDefinitions: boolean; } | UNKNOWN_CAUSE | null }
44
+ * Information about the `cause` of the error being thrown, such as the value node and
45
+ * whether there are multiple definitions of `cause`. `null` if there is no `cause`.
38
46
*/
39
47
function getErrorCause ( throwStatement ) {
40
48
const throwExpression = throwStatement . argument ;
@@ -73,15 +81,21 @@ function getErrorCause(throwStatement) {
73
81
return UNKNOWN_CAUSE ;
74
82
}
75
83
76
- const causeProperty = errorOptions . properties . find (
84
+ const causeProperties = errorOptions . properties . filter (
77
85
prop =>
78
86
prop . type === "Property" &&
79
87
prop . key . type === "Identifier" &&
80
88
prop . key . name === "cause" &&
81
89
! prop . computed , // It is hard to accurately identify the value of computed props
82
90
) ;
83
91
84
- return causeProperty ? causeProperty . value : null ;
92
+ const causeProperty = causeProperties . at ( - 1 ) ;
93
+ return causeProperty
94
+ ? {
95
+ value : causeProperty . value ,
96
+ multipleDefinitions : causeProperties . length > 1 ,
97
+ }
98
+ : null ;
85
99
}
86
100
87
101
// Error options exist, but too complicated to be analyzed/fixed
@@ -290,14 +304,14 @@ module.exports = {
290
304
}
291
305
292
306
// Check if there is a cause attached to the new error
293
- const thrownErrorCause = getErrorCause ( throwStatement ) ;
307
+ const errorCauseInfo = getErrorCause ( throwStatement ) ;
294
308
295
- if ( thrownErrorCause === UNKNOWN_CAUSE ) {
309
+ if ( errorCauseInfo === UNKNOWN_CAUSE ) {
296
310
// Error options exist, but too complicated to be analyzed/fixed
297
311
return ;
298
312
}
299
313
300
- if ( thrownErrorCause === null ) {
314
+ if ( errorCauseInfo === null ) {
301
315
// If there is no `cause` attached to the error being thrown.
302
316
context . report ( {
303
317
messageId : "missingCause" ,
@@ -439,42 +453,52 @@ module.exports = {
439
453
return ;
440
454
}
441
455
442
- // If there is an attached cause, verify that is matches the caught error
456
+ const { value : thrownErrorCause } = errorCauseInfo ;
457
+
458
+ // If there is an attached cause, verify that it matches the caught error
443
459
if (
444
460
! (
445
461
thrownErrorCause . type === "Identifier" &&
446
462
thrownErrorCause . name === caughtError . name
447
463
)
448
464
) {
449
- context . report ( {
450
- messageId : "incorrectCause" ,
451
- node : thrownErrorCause ,
452
- suggest : [
453
- {
454
- messageId : "includeCause" ,
455
- fix ( fixer ) {
456
- /*
457
- * In case `cause` is attached using object property shorthand or as a method.
458
- * e.g. throw Error("fail", { cause });
459
- * throw Error("fail", { cause() { // do something } });
460
- */
461
- if (
462
- thrownErrorCause . parent . method ||
463
- thrownErrorCause . parent . shorthand
464
- ) {
465
+ const suggest = errorCauseInfo . multipleDefinitions
466
+ ? null // If there are multiple `cause` definitions, a suggestion could be confusing.
467
+ : [
468
+ {
469
+ messageId : "includeCause" ,
470
+ fix ( fixer ) {
471
+ /*
472
+ * In case `cause` is attached using object property shorthand or as a method or accessor.
473
+ * e.g. throw Error("fail", { cause });
474
+ * throw Error("fail", { cause() { doSomething(); } });
475
+ * throw Error("fail", { get cause() { return error; } });
476
+ */
477
+ if (
478
+ thrownErrorCause . parent
479
+ . method ||
480
+ thrownErrorCause . parent
481
+ . shorthand ||
482
+ thrownErrorCause . parent . kind !==
483
+ "init"
484
+ ) {
485
+ return fixer . replaceText (
486
+ thrownErrorCause . parent ,
487
+ `cause: ${ caughtError . name } ` ,
488
+ ) ;
489
+ }
490
+
465
491
return fixer . replaceText (
466
- thrownErrorCause . parent ,
467
- `cause: ${ caughtError . name } ` ,
492
+ thrownErrorCause ,
493
+ caughtError . name ,
468
494
) ;
469
- }
470
-
471
- return fixer . replaceText (
472
- thrownErrorCause ,
473
- caughtError . name ,
474
- ) ;
495
+ } ,
475
496
} ,
476
- } ,
477
- ] ,
497
+ ] ;
498
+ context . report ( {
499
+ messageId : "incorrectCause" ,
500
+ node : thrownErrorCause ,
501
+ suggest,
478
502
} ) ;
479
503
return ;
480
504
}
0 commit comments