eslint-plugin-reliability 4.1.10 → 4.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-reliability",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.11",
|
|
4
4
|
"description": "ESLint plugin for runtime reliability — enforces error handling, network timeouts, null checks, and safe type narrowing.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"node": ">=18.0.0"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@interlace/eslint-devkit": "^1.19.
|
|
53
|
+
"@interlace/eslint-devkit": "^1.19.6"
|
|
54
54
|
},
|
|
55
55
|
"repository": {
|
|
56
56
|
"type": "git",
|
package/src/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.rules={get"no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"no-missing-null-checks"(){return require("./rules/reliability/no-missing-null-checks").noMissingNullChecks},get"no-unsafe-type-narrowing"(){return require("./rules/reliability/no-unsafe-type-narrowing").noUnsafeTypeNarrowing},get"require-network-timeout"(){return require("./rules/reliability/require-network-timeout").requireNetworkTimeout},get"no-await-in-loop"(){return require("./rules/reliability/no-await-in-loop").noAwaitInLoop},get"no-jsdoc-terminator-in-example"(){return require("./rules/reliability/no-jsdoc-terminator-in-example").noJsdocTerminatorInExample},get"error-handling/no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"error-handling/no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"error-handling/no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"error-handling/error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"reliability/no-missing-null-checks"(){return require("./rules/reliability/no-missing-null-checks").noMissingNullChecks},get"reliability/no-unsafe-type-narrowing"(){return require("./rules/reliability/no-unsafe-type-narrowing").noUnsafeTypeNarrowing},get"reliability/require-network-timeout"(){return require("./rules/reliability/require-network-timeout").requireNetworkTimeout},get"reliability/no-await-in-loop"(){return require("./rules/reliability/no-await-in-loop").noAwaitInLoop},get"reliability/no-jsdoc-terminator-in-example"(){return require("./rules/reliability/no-jsdoc-terminator-in-example").noJsdocTerminatorInExample}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-reliability",exports.rules);exports.plugin={meta:{name:"eslint-plugin-reliability",version:"4.1.
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");exports.rules={get"no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"no-missing-null-checks"(){return require("./rules/reliability/no-missing-null-checks").noMissingNullChecks},get"no-unsafe-type-narrowing"(){return require("./rules/reliability/no-unsafe-type-narrowing").noUnsafeTypeNarrowing},get"require-network-timeout"(){return require("./rules/reliability/require-network-timeout").requireNetworkTimeout},get"no-await-in-loop"(){return require("./rules/reliability/no-await-in-loop").noAwaitInLoop},get"no-jsdoc-terminator-in-example"(){return require("./rules/reliability/no-jsdoc-terminator-in-example").noJsdocTerminatorInExample},get"error-handling/no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"error-handling/no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"error-handling/no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"error-handling/error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"reliability/no-missing-null-checks"(){return require("./rules/reliability/no-missing-null-checks").noMissingNullChecks},get"reliability/no-unsafe-type-narrowing"(){return require("./rules/reliability/no-unsafe-type-narrowing").noUnsafeTypeNarrowing},get"reliability/require-network-timeout"(){return require("./rules/reliability/require-network-timeout").requireNetworkTimeout},get"reliability/no-await-in-loop"(){return require("./rules/reliability/no-await-in-loop").noAwaitInLoop},get"reliability/no-jsdoc-terminator-in-example"(){return require("./rules/reliability/no-jsdoc-terminator-in-example").noJsdocTerminatorInExample}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-reliability",exports.rules);exports.plugin={meta:{name:"eslint-plugin-reliability",version:"4.1.11"},rules:exports.rules};exports.configs={recommended:{plugins:{reliability:exports.plugin},rules:{"reliability/no-silent-errors":"error","reliability/no-missing-null-checks":"warn","reliability/require-network-timeout":"error"}}};exports.default=exports.plugin;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUnhandledPromise=void 0;exports.hasPromiseEvidence=hasPromiseEvidence;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"]);const PROMISE_STATICS=new Set(["all","allSettled","any","race","resolve","reject"]);function isAsyncFunctionNode(node){if(!node)return false;return(node.type==="FunctionDeclaration"||node.type==="FunctionExpression"||node.type==="ArrowFunctionExpression")&&node.async===true}function hasPromiseEvidence(node,promiseReturning,resolveBinding){if(isAsyncFunctionNode(node.callee))return true;if(node.callee.type==="MemberExpression"){const{object}=node.callee;if(object.type==="Identifier"&&object.name==="Promise"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(node.callee),PROMISE_STATICS)){return true}const member=(0,eslint_devkit_1.propertyName)(node.callee);if(member==="then")return true;return object.type==="Identifier"&&promiseReturning.has(object.name)}if(node.callee.type==="Identifier"){if(promiseReturning.has(node.callee.name))return true;const bound=resolveBinding(node.callee.name);return isAsyncFunctionNode(bound)||isPromiseReturningAnnotation(bound)}return false}function isPromiseReturningAnnotation(node){if(node?.type!=="Identifier")return false;const annotation=node.typeAnnotation?.typeAnnotation;if(annotation?.type!=="TSFunctionType")return false;const returned=annotation.returnType?.typeAnnotation;return returned?.type==="TSTypeReference"&&returned.typeName.type==="Identifier"&&returned.typeName.name==="Promise"}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;const settled=(0,eslint_devkit_1.propertyName)(memberExpr);if(settled!==null){const methodName=settled;if(methodName==="then"||methodName==="catch"||methodName==="finally"){return true}}}}current=parent;depth++}return false}const PROMISE_COMBINATORS=["all","allSettled","any","race"];function isPromiseCombinatorCall(node){const{callee}=node;return callee.type==="MemberExpression"&&callee.object.type==="Identifier"&&callee.object.name==="Promise"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(callee),PROMISE_COMBINATORS)}function isValueConsumed(node){let current=node;for(;;){const parent=current.parent;if(!parent)return false;switch(parent.type){case"MemberExpression":return false;case"CallExpression":if(parent.callee!==current){return current.type===eslint_devkit_1.AST_NODE_TYPES.ArrayExpression&&isPromiseCombinatorCall(parent)}break;case"ArrayExpression":break;case"TSAsExpression":case"TSNonNullExpression":break;case"UnaryExpression":if(parent.operator!=="void")return true;break;case"ExpressionStatement":return false;case"SequenceExpression":return parent.expressions[parent.expressions.length-1]===current;default:return true}current=parent}}function isThenWithRejectionHandler(node){if(node.callee.type!=="MemberExpression")return false;if((0,eslint_devkit_1.propertyName)(node.callee)!=="then")return false;const onRejected=node.arguments[1];if(onRejected===void 0)return false;let candidate=onRejected;while(candidate.type===eslint_devkit_1.AST_NODE_TYPES.TSAsExpression||candidate.type===eslint_devkit_1.AST_NODE_TYPES.TSSatisfiesExpression||candidate.type===eslint_devkit_1.AST_NODE_TYPES.TSNonNullExpression||candidate.type===eslint_devkit_1.AST_NODE_TYPES.TSTypeAssertion){candidate=candidate.expression}switch(candidate.type){case eslint_devkit_1.AST_NODE_TYPES.ArrowFunctionExpression:case eslint_devkit_1.AST_NODE_TYPES.FunctionExpression:case eslint_devkit_1.AST_NODE_TYPES.MemberExpression:return true;case eslint_devkit_1.AST_NODE_TYPES.Identifier:return candidate.name!=="undefined";default: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"},requirePromiseEvidence:{type:"boolean",default:true,description:"Report only calls the file itself shows to be promise-producing."},promiseReturning:{type:"array",items:{type:"string"},default:["fetch"],description:"Names that return a promise and cannot be resolved from the file."},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,requirePromiseEvidence=true,promiseReturning=["fetch"]}=options||{};const promiseNames=new Set(promiseReturning);function resolveBinding(name,from){let scope=context.sourceCode.getScope(from);while(scope){const variable=scope.variables.find(v=>v.name===name);if(variable){const def=variable.defs[0];if(!def)return null;if(def.type==="Parameter"){const pattern=def.name.parent;if(pattern?.type==="AssignmentPattern"&&pattern.left===def.name){return pattern.right}return def.name}if(def.node.type==="FunctionDeclaration")return def.node;if(def.node.type==="VariableDeclarator"){return def.node.init??null}return null}scope=scope.upper}return null}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==="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(requirePromiseEvidence&&!hasPromiseEvidence(node,promiseNames,name=>resolveBinding(name,node))){return}if(isPromiseHandled(node)){return}if(isValueConsumed(node)){return}if(isThenWithRejectionHandler(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;const chainOn=grandParent?.type==="MemberExpression"&&grandParent.object===parent?grandParent:null;const outerChainMethod=chainOn===null?null:(0,eslint_devkit_1.propertyName)(chainOn);const inPromiseChain=chainOn!==null&&chainOn.property.type==="Identifier"&&(outerChainMethod==="then"||outerChainMethod==="catch"||outerChainMethod==="finally");const isArgument=parent.arguments.some(a=>a===node);const outerIsPromise=outerChainMethod==="then"||outerChainMethod==="catch"||outerChainMethod==="finally"||hasPromiseEvidence(parent,promiseNames,name=>resolveBinding(name,parent));if(!inPromiseChain&&(!isArgument||outerIsPromise)){return}}context.report({node,messageId:"unhandledPromise",suggest:[{messageId:"addCatch",fix:()=>null},{messageId:"useTryCatch",fix:()=>null},{messageId:"useAwait",fix:()=>null}]})}function checkStatementPromise(node){const parent=node.parent;if(parent?.type!=="ExpressionStatement")return;context.report({node,messageId:"unhandledPromise"})}return{CallExpression:checkCallExpression,ImportExpression:checkStatementPromise,NewExpression(node){if(node.callee.type==="Identifier"&&node.callee.name==="Promise"){checkStatementPromise(node)}}}}});
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUnhandledPromise=void 0;exports.hasPromiseEvidence=hasPromiseEvidence;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"]);const PROMISE_STATICS=new Set(["all","allSettled","any","race","resolve","reject"]);function isAsyncFunctionNode(node){if(!node)return false;return(node.type==="FunctionDeclaration"||node.type==="FunctionExpression"||node.type==="ArrowFunctionExpression")&&node.async===true}function hasPromiseEvidence(node,promiseReturning,resolveBinding){if(isAsyncFunctionNode(node.callee))return true;if(node.callee.type==="MemberExpression"){const{object}=node.callee;if(object.type==="Identifier"&&object.name==="Promise"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(node.callee),PROMISE_STATICS)){return true}const member=(0,eslint_devkit_1.propertyName)(node.callee);if(member==="then")return true;return object.type==="Identifier"&&promiseReturning.has(object.name)}if(node.callee.type==="Identifier"){if(promiseReturning.has(node.callee.name))return true;const bound=resolveBinding(node.callee.name);return isAsyncFunctionNode(bound)||isPromiseReturningAnnotation(bound)}return false}function isPromiseReturningAnnotation(node){if(node?.type!=="Identifier")return false;const annotation=node.typeAnnotation?.typeAnnotation;if(annotation?.type!=="TSFunctionType")return false;const returned=annotation.returnType?.typeAnnotation;return returned?.type==="TSTypeReference"&&returned.typeName.type==="Identifier"&&returned.typeName.name==="Promise"}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;const settled=(0,eslint_devkit_1.propertyName)(memberExpr);if(settled!==null){const methodName=settled;if(methodName==="then"||methodName==="catch"||methodName==="finally"){return true}}}}current=parent;depth++}return false}const PROMISE_COMBINATORS=["all","allSettled","any","race"];function isPromiseCombinatorCall(node){const{callee}=node;return callee.type==="MemberExpression"&&callee.object.type==="Identifier"&&callee.object.name==="Promise"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(callee),PROMISE_COMBINATORS)}function isValueConsumed(node){let current=node;for(;;){const parent=current.parent;if(!parent)return false;switch(parent.type){case"MemberExpression":return false;case"CallExpression":if(parent.callee!==current){return current.type===eslint_devkit_1.AST_NODE_TYPES.ArrayExpression&&isPromiseCombinatorCall(parent)}break;case"ArrayExpression":break;case"TSAsExpression":case"TSNonNullExpression":break;case"UnaryExpression":if(parent.operator!=="void")return true;break;case"ExpressionStatement":return false;case"SequenceExpression":return parent.expressions[parent.expressions.length-1]===current;default:return true}current=parent}}function isThenWithRejectionHandler(node){if(node.callee.type!=="MemberExpression")return false;if((0,eslint_devkit_1.propertyName)(node.callee)!=="then")return false;const onRejected=node.arguments[1];if(onRejected===void 0)return false;let candidate=onRejected;while(candidate.type===eslint_devkit_1.AST_NODE_TYPES.TSAsExpression||candidate.type===eslint_devkit_1.AST_NODE_TYPES.TSSatisfiesExpression||candidate.type===eslint_devkit_1.AST_NODE_TYPES.TSNonNullExpression||candidate.type===eslint_devkit_1.AST_NODE_TYPES.TSTypeAssertion){candidate=candidate.expression}switch(candidate.type){case eslint_devkit_1.AST_NODE_TYPES.ArrowFunctionExpression:case eslint_devkit_1.AST_NODE_TYPES.FunctionExpression:case eslint_devkit_1.AST_NODE_TYPES.MemberExpression:return true;case eslint_devkit_1.AST_NODE_TYPES.Identifier:return candidate.name!=="undefined";default: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"},requirePromiseEvidence:{type:"boolean",default:true,description:"Report only calls the file itself shows to be promise-producing."},promiseReturning:{type:"array",items:{type:"string"},default:["fetch"],description:"Names that return a promise and cannot be resolved from the file."},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,requirePromiseEvidence=true,promiseReturning=["fetch"]}=options||{};const promiseNames=new Set(promiseReturning);function resolveBinding(name,from){let scope=context.sourceCode.getScope(from);while(scope){const variable=scope.variables.find(v=>v.name===name);if(variable){const def=variable.defs[0];if(!def)return null;if(def.type==="Parameter"){const pattern=def.name.parent;if(pattern?.type==="AssignmentPattern"&&pattern.left===def.name){return pattern.right}return def.name}if(def.node.type==="FunctionDeclaration")return def.node;if(def.node.type==="VariableDeclarator"){return def.node.init??null}return null}scope=scope.upper}return null}const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.[cm]?[jt]sx?$/.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==="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(requirePromiseEvidence&&!hasPromiseEvidence(node,promiseNames,name=>resolveBinding(name,node))){return}if(isPromiseHandled(node)){return}if(isValueConsumed(node)){return}if(isThenWithRejectionHandler(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;const chainOn=grandParent?.type==="MemberExpression"&&grandParent.object===parent?grandParent:null;const outerChainMethod=chainOn===null?null:(0,eslint_devkit_1.propertyName)(chainOn);const inPromiseChain=chainOn!==null&&chainOn.property.type==="Identifier"&&(outerChainMethod==="then"||outerChainMethod==="catch"||outerChainMethod==="finally");const isArgument=parent.arguments.some(a=>a===node);const outerIsPromise=outerChainMethod==="then"||outerChainMethod==="catch"||outerChainMethod==="finally"||hasPromiseEvidence(parent,promiseNames,name=>resolveBinding(name,parent));if(!inPromiseChain&&(!isArgument||outerIsPromise)){return}}context.report({node,messageId:"unhandledPromise",suggest:[{messageId:"addCatch",fix:()=>null},{messageId:"useTryCatch",fix:()=>null},{messageId:"useAwait",fix:()=>null}]})}function checkStatementPromise(node){const parent=node.parent;if(parent?.type!=="ExpressionStatement")return;context.report({node,messageId:"unhandledPromise"})}return{CallExpression:checkCallExpression,ImportExpression:checkStatementPromise,NewExpression(node){if(node.callee.type==="Identifier"&&node.callee.name==="Promise"){checkStatementPromise(node)}}}}});
|