eslint-plugin-reliability 3.1.9 → 3.1.11

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.
@@ -1,321 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noUnhandledPromise = void 0;
4
- exports.isLikelyPromiseExpression = isLikelyPromiseExpression;
5
- exports.isPromiseHandled = isPromiseHandled;
6
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
7
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
8
- const NEVER_RETURNS_PROMISE_FUNCTIONS = new Set([
9
- 'setTimeout', 'clearTimeout', 'setInterval', 'clearInterval',
10
- 'setImmediate', 'clearImmediate',
11
- 'requestAnimationFrame', 'cancelAnimationFrame',
12
- 'queueMicrotask',
13
- 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
14
- 'parseInt', 'parseFloat', 'isNaN', 'isFinite',
15
- 'Array', 'Object',
16
- ]);
17
- const NEVER_RETURNS_PROMISE_METHODS = new Set([
18
- 'log', 'error', 'warn', 'info', 'debug', 'trace', 'group', 'groupEnd',
19
- 'time', 'timeEnd', 'assert',
20
- 'floor', 'ceil', 'round', 'abs', 'min', 'max', 'pow', 'sqrt', 'random',
21
- 'sin', 'cos', 'tan', 'log2', 'log10',
22
- 'slice', 'split', 'join', 'concat', 'includes', 'indexOf', 'lastIndexOf',
23
- 'startsWith', 'endsWith', 'replace', 'replaceAll', 'trim', 'toLowerCase', 'toUpperCase',
24
- 'repeat', 'padStart', 'padEnd', 'charAt', 'charCodeAt', 'codePointAt',
25
- 'push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort',
26
- 'map', 'filter', 'reduce', 'reduceRight', 'forEach', 'every', 'some', 'find', 'findIndex',
27
- 'flat', 'flatMap', 'fill', 'copyWithin', 'entries', 'keys', 'values',
28
- 'parse', 'stringify',
29
- 'abort', 'addEventListener', 'removeEventListener', 'dispatchEvent',
30
- 'now', 'parse', 'UTC', 'from', 'of', 'isArray', 'isBuffer',
31
- 'isInteger', 'isFinite', 'isNaN', 'isSafeInteger',
32
- 'fromCharCode', 'fromCodePoint', 'raw',
33
- 'assign', 'freeze', 'isFrozen', 'create', 'defineProperty', 'defineProperties',
34
- 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 'getPrototypeOf', 'setPrototypeOf',
35
- 'preventExtensions', 'isExtensible', 'seal', 'isSealed', 'fromEntries',
36
- ]);
37
- const SYNC_NAMESPACE_OBJECTS = new Set([
38
- 'Math', 'JSON', 'Date', 'Buffer', 'Array', 'Object', 'Number', 'String',
39
- 'Boolean', 'Symbol', 'BigInt', 'Reflect', 'console', 'process',
40
- ]);
41
- function isLikelyPromiseExpression(node) {
42
- if (node.type !== 'CallExpression')
43
- return false;
44
- const callee = node.callee;
45
- if (callee.type === 'Identifier') {
46
- const name = callee.name;
47
- if (NEVER_RETURNS_PROMISE_FUNCTIONS.has(name))
48
- return false;
49
- return true;
50
- }
51
- if (callee.type === 'MemberExpression') {
52
- const prop = callee.property;
53
- if (prop.type === 'Identifier') {
54
- if (NEVER_RETURNS_PROMISE_METHODS.has(prop.name))
55
- return false;
56
- }
57
- const obj = callee.object;
58
- if (obj.type === 'Identifier' && SYNC_NAMESPACE_OBJECTS.has(obj.name)) {
59
- return false;
60
- }
61
- return true;
62
- }
63
- return true;
64
- }
65
- function isPromiseDelegatedToCaller(node) {
66
- const parent = node.parent;
67
- if (!parent)
68
- return false;
69
- if (parent.type === 'ReturnStatement')
70
- return true;
71
- if (parent.type === 'ArrowFunctionExpression' && parent.body === node)
72
- return true;
73
- return false;
74
- }
75
- function isInsidePromiseCallback(node) {
76
- let current = node;
77
- let depth = 0;
78
- const maxDepth = 10;
79
- while (current && depth < maxDepth) {
80
- const parent = current
81
- .parent;
82
- if (!parent)
83
- break;
84
- if (parent.type === 'ArrowFunctionExpression' ||
85
- parent.type === 'FunctionExpression') {
86
- const funcParent = parent
87
- .parent;
88
- if (funcParent &&
89
- funcParent.type === 'CallExpression' &&
90
- funcParent.callee &&
91
- funcParent.callee.type === 'MemberExpression') {
92
- const memberExpr = funcParent.callee;
93
- if (memberExpr.property.type === 'Identifier') {
94
- const methodName = memberExpr.property.name;
95
- if (methodName === 'then' ||
96
- methodName === 'catch' ||
97
- methodName === 'finally') {
98
- return true;
99
- }
100
- }
101
- }
102
- }
103
- current = parent;
104
- depth++;
105
- }
106
- return false;
107
- }
108
- function isPromiseHandled(node) {
109
- if (node.type === 'Identifier') {
110
- const parent = node.parent;
111
- if (parent &&
112
- parent.type === 'MemberExpression' &&
113
- parent.object === node) {
114
- if (parent.property.type === 'Identifier') {
115
- const methodName = parent.property.name;
116
- if (methodName === 'catch' ||
117
- methodName === 'then' ||
118
- methodName === 'finally') {
119
- const memberParent = parent.parent;
120
- if (memberParent &&
121
- memberParent.type === 'CallExpression' &&
122
- memberParent.callee === parent) {
123
- return true;
124
- }
125
- }
126
- }
127
- }
128
- }
129
- let current = node;
130
- let depth = 0;
131
- const maxDepth = 10;
132
- while (current && depth < maxDepth) {
133
- const parent = current
134
- .parent;
135
- if (!parent)
136
- break;
137
- if (parent.type === 'MemberExpression' && parent.object === current) {
138
- if (parent.property.type === 'Identifier') {
139
- const methodName = parent.property.name;
140
- if (methodName === 'catch' ||
141
- methodName === 'then' ||
142
- methodName === 'finally') {
143
- const memberParent = parent.parent;
144
- if (memberParent &&
145
- memberParent.type === 'CallExpression' &&
146
- memberParent.callee === parent) {
147
- return true;
148
- }
149
- }
150
- }
151
- }
152
- if (parent.type === 'TryStatement') {
153
- return true;
154
- }
155
- if (parent.type === 'AwaitExpression') {
156
- return true;
157
- }
158
- current = parent;
159
- depth++;
160
- }
161
- return false;
162
- }
163
- exports.noUnhandledPromise = (0, eslint_devkit_2.createRule)({
164
- name: 'no-unhandled-promise',
165
- meta: {
166
- type: 'problem',
167
- docs: {
168
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-unhandled-promise.md',
169
- description: 'Detects unhandled Promise rejections',
170
- cwe: 'CWE-1024',
171
- cvss: 7.5,
172
- },
173
- hasSuggestions: true,
174
- messages: {
175
- unhandledPromise: (0, eslint_devkit_1.formatLLMMessage)({
176
- icon: eslint_devkit_1.MessageIcons.WARNING,
177
- issueName: 'Unhandled promise',
178
- cwe: 'CWE-1024',
179
- description: 'Unhandled Promise rejection detected',
180
- severity: 'HIGH',
181
- fix: 'Add .catch() handler or use try/catch with await',
182
- documentationLink: 'https://rules.sonarsource.com/javascript/RSPEC-4635/',
183
- }),
184
- addCatch: (0, eslint_devkit_1.formatLLMMessage)({
185
- icon: eslint_devkit_1.MessageIcons.INFO,
186
- issueName: 'Add catch handler',
187
- description: 'Add .catch() handler to promise',
188
- severity: 'LOW',
189
- fix: 'promise.catch(error => console.error(error))',
190
- documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch',
191
- }),
192
- useTryCatch: (0, eslint_devkit_1.formatLLMMessage)({
193
- icon: eslint_devkit_1.MessageIcons.INFO,
194
- issueName: 'Use try/catch',
195
- description: 'Use try/catch with await',
196
- severity: 'LOW',
197
- fix: 'try { await promise; } catch (error) { handle(error); }',
198
- documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch',
199
- }),
200
- useAwait: (0, eslint_devkit_1.formatLLMMessage)({
201
- icon: eslint_devkit_1.MessageIcons.INFO,
202
- issueName: 'Use await',
203
- description: 'Use await to handle promise',
204
- severity: 'LOW',
205
- fix: 'await promise;',
206
- documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await',
207
- }),
208
- },
209
- schema: [
210
- {
211
- type: 'object',
212
- properties: {
213
- ignoreInTests: {
214
- type: 'boolean',
215
- default: true,
216
- description: 'Ignore promises in test files',
217
- },
218
- ignoreVoidExpressions: {
219
- type: 'boolean',
220
- default: false,
221
- description: 'Ignore promises in void expressions',
222
- },
223
- },
224
- additionalProperties: false,
225
- },
226
- ],
227
- },
228
- defaultOptions: [
229
- {
230
- ignoreInTests: true,
231
- ignoreVoidExpressions: false,
232
- },
233
- ],
234
- create(context, [options = {}]) {
235
- const { ignoreInTests = true, ignoreVoidExpressions = false } = options || {};
236
- const filename = context.filename;
237
- const isTestFile = ignoreInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
238
- if (isTestFile) {
239
- return {};
240
- }
241
- function checkCallExpression(node) {
242
- if (isInsidePromiseCallback(node)) {
243
- return;
244
- }
245
- if (isPromiseDelegatedToCaller(node)) {
246
- return;
247
- }
248
- if (node.callee.type === 'MemberExpression' &&
249
- node.callee.property.type === 'Identifier') {
250
- const methodName = node.callee.property.name;
251
- if (methodName === 'then' ||
252
- methodName === 'catch' ||
253
- methodName === 'finally') {
254
- if (node.arguments.length > 0 &&
255
- node.arguments[0].type === 'ArrowFunctionExpression') {
256
- const callback = node.arguments[0];
257
- if (callback.body.type === 'BlockStatement' &&
258
- callback.body.body.length === 0) {
259
- }
260
- else {
261
- return;
262
- }
263
- }
264
- else {
265
- return;
266
- }
267
- }
268
- }
269
- if (!isLikelyPromiseExpression(node)) {
270
- return;
271
- }
272
- if (isPromiseHandled(node)) {
273
- return;
274
- }
275
- if (ignoreVoidExpressions) {
276
- const parent = node
277
- .parent;
278
- if (parent &&
279
- parent.type === 'UnaryExpression' &&
280
- parent.operator === 'void') {
281
- return;
282
- }
283
- }
284
- const parent = node
285
- .parent;
286
- if (parent && parent.type === 'CallExpression') {
287
- const grandParent = parent.parent;
288
- if (!(grandParent &&
289
- grandParent.type === 'MemberExpression' &&
290
- grandParent.object === parent &&
291
- grandParent.property.type === 'Identifier' &&
292
- (grandParent.property.name === 'then' ||
293
- grandParent.property.name === 'catch' ||
294
- grandParent.property.name === 'finally'))) {
295
- return;
296
- }
297
- }
298
- context.report({
299
- node,
300
- messageId: 'unhandledPromise',
301
- suggest: [
302
- {
303
- messageId: 'addCatch',
304
- fix: () => null,
305
- },
306
- {
307
- messageId: 'useTryCatch',
308
- fix: () => null,
309
- },
310
- {
311
- messageId: 'useAwait',
312
- fix: () => null,
313
- },
314
- ],
315
- });
316
- }
317
- return {
318
- CallExpression: checkCallExpression,
319
- };
320
- },
321
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUnhandledPromise=void 0;exports.isLikelyPromiseExpression=isLikelyPromiseExpression;exports.isPromiseHandled=isPromiseHandled;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const NEVER_RETURNS_PROMISE_FUNCTIONS=new Set(["setTimeout","clearTimeout","setInterval","clearInterval","setImmediate","clearImmediate","requestAnimationFrame","cancelAnimationFrame","queueMicrotask","String","Number","Boolean","Symbol","BigInt","parseInt","parseFloat","isNaN","isFinite","Array","Object"]);const NEVER_RETURNS_PROMISE_METHODS=new Set(["log","error","warn","info","debug","trace","group","groupEnd","time","timeEnd","assert","floor","ceil","round","abs","min","max","pow","sqrt","random","sin","cos","tan","log2","log10","slice","split","join","concat","includes","indexOf","lastIndexOf","startsWith","endsWith","replace","replaceAll","trim","toLowerCase","toUpperCase","repeat","padStart","padEnd","charAt","charCodeAt","codePointAt","push","pop","shift","unshift","splice","reverse","sort","map","filter","reduce","reduceRight","forEach","every","some","find","findIndex","flat","flatMap","fill","copyWithin","entries","keys","values","parse","stringify","abort","addEventListener","removeEventListener","dispatchEvent","now","parse","UTC","from","of","isArray","isBuffer","isInteger","isFinite","isNaN","isSafeInteger","fromCharCode","fromCodePoint","raw","assign","freeze","isFrozen","create","defineProperty","defineProperties","getOwnPropertyDescriptor","getOwnPropertyNames","getPrototypeOf","setPrototypeOf","preventExtensions","isExtensible","seal","isSealed","fromEntries"]);const SYNC_NAMESPACE_OBJECTS=new Set(["Math","JSON","Date","Buffer","Array","Object","Number","String","Boolean","Symbol","BigInt","Reflect","console","process"]);function isLikelyPromiseExpression(node){if(node.type!=="CallExpression")return false;const callee=node.callee;if(callee.type==="Identifier"){const name=callee.name;if(NEVER_RETURNS_PROMISE_FUNCTIONS.has(name))return false;return true}if(callee.type==="MemberExpression"){const prop=callee.property;if(prop.type==="Identifier"){if(NEVER_RETURNS_PROMISE_METHODS.has(prop.name))return false}const obj=callee.object;if(obj.type==="Identifier"&&SYNC_NAMESPACE_OBJECTS.has(obj.name)){return false}return true}return true}function isPromiseDelegatedToCaller(node){const parent=node.parent;if(!parent)return false;if(parent.type==="ReturnStatement")return true;if(parent.type==="ArrowFunctionExpression"&&parent.body===node)return true;return false}function isInsidePromiseCallback(node){let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(!parent)break;if(parent.type==="ArrowFunctionExpression"||parent.type==="FunctionExpression"){const funcParent=parent.parent;if(funcParent&&funcParent.type==="CallExpression"&&funcParent.callee&&funcParent.callee.type==="MemberExpression"){const memberExpr=funcParent.callee;if(memberExpr.property.type==="Identifier"){const methodName=memberExpr.property.name;if(methodName==="then"||methodName==="catch"||methodName==="finally"){return true}}}}current=parent;depth++}return false}function isPromiseHandled(node){if(node.type==="Identifier"){const parent=node.parent;if(parent&&parent.type==="MemberExpression"&&parent.object===node){if(parent.property.type==="Identifier"){const methodName=parent.property.name;if(methodName==="catch"||methodName==="then"||methodName==="finally"){const memberParent=parent.parent;if(memberParent&&memberParent.type==="CallExpression"&&memberParent.callee===parent){return true}}}}}let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(!parent)break;if(parent.type==="MemberExpression"&&parent.object===current){if(parent.property.type==="Identifier"){const methodName=parent.property.name;if(methodName==="catch"||methodName==="then"||methodName==="finally"){const memberParent=parent.parent;if(memberParent&&memberParent.type==="CallExpression"&&memberParent.callee===parent){return true}}}}if(parent.type==="TryStatement"){return true}if(parent.type==="AwaitExpression"){return true}current=parent;depth++}return false}exports.noUnhandledPromise=(0,eslint_devkit_2.createRule)({name:"no-unhandled-promise",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-unhandled-promise.md",description:"Detects unhandled Promise rejections",cwe:"CWE-1024",cvss:7.5},hasSuggestions:true,messages:{unhandledPromise:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Unhandled promise",cwe:"CWE-1024",description:"Unhandled Promise rejection detected",severity:"HIGH",fix:"Add .catch() handler or use try/catch with await",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-4635/"}),addCatch:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add catch handler",description:"Add .catch() handler to promise",severity:"LOW",fix:"promise.catch(error => console.error(error))",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch"}),useTryCatch:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use try/catch",description:"Use try/catch with await",severity:"LOW",fix:"try { await promise; } catch (error) { handle(error); }",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch"}),useAwait:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use await",description:"Use await to handle promise",severity:"LOW",fix:"await promise;",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true,description:"Ignore promises in test files"},ignoreVoidExpressions:{type:"boolean",default:false,description:"Ignore promises in void expressions"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,ignoreVoidExpressions:false}],create(context,[options={}]){const{ignoreInTests=true,ignoreVoidExpressions=false}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);if(isTestFile){return{}}function checkCallExpression(node){if(isInsidePromiseCallback(node)){return}if(isPromiseDelegatedToCaller(node)){return}if(node.callee.type==="MemberExpression"&&node.callee.property.type==="Identifier"){const methodName=node.callee.property.name;if(methodName==="then"||methodName==="catch"||methodName==="finally"){if(node.arguments.length>0&&node.arguments[0].type==="ArrowFunctionExpression"){const callback=node.arguments[0];if(callback.body.type==="BlockStatement"&&callback.body.body.length===0){}else{return}}else{return}}}if(!isLikelyPromiseExpression(node)){return}if(isPromiseHandled(node)){return}if(ignoreVoidExpressions){const parent2=node.parent;if(parent2&&parent2.type==="UnaryExpression"&&parent2.operator==="void"){return}}const parent=node.parent;if(parent&&parent.type==="CallExpression"){const grandParent=parent.parent;if(!(grandParent&&grandParent.type==="MemberExpression"&&grandParent.object===parent&&grandParent.property.type==="Identifier"&&(grandParent.property.name==="then"||grandParent.property.name==="catch"||grandParent.property.name==="finally"))){return}}context.report({node,messageId:"unhandledPromise",suggest:[{messageId:"addCatch",fix:()=>null},{messageId:"useTryCatch",fix:()=>null},{messageId:"useAwait",fix:()=>null}]})}return{CallExpression:checkCallExpression}}});
@@ -1,218 +1 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.noAwaitInLoop = void 0;
4
- const eslint_devkit_1 = require("@interlace/eslint-devkit");
5
- const eslint_devkit_2 = require("@interlace/eslint-devkit");
6
- exports.noAwaitInLoop = (0, eslint_devkit_1.createRule)({
7
- name: 'no-await-in-loop',
8
- meta: {
9
- type: 'suggestion',
10
- docs: {
11
- url: 'https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-await-in-loop.md',
12
- description: 'Disallow await inside loops and suggest appropriate concurrency patterns',
13
- },
14
- hasSuggestions: false,
15
- messages: {
16
- awaitInLoop: (0, eslint_devkit_2.formatLLMMessage)({
17
- icon: eslint_devkit_2.MessageIcons.WARNING,
18
- issueName: 'Sequential Async Loop',
19
- description: 'Await in loop forces sequential execution',
20
- severity: 'MEDIUM',
21
- fix: 'Consider Promise.all() for concurrent execution or extract async logic',
22
- documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function',
23
- }),
24
- suggestPromiseAll: (0, eslint_devkit_2.formatLLMMessage)({
25
- icon: eslint_devkit_2.MessageIcons.INFO,
26
- issueName: 'Use Promise.all',
27
- description: 'Concurrent execution of independent operations',
28
- severity: 'LOW',
29
- fix: 'Promise.all(items.map(async (item) => await process(item)))',
30
- documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all',
31
- }),
32
- suggestConcurrent: (0, eslint_devkit_2.formatLLMMessage)({
33
- icon: eslint_devkit_2.MessageIcons.INFO,
34
- issueName: 'Use Promise.allSettled',
35
- description: 'Concurrent execution with error handling',
36
- severity: 'LOW',
37
- fix: 'Promise.allSettled(items.map(async (item) => await process(item)))',
38
- documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled',
39
- }),
40
- considerSequential: (0, eslint_devkit_2.formatLLMMessage)({
41
- icon: eslint_devkit_2.MessageIcons.INFO,
42
- issueName: 'Sequential Control',
43
- description: 'Operations require sequential execution',
44
- severity: 'LOW',
45
- fix: 'Add concurrency control or extract to async function',
46
- documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function',
47
- }),
48
- asyncLoopPattern: (0, eslint_devkit_2.formatLLMMessage)({
49
- icon: eslint_devkit_2.MessageIcons.INFO,
50
- issueName: 'Async Loop Pattern',
51
- description: 'Use controlled concurrency library',
52
- severity: 'LOW',
53
- fix: 'Consider p-map, p-series, or similar for controlled concurrency',
54
- documentationLink: 'https://github.com/sindresorhus/p-map',
55
- }),
56
- },
57
- schema: [
58
- {
59
- type: 'object',
60
- properties: {
61
- allowForOf: {
62
- type: 'boolean',
63
- default: false,
64
- },
65
- allowWhile: {
66
- type: 'boolean',
67
- default: false,
68
- },
69
- checkConcurrency: {
70
- type: 'boolean',
71
- default: true,
72
- },
73
- },
74
- additionalProperties: false,
75
- },
76
- ],
77
- },
78
- defaultOptions: [{ allowForOf: false, allowWhile: false, checkConcurrency: true }],
79
- create(context) {
80
- const [options] = context.options;
81
- const { allowForOf = false, allowWhile = false } = options || {};
82
- function analyzeLoop(node, loopType) {
83
- if ((loopType === 'ForOfStatement' && allowForOf) ||
84
- (loopType === 'WhileStatement' && allowWhile)) {
85
- return;
86
- }
87
- const awaitExpressions = [];
88
- const skipProperties = new Set(['parent', 'tokens', 'comments', 'loc', 'range']);
89
- function findAwaits(currentNode) {
90
- if (currentNode.type === 'AwaitExpression') {
91
- awaitExpressions.push(currentNode);
92
- }
93
- if (currentNode.type !== 'FunctionDeclaration' &&
94
- currentNode.type !== 'FunctionExpression' &&
95
- currentNode.type !== 'ArrowFunctionExpression') {
96
- for (const key in currentNode) {
97
- if (skipProperties.has(key))
98
- continue;
99
- const child = currentNode[key];
100
- if (Array.isArray(child)) {
101
- child.forEach(item => {
102
- if (item && typeof item === 'object' && 'type' in item) {
103
- findAwaits(item);
104
- }
105
- });
106
- }
107
- else if (child && typeof child === 'object' && 'type' in child) {
108
- findAwaits(child);
109
- }
110
- }
111
- }
112
- }
113
- findAwaits(node);
114
- if (awaitExpressions.length > 0) {
115
- const loopContext = analyzeLoopContext(node, loopType);
116
- for (const awaitExpr of awaitExpressions) {
117
- context.report({
118
- node: awaitExpr,
119
- messageId: 'awaitInLoop',
120
- data: {
121
- loopType: loopContext.loopType,
122
- operations: loopContext.operationCount,
123
- suggestion: getSuggestion(loopContext),
124
- performance: loopContext.estimatedPerformance,
125
- },
126
- });
127
- }
128
- }
129
- }
130
- function analyzeLoopContext(node, loopType) {
131
- const loopCtx = {
132
- loopType,
133
- operationCount: 1,
134
- hasDependencies: false,
135
- isSequential: true,
136
- hasSideEffects: false,
137
- estimatedPerformance: 'unknown',
138
- operations: [],
139
- node,
140
- };
141
- const skipProps = new Set(['parent', 'tokens', 'comments', 'loc', 'range']);
142
- function analyzeOperations(currentNode) {
143
- if (currentNode.type === 'CallExpression') {
144
- if (currentNode.callee.type === 'Identifier') {
145
- loopCtx.operations.push(currentNode.callee.name);
146
- }
147
- else if (currentNode.callee.type === 'MemberExpression' &&
148
- currentNode.callee.property.type === 'Identifier') {
149
- loopCtx.operations.push(currentNode.callee.property.name);
150
- }
151
- }
152
- if (currentNode.type === 'AssignmentExpression' ||
153
- currentNode.type === 'UpdateExpression') {
154
- loopCtx.hasDependencies = true;
155
- }
156
- if (currentNode.type === 'CallExpression' &&
157
- (loopCtx.operations.includes('push') ||
158
- loopCtx.operations.includes('splice') ||
159
- loopCtx.operations.includes('delete'))) {
160
- loopCtx.hasSideEffects = true;
161
- }
162
- if (currentNode.type === 'AwaitExpression') {
163
- loopCtx.operationCount++;
164
- }
165
- for (const key in currentNode) {
166
- if (skipProps.has(key))
167
- continue;
168
- const child = currentNode[key];
169
- if (Array.isArray(child)) {
170
- child.forEach(item => {
171
- if (item && typeof item === 'object' && 'type' in item) {
172
- analyzeOperations(item);
173
- }
174
- });
175
- }
176
- else if (child && typeof child === 'object' && 'type' in child) {
177
- analyzeOperations(child);
178
- }
179
- }
180
- }
181
- analyzeOperations(node);
182
- if (loopCtx.hasDependencies || loopCtx.hasSideEffects) {
183
- loopCtx.isSequential = true;
184
- loopCtx.estimatedPerformance = `sequential (${loopCtx.operationCount} operations)`;
185
- }
186
- else {
187
- loopCtx.isSequential = false;
188
- loopCtx.estimatedPerformance = `potential ${loopCtx.operationCount}x speedup with concurrency`;
189
- }
190
- return loopCtx;
191
- }
192
- function getSuggestion(loopCtx) {
193
- if (loopCtx.isSequential) {
194
- return 'operations may need to be sequential - consider if concurrency is safe';
195
- }
196
- else {
197
- return 'operations appear independent - consider Promise.all() for concurrency';
198
- }
199
- }
200
- return {
201
- ForStatement(node) {
202
- analyzeLoop(node, 'ForStatement');
203
- },
204
- ForInStatement(node) {
205
- analyzeLoop(node, 'ForInStatement');
206
- },
207
- ForOfStatement(node) {
208
- analyzeLoop(node, 'ForOfStatement');
209
- },
210
- WhileStatement(node) {
211
- analyzeLoop(node, 'WhileStatement');
212
- },
213
- DoWhileStatement(node) {
214
- analyzeLoop(node, 'DoWhileStatement');
215
- },
216
- };
217
- },
218
- });
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noAwaitInLoop=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");exports.noAwaitInLoop=(0,eslint_devkit_1.createRule)({name:"no-await-in-loop",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-await-in-loop.md",description:"Disallow await inside loops and suggest appropriate concurrency patterns"},hasSuggestions:false,messages:{awaitInLoop:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.WARNING,issueName:"Sequential Async Loop",description:"Await in loop forces sequential execution",severity:"MEDIUM",fix:"Consider Promise.all() for concurrent execution or extract async logic",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function"}),suggestPromiseAll:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.INFO,issueName:"Use Promise.all",description:"Concurrent execution of independent operations",severity:"LOW",fix:"Promise.all(items.map(async (item) => await process(item)))",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all"}),suggestConcurrent:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.INFO,issueName:"Use Promise.allSettled",description:"Concurrent execution with error handling",severity:"LOW",fix:"Promise.allSettled(items.map(async (item) => await process(item)))",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled"}),considerSequential:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.INFO,issueName:"Sequential Control",description:"Operations require sequential execution",severity:"LOW",fix:"Add concurrency control or extract to async function",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function"}),asyncLoopPattern:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.INFO,issueName:"Async Loop Pattern",description:"Use controlled concurrency library",severity:"LOW",fix:"Consider p-map, p-series, or similar for controlled concurrency",documentationLink:"https://github.com/sindresorhus/p-map"})},schema:[{type:"object",properties:{allowForOf:{type:"boolean",default:false},allowWhile:{type:"boolean",default:false},checkConcurrency:{type:"boolean",default:true}},additionalProperties:false}]},defaultOptions:[{allowForOf:false,allowWhile:false,checkConcurrency:true}],create(context){const[options]=context.options;const{allowForOf=false,allowWhile=false}=options||{};function analyzeLoop(node,loopType){if(loopType==="ForOfStatement"&&allowForOf||loopType==="WhileStatement"&&allowWhile){return}const awaitExpressions=[];const skipProperties=new Set(["parent","tokens","comments","loc","range"]);function findAwaits(currentNode){if(currentNode.type==="AwaitExpression"){awaitExpressions.push(currentNode)}if(currentNode.type!=="FunctionDeclaration"&&currentNode.type!=="FunctionExpression"&&currentNode.type!=="ArrowFunctionExpression"){for(const key in currentNode){if(skipProperties.has(key))continue;const child=currentNode[key];if(Array.isArray(child)){child.forEach(item=>{if(item&&typeof item==="object"&&"type"in item){findAwaits(item)}})}else if(child&&typeof child==="object"&&"type"in child){findAwaits(child)}}}}findAwaits(node);if(awaitExpressions.length>0){const loopContext=analyzeLoopContext(node,loopType);for(const awaitExpr of awaitExpressions){context.report({node:awaitExpr,messageId:"awaitInLoop",data:{loopType:loopContext.loopType,operations:loopContext.operationCount,suggestion:getSuggestion(loopContext),performance:loopContext.estimatedPerformance}})}}}function analyzeLoopContext(node,loopType){const loopCtx={loopType,operationCount:1,hasDependencies:false,isSequential:true,hasSideEffects:false,estimatedPerformance:"unknown",operations:[],node};const skipProps=new Set(["parent","tokens","comments","loc","range"]);function analyzeOperations(currentNode){if(currentNode.type==="CallExpression"){if(currentNode.callee.type==="Identifier"){loopCtx.operations.push(currentNode.callee.name)}else if(currentNode.callee.type==="MemberExpression"&&currentNode.callee.property.type==="Identifier"){loopCtx.operations.push(currentNode.callee.property.name)}}if(currentNode.type==="AssignmentExpression"||currentNode.type==="UpdateExpression"){loopCtx.hasDependencies=true}if(currentNode.type==="CallExpression"&&(loopCtx.operations.includes("push")||loopCtx.operations.includes("splice")||loopCtx.operations.includes("delete"))){loopCtx.hasSideEffects=true}if(currentNode.type==="AwaitExpression"){loopCtx.operationCount++}for(const key in currentNode){if(skipProps.has(key))continue;const child=currentNode[key];if(Array.isArray(child)){child.forEach(item=>{if(item&&typeof item==="object"&&"type"in item){analyzeOperations(item)}})}else if(child&&typeof child==="object"&&"type"in child){analyzeOperations(child)}}}analyzeOperations(node);if(loopCtx.hasDependencies||loopCtx.hasSideEffects){loopCtx.isSequential=true;loopCtx.estimatedPerformance=`sequential (${loopCtx.operationCount} operations)`}else{loopCtx.isSequential=false;loopCtx.estimatedPerformance=`potential ${loopCtx.operationCount}x speedup with concurrency`}return loopCtx}function getSuggestion(loopCtx){if(loopCtx.isSequential){return"operations may need to be sequential - consider if concurrency is safe"}else{return"operations appear independent - consider Promise.all() for concurrency"}}return{ForStatement(node){analyzeLoop(node,"ForStatement")},ForInStatement(node){analyzeLoop(node,"ForInStatement")},ForOfStatement(node){analyzeLoop(node,"ForOfStatement")},WhileStatement(node){analyzeLoop(node,"WhileStatement")},DoWhileStatement(node){analyzeLoop(node,"DoWhileStatement")}}}});