eslint-plugin-unicorn 69.0.0 → 71.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +26 -25
- package/readme.md +15 -1
- package/rules/ast/call-or-new-expression.js +2 -8
- package/rules/ast/is-member-expression.js +2 -8
- package/rules/comment-content.js +4 -4
- package/rules/consistent-boolean-name.js +727 -42
- package/rules/consistent-compound-words.js +1 -2
- package/rules/consistent-json-file-read.js +2 -5
- package/rules/dom-node-dataset.js +3 -7
- package/rules/escape-case.js +4 -4
- package/rules/expiring-todo-comments.js +25 -2
- package/rules/explicit-length-check.js +3 -3
- package/rules/filename-case.js +4 -4
- package/rules/fix/{add-parenthesizes-to-return-or-throw-expression.js → add-parentheses-to-return-or-throw-expression.js} +1 -1
- package/rules/fix/index.js +1 -1
- package/rules/fix/remove-argument.js +34 -8
- package/rules/fix/remove-statement.js +1 -3
- package/rules/fix/switch-new-expression-to-call-expression.js +2 -2
- package/rules/id-match.js +2 -2
- package/rules/index.js +10 -0
- package/rules/isolated-functions.js +203 -19
- package/rules/name-replacements.js +1 -2
- package/rules/no-anonymous-default-export.js +1 -3
- package/rules/no-array-callback-reference.js +2 -10
- package/rules/no-async-promise-finally.js +111 -0
- package/rules/no-chained-comparison.js +1 -1
- package/rules/no-collection-bracket-access.js +27 -4
- package/rules/no-computed-property-existence-check.js +1 -4
- package/rules/no-declarations-before-early-exit.js +6 -5
- package/rules/no-error-property-assignment.js +3 -3
- package/rules/no-for-loop.js +4 -4
- package/rules/no-keyword-prefix.js +2 -2
- package/rules/no-manually-wrapped-comments.js +5 -1
- package/rules/no-mismatched-map-key.js +13 -20
- package/rules/no-named-default.js +2 -5
- package/rules/no-negated-array-predicate.js +6 -13
- package/rules/no-negated-comparison.js +31 -71
- package/rules/no-negated-condition.js +2 -2
- package/rules/no-negation-in-equality-check.js +4 -7
- package/rules/no-non-function-verb-prefix.js +25 -2
- package/rules/no-process-exit.js +6 -6
- package/rules/no-return-array-push.js +9 -2
- package/rules/no-static-only-class.js +2 -8
- package/rules/no-thenable.js +9 -15
- package/rules/no-typeof-undefined.js +2 -2
- package/rules/no-undeclared-class-members.js +1 -5
- package/rules/no-unnecessary-array-flat-map.js +308 -0
- package/rules/no-unnecessary-await.js +2 -2
- package/rules/no-unnecessary-fetch-options.js +624 -0
- package/rules/no-unnecessary-global-this.js +5 -9
- package/rules/no-unsafe-promise-all-settled-values.js +865 -0
- package/rules/no-unused-properties.js +0 -1
- package/rules/no-useless-coercion.js +1 -2
- package/rules/no-useless-else.js +32 -1
- package/rules/no-useless-iterator-to-array.js +42 -16
- package/rules/no-useless-spread.js +2 -2
- package/rules/no-useless-undefined.js +2 -2
- package/rules/prefer-abort-signal-any.js +1391 -0
- package/rules/prefer-array-find.js +2 -5
- package/rules/prefer-array-flat-map.js +6 -11
- package/rules/prefer-array-from-range.js +98 -0
- package/rules/prefer-bigint-literals.js +1 -0
- package/rules/prefer-block-statement-over-iife.js +159 -0
- package/rules/prefer-boolean-return.js +20 -3
- package/rules/prefer-continue.js +6 -2
- package/rules/prefer-default-parameters.js +2 -2
- package/rules/prefer-early-return.js +3 -1
- package/rules/prefer-else-if.js +5 -9
- package/rules/prefer-group-by.js +576 -0
- package/rules/prefer-has-check.js +6 -3
- package/rules/prefer-https.js +3 -3
- package/rules/prefer-identifier-import-export-specifiers.js +1 -3
- package/rules/prefer-iterator-helpers.js +196 -0
- package/rules/prefer-minimal-ternary.js +4 -4
- package/rules/prefer-number-coercion.js +1 -1
- package/rules/prefer-object-define-properties.js +1 -3
- package/rules/prefer-object-iterable-methods.js +57 -14
- package/rules/prefer-observer-apis.js +2 -5
- package/rules/prefer-private-class-fields.js +1 -3
- package/rules/prefer-simple-sort-comparator.js +6 -6
- package/rules/prefer-simplified-conditions.js +584 -0
- package/rules/prefer-single-object-destructuring.js +2 -5
- package/rules/prefer-single-replace.js +2 -2
- package/rules/prefer-spread.js +25 -32
- package/rules/prefer-string-pad-start-end.js +1 -6
- package/rules/prefer-string-raw.js +2 -2
- package/rules/prefer-string-repeat.js +16 -9
- package/rules/prefer-string-replace-all.js +1 -1
- package/rules/prefer-ternary.js +5 -5
- package/rules/prefer-toggle-attribute.js +3 -3
- package/rules/prefer-top-level-await.js +6 -1
- package/rules/prefer-uint8array-base64.js +3 -1
- package/rules/require-proxy-trap-boolean-return.js +2 -2
- package/rules/rule/to-eslint-create.js +1 -2
- package/rules/rule/to-eslint-listener.js +1 -3
- package/rules/rule/to-eslint-problem.js +1 -3
- package/rules/rule/to-eslint-rule-fixer.js +1 -3
- package/rules/rule/to-eslint-rule.js +1 -3
- package/rules/rule/to-eslint-rules.js +1 -3
- package/rules/rule/unicorn-context.js +1 -3
- package/rules/rule/unicorn-listeners.js +1 -3
- package/rules/shared/array-range.js +66 -0
- package/rules/shared/iterator-helpers.js +43 -1
- package/rules/shared/no-array-mutate-rule.js +1 -3
- package/rules/shared/regexp-escape.js +4 -4
- package/rules/utils/can-tokens-be-adjacent.js +95 -0
- package/rules/utils/comparison.js +65 -0
- package/rules/utils/escape-string.js +2 -8
- package/rules/utils/get-available-variable-name.js +2 -13
- package/rules/utils/get-precedence.js +112 -0
- package/rules/utils/get-token-store.js +1 -3
- package/rules/utils/global-reference-tracker.js +3 -6
- package/rules/utils/index.js +4 -0
- package/rules/utils/is-boolean.js +103 -30
- package/rules/utils/is-identifier-name.js +5 -0
- package/rules/utils/is-promise-type.js +1 -3
- package/rules/utils/is-react-hook-name.js +5 -0
- package/rules/utils/parentheses/get-parent-syntax-opening-parenthesis.js +1 -3
- package/rules/utils/parentheses/iterate-surrounding-parentheses.js +1 -1
- package/rules/utils/should-add-parentheses-to-await-expression-argument.js +4 -9
- package/rules/utils/should-add-parentheses-to-call-expression-callee.js +6 -12
- package/rules/utils/should-add-parentheses-to-unary-expression.js +7 -14
- package/rules/utils/to-location.js +1 -3
- package/rules/utils/track-branch-exits.js +8 -7
|
@@ -0,0 +1,865 @@
|
|
|
1
|
+
import {findVariable, getPropertyName} from '@eslint-community/eslint-utils';
|
|
2
|
+
import {isMethodCall} from './ast/index.js';
|
|
3
|
+
|
|
4
|
+
const MESSAGE_ID = 'no-unsafe-promise-all-settled-values';
|
|
5
|
+
|
|
6
|
+
const messages = {
|
|
7
|
+
[MESSAGE_ID]: 'Do not read `.value` from `Promise.allSettled()` results without checking that the result is fulfilled.',
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const unsafePromiseSettledResultTypeNames = new Set([
|
|
11
|
+
'PromiseSettledResult',
|
|
12
|
+
'PromiseRejectedResult',
|
|
13
|
+
]);
|
|
14
|
+
|
|
15
|
+
const promiseFulfilledResultTypeNames = new Set([
|
|
16
|
+
'PromiseFulfilledResult',
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
const transparentExpressionTypes = new Set([
|
|
20
|
+
'ChainExpression',
|
|
21
|
+
'ParenthesizedExpression',
|
|
22
|
+
'TSAsExpression',
|
|
23
|
+
'TSSatisfiesExpression',
|
|
24
|
+
'TSNonNullExpression',
|
|
25
|
+
'TSTypeAssertion',
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const isFunction = node =>
|
|
29
|
+
node?.type === 'ArrowFunctionExpression'
|
|
30
|
+
|| node?.type === 'FunctionExpression'
|
|
31
|
+
|| node?.type === 'FunctionDeclaration';
|
|
32
|
+
|
|
33
|
+
const unwrapExpression = node => {
|
|
34
|
+
while (node && transparentExpressionTypes.has(node.type)) {
|
|
35
|
+
node = node.expression;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return node;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const isPromiseAllSettledCall = node =>
|
|
42
|
+
isMethodCall(unwrapExpression(node), {
|
|
43
|
+
object: 'Promise',
|
|
44
|
+
method: 'allSettled',
|
|
45
|
+
argumentsLength: 1,
|
|
46
|
+
optionalCall: false,
|
|
47
|
+
optionalMember: false,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const isAwaitedPromiseAllSettledCall = node => {
|
|
51
|
+
node = unwrapExpression(node);
|
|
52
|
+
return node?.type === 'AwaitExpression' && isPromiseAllSettledCall(node.argument);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const isReturnValue = node =>
|
|
56
|
+
node?.parent.type === 'ReturnStatement'
|
|
57
|
+
&& node.parent.argument === node;
|
|
58
|
+
|
|
59
|
+
const isFilterCall = node =>
|
|
60
|
+
isMethodCall(unwrapExpression(node), {
|
|
61
|
+
method: 'filter',
|
|
62
|
+
minimumArguments: 1,
|
|
63
|
+
maximumArguments: 2,
|
|
64
|
+
optionalCall: false,
|
|
65
|
+
optionalMember: false,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
function getTypeName(typeName) {
|
|
69
|
+
if (typeName.type === 'Identifier') {
|
|
70
|
+
return typeName.name;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (typeName.type === 'TSQualifiedName') {
|
|
74
|
+
return getTypeName(typeName.right);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getArrayElementTypeAnnotation(node) {
|
|
79
|
+
node = node?.type === 'TSTypeAnnotation' ? node.typeAnnotation : node;
|
|
80
|
+
|
|
81
|
+
while (node?.type === 'TSParenthesizedType' || node?.type === 'TSTypeOperator') {
|
|
82
|
+
node = node.typeAnnotation;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (node?.type === 'TSArrayType') {
|
|
86
|
+
return node.elementType;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (
|
|
90
|
+
node?.type === 'TSTypeReference'
|
|
91
|
+
&& node.typeName.type === 'Identifier'
|
|
92
|
+
&& (node.typeName.name === 'Array' || node.typeName.name === 'ReadonlyArray')
|
|
93
|
+
) {
|
|
94
|
+
return node.typeArguments?.params[0] ?? node.typeParameters?.params[0];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isUnsafePromiseSettledResultTypeAnnotation(node) {
|
|
99
|
+
if (node?.type === 'TSTypeReference') {
|
|
100
|
+
const typeName = getTypeName(node.typeName);
|
|
101
|
+
return unsafePromiseSettledResultTypeNames.has(typeName);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (node?.type === 'TSUnionType') {
|
|
105
|
+
return node.types.some(type => isUnsafePromiseSettledResultTypeAnnotation(type));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function isUnsafePromiseSettledResultArrayTypeAnnotation(node) {
|
|
112
|
+
return isUnsafePromiseSettledResultTypeAnnotation(getArrayElementTypeAnnotation(node));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isPromiseFulfilledResultTypeAnnotation(node) {
|
|
116
|
+
if (node?.type === 'TSTypeReference') {
|
|
117
|
+
const typeName = getTypeName(node.typeName);
|
|
118
|
+
return promiseFulfilledResultTypeNames.has(typeName);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (node?.type === 'TSUnionType') {
|
|
122
|
+
return node.types.every(type => isPromiseFulfilledResultTypeAnnotation(type));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isPromiseFulfilledResultArrayTypeAnnotation(node) {
|
|
129
|
+
return isPromiseFulfilledResultTypeAnnotation(getArrayElementTypeAnnotation(node));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function isUnsafePromiseSettledResultType(type) {
|
|
133
|
+
if (type.isUnion()) {
|
|
134
|
+
return type.types.some(type => isUnsafePromiseSettledResultType(type));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const symbol = type.aliasSymbol ?? type.getSymbol();
|
|
138
|
+
return unsafePromiseSettledResultTypeNames.has(symbol?.getName());
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function isPromiseFulfilledResultType(type) {
|
|
142
|
+
if (type.isUnion()) {
|
|
143
|
+
return type.types.every(type => isPromiseFulfilledResultType(type));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const symbol = type.aliasSymbol ?? type.getSymbol();
|
|
147
|
+
return promiseFulfilledResultTypeNames.has(symbol?.getName());
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getArrayElementType(type, checker) {
|
|
151
|
+
type = checker.getNonNullableType(type);
|
|
152
|
+
|
|
153
|
+
if (type.isUnion()) {
|
|
154
|
+
const elementTypes = type.types.map(type => getArrayElementType(type, checker));
|
|
155
|
+
return elementTypes.every(Boolean) ? checker.getUnionType(elementTypes) : undefined;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!(checker.isArrayType(type) || checker.isTupleType(type))) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return checker.getTypeArguments(type)[0];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function getTypeInformation(node, context) {
|
|
166
|
+
const {parserServices} = context.sourceCode;
|
|
167
|
+
if (!parserServices?.program) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
return {
|
|
173
|
+
checker: parserServices.program.getTypeChecker(),
|
|
174
|
+
type: parserServices.getTypeAtLocation(node),
|
|
175
|
+
};
|
|
176
|
+
} catch {}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function isPromiseSettledResultArrayFromTypeInformation(node, context) {
|
|
180
|
+
const typeInformation = getTypeInformation(node, context);
|
|
181
|
+
if (!typeInformation) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const {type, checker} = typeInformation;
|
|
186
|
+
const elementType = getArrayElementType(type, checker);
|
|
187
|
+
return Boolean(elementType && isUnsafePromiseSettledResultType(elementType));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function isPromiseFulfilledResultFromTypeInformation(node, context) {
|
|
191
|
+
const typeInformation = getTypeInformation(node, context);
|
|
192
|
+
if (!typeInformation) {
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const {type} = typeInformation;
|
|
197
|
+
return isPromiseFulfilledResultType(type);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function isPromiseFulfilledResultArrayFromTypeInformation(node, context) {
|
|
201
|
+
const typeInformation = getTypeInformation(node, context);
|
|
202
|
+
if (!typeInformation) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const {type, checker} = typeInformation;
|
|
207
|
+
const elementType = getArrayElementType(type, checker);
|
|
208
|
+
return Boolean(elementType && isPromiseFulfilledResultType(elementType));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const isTypeAssertion = node =>
|
|
212
|
+
node?.type === 'TSAsExpression'
|
|
213
|
+
|| node?.type === 'TSTypeAssertion';
|
|
214
|
+
|
|
215
|
+
const isPromiseSettledResultArrayTypeAssertion = node =>
|
|
216
|
+
isTypeAssertion(node)
|
|
217
|
+
&& isUnsafePromiseSettledResultArrayTypeAnnotation(node.typeAnnotation);
|
|
218
|
+
|
|
219
|
+
function isPromiseSettledResultVariable(node, context, visitedVariables) {
|
|
220
|
+
const variable = findVariable(context.sourceCode.getScope(node), node);
|
|
221
|
+
if (
|
|
222
|
+
!variable
|
|
223
|
+
|| visitedVariables.has(variable)
|
|
224
|
+
|| variable.defs.length !== 1
|
|
225
|
+
) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
visitedVariables.add(variable);
|
|
230
|
+
|
|
231
|
+
const [definition] = variable.defs;
|
|
232
|
+
const isPromiseSettledResult = Boolean(
|
|
233
|
+
isUnsafePromiseSettledResultArrayTypeAnnotation(definition.name?.typeAnnotation)
|
|
234
|
+
|| (
|
|
235
|
+
definition.type === 'Variable'
|
|
236
|
+
&& definition.parent.kind === 'const'
|
|
237
|
+
&& definition.node.id === definition.name
|
|
238
|
+
&& definition.node.init
|
|
239
|
+
&& isPromiseSettledResultArray(definition.node.init, context, visitedVariables)
|
|
240
|
+
),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
visitedVariables.delete(variable);
|
|
244
|
+
|
|
245
|
+
return isPromiseSettledResult;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function isPromiseFulfilledResultVariable(node, context, visitedVariables) {
|
|
249
|
+
const variable = findVariable(context.sourceCode.getScope(node), node);
|
|
250
|
+
if (
|
|
251
|
+
!variable
|
|
252
|
+
|| visitedVariables.has(variable)
|
|
253
|
+
|| variable.defs.length !== 1
|
|
254
|
+
) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
visitedVariables.add(variable);
|
|
259
|
+
|
|
260
|
+
const [definition] = variable.defs;
|
|
261
|
+
const isPromiseFulfilledResult = Boolean(
|
|
262
|
+
isPromiseFulfilledResultArrayTypeAnnotation(definition.name?.typeAnnotation)
|
|
263
|
+
|| (
|
|
264
|
+
definition.type === 'Variable'
|
|
265
|
+
&& definition.parent.kind === 'const'
|
|
266
|
+
&& definition.node.id === definition.name
|
|
267
|
+
&& definition.node.init
|
|
268
|
+
&& isKnownFulfilledResultArray(definition.node.init, context, visitedVariables)
|
|
269
|
+
),
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
visitedVariables.delete(variable);
|
|
273
|
+
|
|
274
|
+
return isPromiseFulfilledResult;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function isPromiseSettledResultArray(node, context, visitedVariables = new Set()) {
|
|
278
|
+
if (isPromiseSettledResultArrayTypeAssertion(node)) {
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
node = unwrapExpression(node);
|
|
283
|
+
|
|
284
|
+
if (!node) {
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (isAwaitedPromiseAllSettledCall(node)) {
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (isUnsafePromiseSettledResultArrayTypeAnnotation(node.typeAnnotation) || isPromiseSettledResultArrayFromTypeInformation(node, context)) {
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (isFilterCall(node)) {
|
|
297
|
+
return isPromiseSettledResultArray(node.callee.object, context, visitedVariables);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (node.type !== 'Identifier') {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return isPromiseSettledResultVariable(node, context, visitedVariables);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const getIdentifierName = node => {
|
|
308
|
+
node = unwrapExpression(node);
|
|
309
|
+
return node?.type === 'Identifier' ? node.name : undefined;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
const getParameterStatusName = parameter => getParameterStatusIdentifier(parameter)?.name;
|
|
313
|
+
|
|
314
|
+
const getParameterStatusIdentifier = parameter => {
|
|
315
|
+
parameter = unwrapExpression(parameter);
|
|
316
|
+
|
|
317
|
+
if (parameter?.type !== 'ObjectPattern') {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
for (const property of parameter.properties) {
|
|
322
|
+
if (
|
|
323
|
+
property.type === 'Property'
|
|
324
|
+
&& !property.computed
|
|
325
|
+
&& getPropertyName(property) === 'status'
|
|
326
|
+
&& property.value.type === 'Identifier'
|
|
327
|
+
) {
|
|
328
|
+
return property.value;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const getStatusLiteral = node => {
|
|
334
|
+
if (node?.type === 'Literal' && (node.value === 'fulfilled' || node.value === 'rejected')) {
|
|
335
|
+
return node.value;
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
function isStatusReference(node, statusContext) {
|
|
340
|
+
node = unwrapExpression(node);
|
|
341
|
+
|
|
342
|
+
if (
|
|
343
|
+
statusContext.statusVariable
|
|
344
|
+
&& isSameVariableReference(node, statusContext.statusVariable, statusContext.context)
|
|
345
|
+
) {
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (
|
|
350
|
+
!statusContext.statusVariable
|
|
351
|
+
&& statusContext.statusName
|
|
352
|
+
&& node?.type === 'Identifier'
|
|
353
|
+
&& node.name === statusContext.statusName
|
|
354
|
+
) {
|
|
355
|
+
return true;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (
|
|
359
|
+
statusContext.entryVariable
|
|
360
|
+
&& node?.type === 'MemberExpression'
|
|
361
|
+
&& isSameVariableReference(node.object, statusContext.entryVariable, statusContext.context)
|
|
362
|
+
&& getPropertyName(node) === 'status'
|
|
363
|
+
) {
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return Boolean(
|
|
368
|
+
!statusContext.entryVariable
|
|
369
|
+
&& statusContext.entryName
|
|
370
|
+
&& node?.type === 'MemberExpression'
|
|
371
|
+
&& getIdentifierName(node.object) === statusContext.entryName
|
|
372
|
+
&& getPropertyName(node) === 'status',
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function getStatusCheckKind(node, statusContext) {
|
|
377
|
+
node = unwrapExpression(node);
|
|
378
|
+
|
|
379
|
+
if (
|
|
380
|
+
node?.type !== 'BinaryExpression'
|
|
381
|
+
|| !['===', '==', '!==', '!='].includes(node.operator)
|
|
382
|
+
) {
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const statusLiteral = isStatusReference(node.left, statusContext)
|
|
387
|
+
? getStatusLiteral(node.right)
|
|
388
|
+
: (isStatusReference(node.right, statusContext)
|
|
389
|
+
? getStatusLiteral(node.left)
|
|
390
|
+
: undefined);
|
|
391
|
+
|
|
392
|
+
if (!statusLiteral) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
const isEqualityCheck = node.operator === '===' || node.operator === '==';
|
|
397
|
+
|
|
398
|
+
if (
|
|
399
|
+
(isEqualityCheck && statusLiteral === 'fulfilled')
|
|
400
|
+
|| (!isEqualityCheck && statusLiteral === 'rejected')
|
|
401
|
+
) {
|
|
402
|
+
return 'fulfilled';
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return 'unfulfilled';
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const isStatusFulfilledCheck = (node, statusContext) =>
|
|
409
|
+
getStatusCheckKind(node, statusContext) === 'fulfilled';
|
|
410
|
+
|
|
411
|
+
const isStatusUnfulfilledCheck = (node, statusContext) =>
|
|
412
|
+
getStatusCheckKind(node, statusContext) === 'unfulfilled';
|
|
413
|
+
|
|
414
|
+
function hasFulfilledStatusCheckInConjunction(node, statusContext) {
|
|
415
|
+
node = unwrapExpression(node);
|
|
416
|
+
|
|
417
|
+
if (isStatusFulfilledCheck(node, statusContext)) {
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return node?.type === 'LogicalExpression'
|
|
422
|
+
&& node.operator === '&&'
|
|
423
|
+
&& (
|
|
424
|
+
hasFulfilledStatusCheckInConjunction(node.left, statusContext)
|
|
425
|
+
|| hasFulfilledStatusCheckInConjunction(node.right, statusContext)
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function hasUnfulfilledStatusCheckInDisjunction(node, statusContext) {
|
|
430
|
+
node = unwrapExpression(node);
|
|
431
|
+
|
|
432
|
+
if (isStatusUnfulfilledCheck(node, statusContext)) {
|
|
433
|
+
return true;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
return node?.type === 'LogicalExpression'
|
|
437
|
+
&& node.operator === '||'
|
|
438
|
+
&& (
|
|
439
|
+
hasUnfulfilledStatusCheckInDisjunction(node.left, statusContext)
|
|
440
|
+
|| hasUnfulfilledStatusCheckInDisjunction(node.right, statusContext)
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const isConditionalFulfilledGuard = (node, child, statusContext) =>
|
|
445
|
+
node.type === 'ConditionalExpression'
|
|
446
|
+
&& (
|
|
447
|
+
(
|
|
448
|
+
node.consequent === child
|
|
449
|
+
&& hasFulfilledStatusCheckInConjunction(node.test, statusContext)
|
|
450
|
+
) || (
|
|
451
|
+
node.alternate === child
|
|
452
|
+
&& hasUnfulfilledStatusCheckInDisjunction(node.test, statusContext)
|
|
453
|
+
)
|
|
454
|
+
);
|
|
455
|
+
|
|
456
|
+
const isLogicalFulfilledGuard = (node, child, statusContext) =>
|
|
457
|
+
node.type === 'LogicalExpression'
|
|
458
|
+
&& (
|
|
459
|
+
(
|
|
460
|
+
node.operator === '&&'
|
|
461
|
+
&& node.right === child
|
|
462
|
+
&& hasFulfilledStatusCheckInConjunction(node.left, statusContext)
|
|
463
|
+
) || (
|
|
464
|
+
node.operator === '||'
|
|
465
|
+
&& node.right === child
|
|
466
|
+
&& hasUnfulfilledStatusCheckInDisjunction(node.left, statusContext)
|
|
467
|
+
)
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
const isIfStatementFulfilledGuard = (node, child, statusContext) =>
|
|
471
|
+
node.type === 'IfStatement'
|
|
472
|
+
&& (
|
|
473
|
+
(
|
|
474
|
+
node.consequent === child
|
|
475
|
+
&& hasFulfilledStatusCheckInConjunction(node.test, statusContext)
|
|
476
|
+
) || (
|
|
477
|
+
node.alternate === child
|
|
478
|
+
&& hasUnfulfilledStatusCheckInDisjunction(node.test, statusContext)
|
|
479
|
+
)
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
function isAlwaysExitingStatement(node) {
|
|
483
|
+
node = unwrapExpression(node);
|
|
484
|
+
|
|
485
|
+
if (node?.type === 'ReturnStatement' || node?.type === 'ThrowStatement') {
|
|
486
|
+
return true;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (node?.type === 'BlockStatement') {
|
|
490
|
+
return node.body.length > 0 && isAlwaysExitingStatement(node.body.at(-1));
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
function isGuardedByPreviousUnfulfilledExit(node, statusContext) {
|
|
497
|
+
const statements = node.parent?.body;
|
|
498
|
+
if (!Array.isArray(statements)) {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const nodeIndex = statements.indexOf(node);
|
|
503
|
+
const previousStatement = statements[nodeIndex - 1];
|
|
504
|
+
|
|
505
|
+
return previousStatement?.type === 'IfStatement'
|
|
506
|
+
&& !previousStatement.alternate
|
|
507
|
+
&& hasUnfulfilledStatusCheckInDisjunction(previousStatement.test, statusContext)
|
|
508
|
+
&& isAlwaysExitingStatement(previousStatement.consequent);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const isFulfilledFilterCallback = callback => {
|
|
512
|
+
if (!isFunction(callback) || callback.params.length === 0) {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const entryName = getIdentifierName(callback.params[0]);
|
|
517
|
+
const statusName = getParameterStatusName(callback.params[0]);
|
|
518
|
+
const statusContext = {
|
|
519
|
+
entryName,
|
|
520
|
+
statusName,
|
|
521
|
+
};
|
|
522
|
+
const body = unwrapExpression(callback.body);
|
|
523
|
+
|
|
524
|
+
if (body?.type === 'BlockStatement') {
|
|
525
|
+
const [statement] = body.body;
|
|
526
|
+
return body.body.length === 1
|
|
527
|
+
&& statement.type === 'ReturnStatement'
|
|
528
|
+
&& statement.argument
|
|
529
|
+
&& hasFulfilledStatusCheckInConjunction(statement.argument, statusContext);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
return hasFulfilledStatusCheckInConjunction(body, statusContext);
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
function isKnownFulfilledResultArray(node, context, visitedVariables = new Set()) {
|
|
536
|
+
if (isTypeAssertion(node) && isPromiseFulfilledResultArrayTypeAnnotation(node.typeAnnotation)) {
|
|
537
|
+
return true;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
node = unwrapExpression(node);
|
|
541
|
+
|
|
542
|
+
if (isPromiseFulfilledResultArrayFromTypeInformation(node, context)) {
|
|
543
|
+
return true;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (node.type === 'Identifier') {
|
|
547
|
+
return isPromiseFulfilledResultVariable(node, context, visitedVariables);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return isFilterCall(node)
|
|
551
|
+
&& hasFulfilledFilterInChain(node)
|
|
552
|
+
&& isPromiseSettledResultArray(node, context);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
function isGuardedByFulfilledCheck(node, readContext) {
|
|
556
|
+
for (
|
|
557
|
+
let child = node, current = node.parent;
|
|
558
|
+
current;
|
|
559
|
+
child = current, current = current.parent
|
|
560
|
+
) {
|
|
561
|
+
if (current.type === 'ArrowFunctionExpression' || current.type === 'FunctionExpression') {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (
|
|
566
|
+
isConditionalFulfilledGuard(current, child, readContext)
|
|
567
|
+
|| isLogicalFulfilledGuard(current, child, readContext)
|
|
568
|
+
|| isIfStatementFulfilledGuard(current, child, readContext)
|
|
569
|
+
) {
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
if (isGuardedByPreviousUnfulfilledExit(current, readContext)) {
|
|
574
|
+
return true;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (isReturnValue(current)) {
|
|
578
|
+
return false;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function isUnsafeValueRead(node, readContext) {
|
|
586
|
+
node = unwrapExpression(node);
|
|
587
|
+
|
|
588
|
+
return (
|
|
589
|
+
node?.type === 'MemberExpression'
|
|
590
|
+
&& isSameVariableReference(node.object, readContext.entryVariable, readContext.context)
|
|
591
|
+
&& getPropertyName(node) === 'value'
|
|
592
|
+
) || (
|
|
593
|
+
node?.type === 'Identifier'
|
|
594
|
+
&& isSameVariableReference(node, readContext.valueVariable, readContext.context)
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function getUnsafeValueReadFromChildren(nodes, readContext) {
|
|
599
|
+
for (const child of nodes) {
|
|
600
|
+
if (!child?.type) {
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (isFunction(child) || child.type === 'ClassExpression' || child.type === 'ClassDeclaration') {
|
|
605
|
+
continue;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const unsafeValueRead = getUnsafeValueRead(child, readContext);
|
|
609
|
+
if (unsafeValueRead) {
|
|
610
|
+
return unsafeValueRead;
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function getValueProperty(parameter) {
|
|
616
|
+
parameter = unwrapExpression(parameter);
|
|
617
|
+
|
|
618
|
+
if (parameter?.type !== 'ObjectPattern') {
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
for (const property of parameter.properties) {
|
|
623
|
+
if (
|
|
624
|
+
property.type === 'Property'
|
|
625
|
+
&& !property.computed
|
|
626
|
+
&& getPropertyName(property) === 'value'
|
|
627
|
+
) {
|
|
628
|
+
return property;
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function getParameterValueIdentifier(parameter) {
|
|
634
|
+
const valueProperty = getValueProperty(parameter);
|
|
635
|
+
const valueNode = valueProperty?.value;
|
|
636
|
+
|
|
637
|
+
if (valueNode?.type === 'Identifier') {
|
|
638
|
+
return valueNode;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
if (
|
|
642
|
+
valueNode?.type === 'AssignmentPattern'
|
|
643
|
+
&& valueNode.left.type === 'Identifier'
|
|
644
|
+
) {
|
|
645
|
+
return valueNode.left;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
const shouldSkipChildNode = (node, key) =>
|
|
650
|
+
(
|
|
651
|
+
node.type === 'MemberExpression'
|
|
652
|
+
&& key === 'property'
|
|
653
|
+
&& !node.computed
|
|
654
|
+
) || (
|
|
655
|
+
['Property', 'PropertyDefinition', 'MethodDefinition'].includes(node.type)
|
|
656
|
+
&& key === 'key'
|
|
657
|
+
&& !node.computed
|
|
658
|
+
);
|
|
659
|
+
|
|
660
|
+
function getUnsafeValueRead(node, readContext) {
|
|
661
|
+
node = unwrapExpression(node);
|
|
662
|
+
|
|
663
|
+
if (
|
|
664
|
+
isUnsafeValueRead(node, readContext)
|
|
665
|
+
&& !isGuardedByFulfilledCheck(node, readContext)
|
|
666
|
+
) {
|
|
667
|
+
return node;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
for (const [key, value] of Object.entries(node)) {
|
|
671
|
+
if (key === 'parent' || shouldSkipChildNode(node, key)) {
|
|
672
|
+
continue;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
if (!value || typeof value !== 'object') {
|
|
676
|
+
continue;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
const nodes = Array.isArray(value) ? value : [value];
|
|
680
|
+
const unsafeValueRead = getUnsafeValueReadFromChildren(nodes, readContext);
|
|
681
|
+
if (unsafeValueRead) {
|
|
682
|
+
return unsafeValueRead;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function isSameVariableReference(node, variable, context) {
|
|
688
|
+
node = unwrapExpression(node);
|
|
689
|
+
|
|
690
|
+
return node?.type === 'Identifier'
|
|
691
|
+
&& variable
|
|
692
|
+
&& findVariable(context.sourceCode.getScope(node), node) === variable;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
function isDerivedFromVariableThroughFilters(node, variable, context) {
|
|
696
|
+
node = unwrapExpression(node);
|
|
697
|
+
|
|
698
|
+
if (isSameVariableReference(node, variable, context)) {
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
return isFilterCall(node)
|
|
703
|
+
&& isDerivedFromVariableThroughFilters(node.callee.object, variable, context);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function hasFulfilledFilterInChain(node) {
|
|
707
|
+
node = unwrapExpression(node);
|
|
708
|
+
|
|
709
|
+
while (isFilterCall(node)) {
|
|
710
|
+
if (isFulfilledFilterCallback(node.arguments[0])) {
|
|
711
|
+
return true;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
node = unwrapExpression(node.callee.object);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
function getMapCallbackProblem(callExpression, context, {knownPromiseSettledResultArray = false} = {}) {
|
|
721
|
+
if (
|
|
722
|
+
!isMethodCall(callExpression, {
|
|
723
|
+
method: 'map',
|
|
724
|
+
minimumArguments: 1,
|
|
725
|
+
maximumArguments: 2,
|
|
726
|
+
optionalCall: false,
|
|
727
|
+
optionalMember: false,
|
|
728
|
+
})
|
|
729
|
+
|| (!knownPromiseSettledResultArray && !isPromiseSettledResultArray(callExpression.callee.object, context))
|
|
730
|
+
|| isKnownFulfilledResultArray(callExpression.callee.object, context)
|
|
731
|
+
) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
const [callback] = callExpression.arguments;
|
|
736
|
+
if (!isFunction(callback) || callback.params.length === 0) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
const [parameter] = callback.params;
|
|
741
|
+
if (isPromiseFulfilledResultFromTypeInformation(parameter, context)) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
const valueProperty = getValueProperty(parameter);
|
|
746
|
+
if (valueProperty && valueProperty.value.type !== 'Identifier') {
|
|
747
|
+
return {
|
|
748
|
+
node: valueProperty.key,
|
|
749
|
+
messageId: MESSAGE_ID,
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const entryName = getIdentifierName(parameter);
|
|
754
|
+
const entryVariable = entryName && findVariable(context.sourceCode.getScope(parameter), parameter);
|
|
755
|
+
const valueIdentifier = getParameterValueIdentifier(parameter);
|
|
756
|
+
const valueVariable = valueIdentifier && findVariable(context.sourceCode.getScope(valueIdentifier), valueIdentifier);
|
|
757
|
+
const statusIdentifier = getParameterStatusIdentifier(parameter);
|
|
758
|
+
const statusName = statusIdentifier?.name;
|
|
759
|
+
const statusVariable = statusIdentifier && findVariable(context.sourceCode.getScope(statusIdentifier), statusIdentifier);
|
|
760
|
+
const unsafeValueRead = getUnsafeValueRead(callback.body, {
|
|
761
|
+
entryName,
|
|
762
|
+
entryVariable,
|
|
763
|
+
statusName,
|
|
764
|
+
statusVariable,
|
|
765
|
+
valueVariable,
|
|
766
|
+
context,
|
|
767
|
+
});
|
|
768
|
+
if (unsafeValueRead) {
|
|
769
|
+
return {
|
|
770
|
+
node: unsafeValueRead,
|
|
771
|
+
messageId: MESSAGE_ID,
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
function getThenMapCallbackProblem(node, resultsVariable, context) {
|
|
777
|
+
node = unwrapExpression(node);
|
|
778
|
+
|
|
779
|
+
if (!node?.type || isFunction(node) || node.type === 'ClassExpression' || node.type === 'ClassDeclaration') {
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (
|
|
784
|
+
isMethodCall(node, {
|
|
785
|
+
method: 'map',
|
|
786
|
+
minimumArguments: 1,
|
|
787
|
+
maximumArguments: 2,
|
|
788
|
+
optionalCall: false,
|
|
789
|
+
optionalMember: false,
|
|
790
|
+
})
|
|
791
|
+
&& isDerivedFromVariableThroughFilters(node.callee.object, resultsVariable, context)
|
|
792
|
+
&& !hasFulfilledFilterInChain(node.callee.object)
|
|
793
|
+
) {
|
|
794
|
+
const problem = getMapCallbackProblem(node, context, {knownPromiseSettledResultArray: true});
|
|
795
|
+
if (problem) {
|
|
796
|
+
return problem;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
for (const [key, value] of Object.entries(node)) {
|
|
801
|
+
if (key === 'parent' || shouldSkipChildNode(node, key) || !value || typeof value !== 'object') {
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const nodes = Array.isArray(value) ? value : [value];
|
|
806
|
+
for (const child of nodes) {
|
|
807
|
+
const problem = getThenMapCallbackProblem(child, resultsVariable, context);
|
|
808
|
+
if (problem) {
|
|
809
|
+
return problem;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
function getThenCallbackProblem(callExpression, context) {
|
|
816
|
+
if (
|
|
817
|
+
!isMethodCall(callExpression, {
|
|
818
|
+
method: 'then',
|
|
819
|
+
minimumArguments: 1,
|
|
820
|
+
optionalCall: false,
|
|
821
|
+
optionalMember: false,
|
|
822
|
+
})
|
|
823
|
+
|| !isPromiseAllSettledCall(callExpression.callee.object)
|
|
824
|
+
) {
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
const [callback] = callExpression.arguments;
|
|
829
|
+
if (!isFunction(callback) || callback.params.length === 0) {
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
const [parameter] = callback.params;
|
|
834
|
+
const resultsName = getIdentifierName(parameter);
|
|
835
|
+
const resultsVariable = resultsName && findVariable(context.sourceCode.getScope(parameter), parameter);
|
|
836
|
+
if (!resultsVariable) {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
return getThenMapCallbackProblem(callback.body, resultsVariable, context);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/** @param {import('eslint').Rule.RuleContext} context */
|
|
844
|
+
const create = context => {
|
|
845
|
+
context.on('CallExpression', callExpression => getThenCallbackProblem(callExpression, context) ?? getMapCallbackProblem(callExpression, context));
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
/** @type {import('eslint').Rule.RuleModule} */
|
|
849
|
+
const config = {
|
|
850
|
+
create,
|
|
851
|
+
meta: {
|
|
852
|
+
type: 'problem',
|
|
853
|
+
docs: {
|
|
854
|
+
description: 'Disallow reading `.value` from `Promise.allSettled()` results without a fulfilled status guard.',
|
|
855
|
+
recommended: 'unopinionated',
|
|
856
|
+
},
|
|
857
|
+
schema: [],
|
|
858
|
+
messages,
|
|
859
|
+
languages: [
|
|
860
|
+
'js/js',
|
|
861
|
+
],
|
|
862
|
+
},
|
|
863
|
+
};
|
|
864
|
+
|
|
865
|
+
export default config;
|