eslint-plugin-skuba 1.0.3-replace-global-vars-20251121010036 → 1.0.4-node-24-20251215224735
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/lib/index.cjs +370 -0
- package/lib/index.mjs +46 -11001
- package/package.json +11 -8
- package/lib/index.d.ts +0 -21
- package/lib/index.js +0 -11323
package/lib/index.cjs
ADDED
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
let __typescript_eslint_utils = require("@typescript-eslint/utils");
|
|
2
|
+
|
|
3
|
+
//#region src/rules/no-sync-in-promise-iterable.ts
|
|
4
|
+
/**
|
|
5
|
+
* Whether a TypeScript type is a Promise-like "thenable".
|
|
6
|
+
*/
|
|
7
|
+
const isThenableType = (type, checker) => {
|
|
8
|
+
if (type.symbol?.name === "Promise") return true;
|
|
9
|
+
const thenSymbol = type.getProperty("then");
|
|
10
|
+
if (thenSymbol?.valueDeclaration) {
|
|
11
|
+
if (checker.getTypeOfSymbolAtLocation(thenSymbol, thenSymbol.valueDeclaration).getCallSignatures().length) return true;
|
|
12
|
+
}
|
|
13
|
+
if (type.isUnion()) return type.types.every((t) => isThenableType(t, checker));
|
|
14
|
+
return false;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Whether a node represents a Promise-like "thenable".
|
|
18
|
+
*/
|
|
19
|
+
const isThenableNode = (node, esTreeNodeToTSNodeMap, checker) => {
|
|
20
|
+
const tsNode = esTreeNodeToTSNodeMap.get(node);
|
|
21
|
+
return isThenableType(checker.getTypeAtLocation(tsNode), checker);
|
|
22
|
+
};
|
|
23
|
+
const isIterableType = (type, checker) => {
|
|
24
|
+
if (checker.isArrayLikeType(type)) return true;
|
|
25
|
+
return type.getProperties().some((property) => property.name.startsWith("__@iterator"));
|
|
26
|
+
};
|
|
27
|
+
const PROMISE_INSTANCE_METHODS = new Set([
|
|
28
|
+
"catch",
|
|
29
|
+
"then",
|
|
30
|
+
"finally"
|
|
31
|
+
]);
|
|
32
|
+
const isChainedPromise = (node, esTreeNodeToTSNodeMap, checker) => {
|
|
33
|
+
if (!(node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.MemberExpression && node.callee.property.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && PROMISE_INSTANCE_METHODS.has(node.callee.property.name))) return false;
|
|
34
|
+
const parent = esTreeNodeToTSNodeMap.get(node.callee.object);
|
|
35
|
+
return isThenableType(checker.getTypeAtLocation(parent), checker);
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
|
|
39
|
+
*/
|
|
40
|
+
const SAFE_ISH_CONSTRUCTORS = new Set([
|
|
41
|
+
"AggregateError",
|
|
42
|
+
"AsyncDisposableStack",
|
|
43
|
+
"Boolean",
|
|
44
|
+
"Date",
|
|
45
|
+
"DisposableStack",
|
|
46
|
+
"Error",
|
|
47
|
+
"EvalError",
|
|
48
|
+
"FinalizationRegistry",
|
|
49
|
+
"Map",
|
|
50
|
+
"Number",
|
|
51
|
+
"Object",
|
|
52
|
+
"Promise",
|
|
53
|
+
"Proxy",
|
|
54
|
+
"RangeError",
|
|
55
|
+
"ReferenceError",
|
|
56
|
+
"Set",
|
|
57
|
+
"SharedArrayBuffer",
|
|
58
|
+
"String",
|
|
59
|
+
"SuppressedError",
|
|
60
|
+
"Symbol",
|
|
61
|
+
"SyntaxError",
|
|
62
|
+
"TypeError",
|
|
63
|
+
"URIError",
|
|
64
|
+
"WeakMap",
|
|
65
|
+
"WeakRef",
|
|
66
|
+
"WeakSet"
|
|
67
|
+
]);
|
|
68
|
+
const SAFE_ISH_FUNCTIONS = new Set([
|
|
69
|
+
"Boolean",
|
|
70
|
+
"isFinite",
|
|
71
|
+
"isNaN",
|
|
72
|
+
"Number",
|
|
73
|
+
"parseFloat",
|
|
74
|
+
"parseInt",
|
|
75
|
+
"String",
|
|
76
|
+
"Symbol"
|
|
77
|
+
]);
|
|
78
|
+
const SAFE_ISH_STATIC_METHODS = {
|
|
79
|
+
Array: new Set([
|
|
80
|
+
"from",
|
|
81
|
+
"isArray",
|
|
82
|
+
"of"
|
|
83
|
+
]),
|
|
84
|
+
Date: new Set([
|
|
85
|
+
"now",
|
|
86
|
+
"parse",
|
|
87
|
+
"UTC"
|
|
88
|
+
]),
|
|
89
|
+
Iterator: new Set(["from"]),
|
|
90
|
+
Number: new Set([
|
|
91
|
+
"isFinite",
|
|
92
|
+
"isInteger",
|
|
93
|
+
"isNaN",
|
|
94
|
+
"isSafeInteger",
|
|
95
|
+
"parseFloat",
|
|
96
|
+
"parseInt"
|
|
97
|
+
]),
|
|
98
|
+
Object: new Set([
|
|
99
|
+
"entries",
|
|
100
|
+
"fromEntries",
|
|
101
|
+
"groupBy",
|
|
102
|
+
"hasOwn",
|
|
103
|
+
"is",
|
|
104
|
+
"keys",
|
|
105
|
+
"values"
|
|
106
|
+
]),
|
|
107
|
+
Promise: new Set([
|
|
108
|
+
"reject",
|
|
109
|
+
"resolve",
|
|
110
|
+
"try",
|
|
111
|
+
"withResolvers"
|
|
112
|
+
])
|
|
113
|
+
};
|
|
114
|
+
const SAFE_ISH_INSTANCE_METHODS = new Set([
|
|
115
|
+
"apply",
|
|
116
|
+
"bind",
|
|
117
|
+
"call",
|
|
118
|
+
"toLocaleString",
|
|
119
|
+
"toString"
|
|
120
|
+
]);
|
|
121
|
+
const isArrayFromAsync = (node) => node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.MemberExpression && node.callee.object.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && node.callee.property.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && node.callee.object.name === "Array" && node.callee.property.name === "fromAsync";
|
|
122
|
+
const isPromiseTry = (node) => node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.MemberExpression && node.callee.object.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && node.callee.object.name === "Promise" && node.callee.property.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && node.callee.property.name === "try";
|
|
123
|
+
/**
|
|
124
|
+
* Whether a call expression represents a safe-ish built-in.
|
|
125
|
+
*
|
|
126
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects
|
|
127
|
+
*/
|
|
128
|
+
const isSafeIshBuiltIn = (node) => {
|
|
129
|
+
if (node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier) return SAFE_ISH_FUNCTIONS.has(node.callee.name);
|
|
130
|
+
if (node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.MemberExpression) {
|
|
131
|
+
const { object, property } = node.callee;
|
|
132
|
+
if (object.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && property.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier) return SAFE_ISH_STATIC_METHODS[object.name]?.has(property.name) ?? SAFE_ISH_INSTANCE_METHODS.has(property.name);
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
};
|
|
136
|
+
const SAFE_ISH_ITERABLE_INSTANCE_METHODS = new Set([
|
|
137
|
+
"concat",
|
|
138
|
+
"copyWithin",
|
|
139
|
+
"difference",
|
|
140
|
+
"drop",
|
|
141
|
+
"entries",
|
|
142
|
+
"every",
|
|
143
|
+
"fill",
|
|
144
|
+
"filter",
|
|
145
|
+
"find",
|
|
146
|
+
"findIndex",
|
|
147
|
+
"findLast",
|
|
148
|
+
"findLastIndex",
|
|
149
|
+
"flat",
|
|
150
|
+
"flatMap",
|
|
151
|
+
"has",
|
|
152
|
+
"includes",
|
|
153
|
+
"indexOf",
|
|
154
|
+
"intersection",
|
|
155
|
+
"keys",
|
|
156
|
+
"lastIndexOf",
|
|
157
|
+
"map",
|
|
158
|
+
"reduce",
|
|
159
|
+
"reduceRight",
|
|
160
|
+
"reverse",
|
|
161
|
+
"slice",
|
|
162
|
+
"some",
|
|
163
|
+
"splice",
|
|
164
|
+
"symmetricDifference",
|
|
165
|
+
"toArray",
|
|
166
|
+
"toReversed",
|
|
167
|
+
"toSorted",
|
|
168
|
+
"toSpliced",
|
|
169
|
+
"union",
|
|
170
|
+
"values",
|
|
171
|
+
"with"
|
|
172
|
+
]);
|
|
173
|
+
/**
|
|
174
|
+
* Whether a call expression represents a method on an iterable object that is
|
|
175
|
+
* unlikely to internally throw a synchronous error.
|
|
176
|
+
*/
|
|
177
|
+
const isSafeIshIterableMethod = (node, esTreeNodeToTSNodeMap, checker) => {
|
|
178
|
+
if (!(node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.MemberExpression && node.callee.property.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && SAFE_ISH_ITERABLE_INSTANCE_METHODS.has(node.callee.property.name))) return false;
|
|
179
|
+
const tsNode = esTreeNodeToTSNodeMap.get(node.callee.object);
|
|
180
|
+
return isIterableType(checker.getTypeAtLocation(tsNode), checker);
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Whether a call expression represents a safe-ish builder.
|
|
184
|
+
*
|
|
185
|
+
* This is currently overfitted to `knex`.
|
|
186
|
+
*/
|
|
187
|
+
const isSafeIshBuilder = (node) => {
|
|
188
|
+
if (node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.MemberExpression && node.callee.object.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.CallExpression) return isSafeIshBuilder(node.callee.object);
|
|
189
|
+
if (node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && node.callee.name === "knex" || node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.MemberExpression && node.callee.object.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && node.callee.object.name === "knex") return true;
|
|
190
|
+
return false;
|
|
191
|
+
};
|
|
192
|
+
/**
|
|
193
|
+
* The nodes traversable from the current AST position containing logic with a
|
|
194
|
+
* reasonable chance of throwing a synchronous error.
|
|
195
|
+
*/
|
|
196
|
+
const possibleNodesWithSyncError = (node, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls) => {
|
|
197
|
+
switch (node.type) {
|
|
198
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.ArrayExpression: return node.elements.flatMap((arg) => arg ? possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls) : []);
|
|
199
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
200
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.FunctionExpression:
|
|
201
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.FunctionDeclaration:
|
|
202
|
+
if (calls < 1) return [];
|
|
203
|
+
if (node.async) return [];
|
|
204
|
+
if (node.body.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.BlockStatement) return [node.parent];
|
|
205
|
+
return possibleNodesWithSyncError(node.body, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls - 1);
|
|
206
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.AssignmentExpression: return possibleNodesWithSyncError(node.right, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls);
|
|
207
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.BinaryExpression:
|
|
208
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.LogicalExpression: return [node.left, node.right].flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls));
|
|
209
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.CallExpression: {
|
|
210
|
+
if (isSafeIshBuilder(node)) return [];
|
|
211
|
+
if (isPromiseTry(node)) return node.arguments.flatMap((arg, index) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, index === 0 ? calls : calls + 1));
|
|
212
|
+
if (isArrayFromAsync(node)) return node.arguments.flatMap((arg, index) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, index === 1 ? calls : calls + 1));
|
|
213
|
+
if (isSafeIshBuiltIn(node)) return node.arguments.flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls + 1));
|
|
214
|
+
const collectCalleeArgumentErrors = (callee) => {
|
|
215
|
+
if (callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.CallExpression) {
|
|
216
|
+
const nestedErrors = collectCalleeArgumentErrors(callee.callee);
|
|
217
|
+
const argErrors = callee.arguments.flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls + 1));
|
|
218
|
+
return [...nestedErrors, ...argErrors];
|
|
219
|
+
}
|
|
220
|
+
return [];
|
|
221
|
+
};
|
|
222
|
+
const expression = node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier ? findExpression(node.callee, sourceCode, visited) : node.callee;
|
|
223
|
+
if (expression?.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.ArrowFunctionExpression || expression?.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.FunctionExpression) return [expression, ...node.arguments].flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls + 1));
|
|
224
|
+
const calleeErrors = collectCalleeArgumentErrors(node.callee);
|
|
225
|
+
if (isChainedPromise(node, esTreeNodeToTSNodeMap, checker)) return [...calleeErrors, ...node.arguments.flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls))];
|
|
226
|
+
if (isSafeIshIterableMethod(node, esTreeNodeToTSNodeMap, checker) || isThenableNode(node, esTreeNodeToTSNodeMap, checker)) return [...calleeErrors, ...node.arguments.flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls + 1))];
|
|
227
|
+
if (calleeErrors.length) return calleeErrors;
|
|
228
|
+
return [node];
|
|
229
|
+
}
|
|
230
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.ChainExpression: return possibleNodesWithSyncError(node.expression, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls);
|
|
231
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.ConditionalExpression: return [
|
|
232
|
+
node.test,
|
|
233
|
+
node.consequent,
|
|
234
|
+
node.alternate
|
|
235
|
+
].flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls));
|
|
236
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier: return [];
|
|
237
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Literal: return [];
|
|
238
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.MemberExpression: return possibleNodesWithSyncError(node.object, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls);
|
|
239
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.NewExpression:
|
|
240
|
+
if (node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && node.callee.name === "Promise") return node.arguments.flatMap((arg, index) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, index === 0 ? calls : calls + 1));
|
|
241
|
+
if (node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && SAFE_ISH_CONSTRUCTORS.has(node.callee.name)) return node.arguments.flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls + 1));
|
|
242
|
+
return [node];
|
|
243
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.SequenceExpression: return node.expressions.flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls));
|
|
244
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.SpreadElement: return possibleNodesWithSyncError(node.argument, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls);
|
|
245
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.TaggedTemplateExpression: return [node];
|
|
246
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.TemplateLiteral: return node.expressions.flatMap((arg) => possibleNodesWithSyncError(arg, esTreeNodeToTSNodeMap, checker, sourceCode, visited, calls));
|
|
247
|
+
}
|
|
248
|
+
return [];
|
|
249
|
+
};
|
|
250
|
+
const getSourceCodeExcerpt = (node, sourceCode) => {
|
|
251
|
+
const text = sourceCode.getText(node);
|
|
252
|
+
const lines = text.split("\n");
|
|
253
|
+
return lines.length <= 2 ? text : `${lines[0]?.trimEnd()}...${lines[lines.length - 1]?.trimStart()}`;
|
|
254
|
+
};
|
|
255
|
+
const checkIterableForSyncErrors = (elements, method, context, esTreeNodeToTSNodeMap, checker) => {
|
|
256
|
+
for (const { element, reference } of elements) {
|
|
257
|
+
const nodes = possibleNodesWithSyncError(element, esTreeNodeToTSNodeMap, checker, context.sourceCode, /* @__PURE__ */ new Set(), 0);
|
|
258
|
+
for (const node of nodes) {
|
|
259
|
+
const root = reference ?? element;
|
|
260
|
+
const value = getSourceCodeExcerpt(root, context.sourceCode);
|
|
261
|
+
if (root.loc.start.column === node.loc.start.column && root.loc.start.line === node.loc.start.line) context.report({
|
|
262
|
+
node,
|
|
263
|
+
messageId: "mayThrowSyncError",
|
|
264
|
+
data: {
|
|
265
|
+
value,
|
|
266
|
+
method
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
else context.report({
|
|
270
|
+
node,
|
|
271
|
+
messageId: "mayLeadToSyncError",
|
|
272
|
+
data: {
|
|
273
|
+
value,
|
|
274
|
+
method,
|
|
275
|
+
underlying: getSourceCodeExcerpt(node, context.sourceCode),
|
|
276
|
+
line: node.loc.start.line.toString(),
|
|
277
|
+
column: node.loc.start.column.toString()
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
const findExpression = (node, sourceCode, visited) => {
|
|
284
|
+
let currentScope = sourceCode.getScope(node);
|
|
285
|
+
do {
|
|
286
|
+
const variable = currentScope.set.get(node.name);
|
|
287
|
+
const definition = variable?.defs[0];
|
|
288
|
+
if (!definition) continue;
|
|
289
|
+
if (visited.has(variable.name)) return;
|
|
290
|
+
visited.add(variable.name);
|
|
291
|
+
if (definition.node.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.VariableDeclarator && definition.node.init) return definition.node.init;
|
|
292
|
+
} while (currentScope = currentScope.upper);
|
|
293
|
+
};
|
|
294
|
+
const resolveArrayElements = (node, sourceCode, visited = /* @__PURE__ */ new Set(), reference) => {
|
|
295
|
+
switch (node.type) {
|
|
296
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.ArrayExpression: return node.elements.flatMap((element, index) => {
|
|
297
|
+
if (!element) return [];
|
|
298
|
+
if (element.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.SpreadElement) return resolveArrayElements(element.argument, sourceCode, visited, reference ?? element);
|
|
299
|
+
if (index === 0) return [];
|
|
300
|
+
return {
|
|
301
|
+
element,
|
|
302
|
+
reference
|
|
303
|
+
};
|
|
304
|
+
});
|
|
305
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.CallExpression: return [{
|
|
306
|
+
element: node,
|
|
307
|
+
reference
|
|
308
|
+
}];
|
|
309
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier: {
|
|
310
|
+
const expression = findExpression(node, sourceCode, visited);
|
|
311
|
+
if (!expression) return [];
|
|
312
|
+
return resolveArrayElements(expression, sourceCode, visited, reference ?? node);
|
|
313
|
+
}
|
|
314
|
+
case __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.SpreadElement: return resolveArrayElements(node.argument, sourceCode, visited, reference ?? node);
|
|
315
|
+
}
|
|
316
|
+
return [];
|
|
317
|
+
};
|
|
318
|
+
const createRule = __typescript_eslint_utils.ESLintUtils.RuleCreator((name) => `https://github.com/seek-oss/skuba/tree/main/docs/eslint-plugin/${name}.md`);
|
|
319
|
+
var no_sync_in_promise_iterable_default = createRule({
|
|
320
|
+
defaultOptions: [],
|
|
321
|
+
name: "no-sync-in-promise-iterable",
|
|
322
|
+
meta: {
|
|
323
|
+
type: "problem",
|
|
324
|
+
docs: {
|
|
325
|
+
description: "Heuristically flags synchronous logic in the iterable argument of static Promise methods that could leave preceding promises dangling",
|
|
326
|
+
recommended: true,
|
|
327
|
+
requiresTypeChecking: true
|
|
328
|
+
},
|
|
329
|
+
schema: [],
|
|
330
|
+
messages: {
|
|
331
|
+
mayLeadToSyncError: "{{value}} leads to {{underlying}} at {{line}}:{{column}} which may synchronously throw an error and leave preceding promises dangling. Evaluate synchronous expressions outside of the iterable argument to Promise.{{method}}, or safely wrap with the async keyword, Promise.try(), or Promise.resolve().then().",
|
|
332
|
+
mayThrowSyncError: "{{value}} may synchronously throw an error and leave preceding promises dangling. Evaluate synchronous expressions outside of the iterable argument to Promise.{{method}}, or safely wrap with the async keyword, Promise.try(), or Promise.resolve().then()."
|
|
333
|
+
}
|
|
334
|
+
},
|
|
335
|
+
create: (context) => {
|
|
336
|
+
const { esTreeNodeToTSNodeMap, program } = __typescript_eslint_utils.ESLintUtils.getParserServices(context);
|
|
337
|
+
const checker = program.getTypeChecker();
|
|
338
|
+
return { CallExpression: (node) => {
|
|
339
|
+
if (!(node.callee.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.MemberExpression && node.callee.object.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && node.callee.object.name === "Promise" && node.callee.property.type === __typescript_eslint_utils.TSESTree.AST_NODE_TYPES.Identifier && [
|
|
340
|
+
"all",
|
|
341
|
+
"allSettled",
|
|
342
|
+
"any",
|
|
343
|
+
"race"
|
|
344
|
+
].includes(node.callee.property.name))) return;
|
|
345
|
+
const method = node.callee.property.name;
|
|
346
|
+
const iterableArg = node.arguments[0];
|
|
347
|
+
if (!iterableArg) return;
|
|
348
|
+
checkIterableForSyncErrors(resolveArrayElements(iterableArg, context.sourceCode), method, context, esTreeNodeToTSNodeMap, checker);
|
|
349
|
+
} };
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region src/index.ts
|
|
355
|
+
const skuba = {
|
|
356
|
+
meta: {
|
|
357
|
+
name: "skuba",
|
|
358
|
+
version: "1.0.0"
|
|
359
|
+
},
|
|
360
|
+
rules: { "no-sync-in-promise-iterable": no_sync_in_promise_iterable_default },
|
|
361
|
+
configs: {}
|
|
362
|
+
};
|
|
363
|
+
skuba.configs = { recommended: [{
|
|
364
|
+
plugins: { skuba },
|
|
365
|
+
rules: { "skuba/no-sync-in-promise-iterable": "warn" }
|
|
366
|
+
}] };
|
|
367
|
+
var src_default = skuba;
|
|
368
|
+
|
|
369
|
+
//#endregion
|
|
370
|
+
module.exports = src_default;
|