eslint-plugin-reliability 4.1.8 → 4.1.10
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 +1 -1
- package/src/index.js +1 -1
- package/src/rules/error-handling/no-missing-error-context.js +1 -1
- package/src/rules/error-handling/no-silent-errors.js +1 -1
- package/src/rules/reliability/no-await-in-loop.js +1 -1
- package/src/rules/reliability/no-missing-null-checks.js +1 -1
- package/src/rules/reliability/no-unsafe-type-narrowing.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eslint-plugin-reliability",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.10",
|
|
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",
|
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.10"},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.noMissingErrorContext=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const BUILTIN_ERROR_CONSTRUCTORS=new Set(["Error","TypeError","RangeError","SyntaxError","ReferenceError","EvalError","URIError","AggregateError"]);function isProvablyString(node){if(node.type==="TemplateLiteral")return true;if(node.type==="Literal"){return typeof node.value==="string"&&node.value.length>0}if(node.type==="LogicalExpression"){return node.operator==="&&"?isProvablyString(node.right):isProvablyString(node.left)||isProvablyString(node.right)}if(node.type==="ConditionalExpression"){return isProvablyString(node.consequent)||isProvablyString(node.alternate)}if(node.type==="BinaryExpression"&&node.operator==="+"){return isProvablyString(node.left)||isProvablyString(node.right)}return false}function hasErrorMessage(node){if(!node.argument){return false}if(node.argument.type==="Identifier"&&node.argument.name!=="undefined"&&node.argument.name!=="NaN"&&node.argument.name!=="Infinity"){return true}const constructed=node.argument.type==="NewExpression"||node.argument.type==="CallExpression"?node.argument:null;if(constructed!==null&&constructed.callee.type==="Identifier"){if(constructed.type==="CallExpression"&&!BUILTIN_ERROR_CONSTRUCTORS.has(constructed.callee.name)){return false}if(constructed.arguments.length===0){return false}const firstArg=constructed.arguments[0];const staticText=(0,eslint_devkit_1.staticString)(firstArg);if(staticText!==null){return staticText.length>0}if(!BUILTIN_ERROR_CONSTRUCTORS.has(constructed.callee.name)){return true}return isProvablyString(firstArg)}if(node.argument.type==="Literal"&&typeof node.argument.value==="string"){return node.argument.value.length>0}if(node.argument.type==="TemplateLiteral"){return true}return false}function hasErrorStack(node){if(!node.argument){return false}if(node.argument.type==="Identifier"&&node.argument.name!=="undefined"&&node.argument.name!=="NaN"&&node.argument.name!=="Infinity"){return true}if((node.argument.type==="NewExpression"||node.argument.type==="CallExpression")&&node.argument.callee.type==="Identifier"){const calleeName=node.argument.callee.name;if(calleeName==="Error"||calleeName.endsWith("Error")){return true}}return false}exports.noMissingErrorContext=(0,eslint_devkit_2.createRule)({name:"no-missing-error-context",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-missing-error-context.md",description:"Detects thrown errors without context"},hasSuggestions:true,messages:{missingErrorContext:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Missing error context",description:"Thrown error missing {{missing}}",severity:"MEDIUM",fix:"Add error message and use Error class for stack trace",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1128/"}),addErrorMessage:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Error Message",description:"Add descriptive error message",severity:"LOW",fix:'throw new Error("descriptive message")',documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error"}),addErrorStack:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Error Class",description:"Use Error class for stack trace",severity:"LOW",fix:"throw new Error(message) instead of throw message",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error"}),useErrorClass:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Specific Error",description:"Use specific error class",severity:"LOW",fix:'throw new TypeError("message") or throw new RangeError("message")',documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError"})},schema:[{type:"object",properties:{requireMessage:{type:"boolean",default:true,description:"Require error message"},requireStackTrace:{type:"boolean",default:false,description:"Require stack trace"},ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"}},additionalProperties:false}]},defaultOptions:[{requireMessage:true,requireStackTrace:false,ignoreInTests:true}],create(context,[options={}]){const{requireMessage=true,requireStackTrace=false,ignoreInTests=true}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMissingErrorContext=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const BUILTIN_ERROR_CONSTRUCTORS=new Set(["Error","TypeError","RangeError","SyntaxError","ReferenceError","EvalError","URIError","AggregateError"]);function isProvablyString(node,sourceCode){if(node.type==="TemplateLiteral")return true;if(node.type==="Identifier"){const init=constInitializer(node,sourceCode);return init!==null&&isProvablyString(init,sourceCode)}if(node.type==="Literal"){return typeof node.value==="string"&&node.value.length>0}if(node.type==="LogicalExpression"){return node.operator==="&&"?isProvablyString(node.right,sourceCode):isProvablyString(node.left,sourceCode)||isProvablyString(node.right,sourceCode)}if(node.type==="ConditionalExpression"){return isProvablyString(node.consequent,sourceCode)||isProvablyString(node.alternate,sourceCode)}if(node.type==="BinaryExpression"&&node.operator==="+"){return isProvablyString(node.left,sourceCode)||isProvablyString(node.right,sourceCode)}return false}function constInitializer(node,sourceCode){let scope=sourceCode.getScope(node);let variable;while(scope&&!variable){variable=scope.set?.get(node.name);scope=scope.upper}if(!variable||variable.defs.length!==1)return null;const def=variable.defs[0];if(def?.type!=="Variable"||def.parent.kind!=="const")return null;return def.node.init??null}function hasErrorMessage(node,sourceCode){if(!node.argument){return false}if(node.argument.type==="Identifier"&&node.argument.name!=="undefined"&&node.argument.name!=="NaN"&&node.argument.name!=="Infinity"){return true}const constructed=node.argument.type==="NewExpression"||node.argument.type==="CallExpression"?node.argument:null;if(constructed!==null&&constructed.callee.type==="Identifier"){if(constructed.type==="CallExpression"&&!BUILTIN_ERROR_CONSTRUCTORS.has(constructed.callee.name)){return false}if(constructed.arguments.length===0){return false}const firstArg=constructed.arguments[0];const staticText=(0,eslint_devkit_1.staticString)(firstArg);if(staticText!==null){return staticText.length>0}if(!BUILTIN_ERROR_CONSTRUCTORS.has(constructed.callee.name)){return true}return isProvablyString(firstArg,sourceCode)}if(node.argument.type==="Literal"&&typeof node.argument.value==="string"){return node.argument.value.length>0}if(node.argument.type==="TemplateLiteral"){return true}return false}function hasErrorStack(node){if(!node.argument){return false}if(node.argument.type==="Identifier"&&node.argument.name!=="undefined"&&node.argument.name!=="NaN"&&node.argument.name!=="Infinity"){return true}if((node.argument.type==="NewExpression"||node.argument.type==="CallExpression")&&node.argument.callee.type==="Identifier"){const calleeName=node.argument.callee.name;if(calleeName==="Error"||calleeName.endsWith("Error")){return true}}return false}exports.noMissingErrorContext=(0,eslint_devkit_2.createRule)({name:"no-missing-error-context",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-missing-error-context.md",description:"Detects thrown errors without context"},hasSuggestions:true,messages:{missingErrorContext:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Missing error context",description:"Thrown error missing {{missing}}",severity:"MEDIUM",fix:"Add error message and use Error class for stack trace",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1128/"}),addErrorMessage:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Error Message",description:"Add descriptive error message",severity:"LOW",fix:'throw new Error("descriptive message")',documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error"}),addErrorStack:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Error Class",description:"Use Error class for stack trace",severity:"LOW",fix:"throw new Error(message) instead of throw message",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error"}),useErrorClass:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Specific Error",description:"Use specific error class",severity:"LOW",fix:'throw new TypeError("message") or throw new RangeError("message")',documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError"})},schema:[{type:"object",properties:{requireMessage:{type:"boolean",default:true,description:"Require error message"},requireStackTrace:{type:"boolean",default:false,description:"Require stack trace"},ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"}},additionalProperties:false}]},defaultOptions:[{requireMessage:true,requireStackTrace:false,ignoreInTests:true}],create(context,[options={}]){const{requireMessage=true,requireStackTrace=false,ignoreInTests=true}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.[cm]?[jt]sx?$/.test(filename);if(isTestFile){return{}}function checkThrowStatement(node){const missing=[];if(requireMessage&&!hasErrorMessage(node,context.sourceCode)){missing.push("message")}if(requireStackTrace&&!hasErrorStack(node)){missing.push("stack trace")}if(missing.length===0){return}context.report({node,messageId:"missingErrorContext",data:{missing:missing.join(" and ")},suggest:[{messageId:"addErrorMessage",fix:()=>null},{messageId:"addErrorStack",fix:()=>null},{messageId:"useErrorClass",fix:()=>null}]})}return{ThrowStatement:checkThrowStatement}}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noSilentErrors=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function isEmptyCatchBlock(catchClause){const body=catchClause.body;if(!body||body.type!=="BlockStatement"){return true}const statements=body.body;if(statements.length===0){return true}const nonEmptyStatements=statements.filter(stmt=>stmt.type!=="EmptyStatement");return nonEmptyStatements.length===0}function hasExplanatoryComment(catchClause,sourceCode){const comments=sourceCode.getAllComments();const catchStart=catchClause.loc?.start;if(!catchStart||!comments.length){return false}const explanatoryPatterns=[/intentional/i,/expected/i,/ignore/i,/silent/i,/noop/i,/no-op/i,/by design/i,/known issue/i,/legacy/i,/third.?party/i,/framework/i,/library/i,/not implemented/i,/todo/i,/fixme/i];for(const comment of comments){const distance=catchStart.line-comment.loc.end.line;if(distance<0||distance>2)continue;const commentText=comment.value.toLowerCase();if(explanatoryPatterns.some(pattern=>pattern.test(commentText))){return true}}return false}exports.noSilentErrors=(0,eslint_devkit_2.createRule)({name:"no-silent-errors",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-silent-errors.md",description:"Detects empty catch blocks"},hasSuggestions:true,messages:{silentError:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Silent error",description:"Empty catch block detected - errors are silently ignored",severity:"MEDIUM",fix:"Add error logging or handling in catch block",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1186/"}),addErrorLogging:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Error Logging",description:"Add error logging to catch block",severity:"LOW",fix:"catch (error) { console.error(error); }",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1186/"}),addErrorHandling:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Error Handling",description:"Add error handling to catch block",severity:"LOW",fix:"catch (error) { handleError(error); }",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1186/"}),rethrowError:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Rethrow Error",description:"Rethrow error if needed",severity:"LOW",fix:"catch (error) { throw new CustomError(error); }",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1186/"})},schema:[{type:"object",properties:{allowWithComment:{type:"boolean",default:false,description:"Allow empty catch if comment explains why"},ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"}},additionalProperties:false}]},defaultOptions:[{allowWithComment:false,ignoreInTests:true}],create(context,[options={}]){const{allowWithComment=false,ignoreInTests=true}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noSilentErrors=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function isEmptyCatchBlock(catchClause){const body=catchClause.body;if(!body||body.type!=="BlockStatement"){return true}const statements=body.body;if(statements.length===0){return true}const nonEmptyStatements=statements.filter(stmt=>stmt.type!=="EmptyStatement");return nonEmptyStatements.length===0}function hasExplanatoryComment(catchClause,sourceCode){const comments=sourceCode.getAllComments();const catchStart=catchClause.loc?.start;if(!catchStart||!comments.length){return false}const explanatoryPatterns=[/intentional/i,/expected/i,/ignore/i,/silent/i,/noop/i,/no-op/i,/by design/i,/known issue/i,/legacy/i,/third.?party/i,/framework/i,/library/i,/not implemented/i,/todo/i,/fixme/i];for(const comment of comments){const distance=catchStart.line-comment.loc.end.line;if(distance<0||distance>2)continue;const commentText=comment.value.toLowerCase();if(explanatoryPatterns.some(pattern=>pattern.test(commentText))){return true}}for(const comment of sourceCode.getCommentsInside(catchClause.body)){const commentText=comment.value.toLowerCase();if(explanatoryPatterns.some(pattern=>pattern.test(commentText))){return true}}return false}exports.noSilentErrors=(0,eslint_devkit_2.createRule)({name:"no-silent-errors",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-silent-errors.md",description:"Detects empty catch blocks"},hasSuggestions:true,messages:{silentError:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Silent error",description:"Empty catch block detected - errors are silently ignored",severity:"MEDIUM",fix:"Add error logging or handling in catch block",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1186/"}),addErrorLogging:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Error Logging",description:"Add error logging to catch block",severity:"LOW",fix:"catch (error) { console.error(error); }",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1186/"}),addErrorHandling:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Error Handling",description:"Add error handling to catch block",severity:"LOW",fix:"catch (error) { handleError(error); }",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1186/"}),rethrowError:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Rethrow Error",description:"Rethrow error if needed",severity:"LOW",fix:"catch (error) { throw new CustomError(error); }",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-1186/"})},schema:[{type:"object",properties:{allowWithComment:{type:"boolean",default:false,description:"Allow empty catch if comment explains why"},ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"}},additionalProperties:false}]},defaultOptions:[{allowWithComment:false,ignoreInTests:true}],create(context,[options={}]){const{allowWithComment=false,ignoreInTests=true}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.[cm]?[jt]sx?$/.test(filename);if(isTestFile){return{}}const sourceCode=context.sourceCode;function checkCatchClause(node){if(!isEmptyCatchBlock(node)){return}if(allowWithComment&&hasExplanatoryComment(node,sourceCode)){return}context.report({node,messageId:"silentError",suggest:[{messageId:"addErrorLogging",fix:()=>null},{messageId:"addErrorHandling",fix:()=>null},{messageId:"rethrowError",fix:()=>null}]})}return{CatchClause:checkCatchClause}}});
|
|
@@ -1 +1 @@
|
|
|
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
|
|
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");const LOOP_NODE_TYPES=new Set(["ForStatement","ForInStatement","ForOfStatement","WhileStatement","DoWhileStatement"]);function onceEvaluatedSlot(loop){if(loop.type==="ForOfStatement"||loop.type==="ForInStatement"){return"right"}if(loop.type==="ForStatement"){return"init"}return null}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,isLoopRoot=false){if(currentNode.type==="AwaitExpression"){awaitExpressions.push(currentNode)}if(currentNode.type==="FunctionDeclaration"||currentNode.type==="FunctionExpression"||currentNode.type==="ArrowFunctionExpression"){return}if(!isLoopRoot&&LOOP_NODE_TYPES.has(currentNode.type)){const slot=onceEvaluatedSlot(currentNode);const head=slot?currentNode[slot]:void 0;if(head&&typeof head==="object"&&"type"in head){findAwaits(head)}return}const skipSlot=isLoopRoot?onceEvaluatedSlot(currentNode):null;for(const key in currentNode){if(skipProperties.has(key))continue;if(skipSlot!==null&&key===skipSlot)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,true);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"&¤tNode.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")}}}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMissingNullChecks=void 0;exports.hasNullCheck=hasNullCheck;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function assignedValue(init){let current=init;while(current?.type==="AssignmentExpression"&¤t.operator==="="){current=current.right}return current}const NULLABLE_RETURNS=new Set(["find","findLast","pop","shift","match","exec","getElementById","querySelector","closest","getAttribute","getNamedItem"]);function isShadowedBinding(scope,name){for(let s=scope;s;s=s.upper){const variable=s.variables.find(v=>v.name===name);if(variable)return variable.defs.length>0}return false}function nullabilityEvidence(ident,scope,seen=new Set){const readAt=ident.range?.[0]??Number.POSITIVE_INFINITY;if(seen.has(ident.name))return null;seen.add(ident.name);let s=scope;while(s){const variable=s.variables.find(v=>v.name===ident.name);if(!variable){s=s.upper;continue}if(variable.defs.length!==1)return null;const[def]=variable.defs;if(def.type!=="Variable"||def.node?.type!=="VariableDeclarator")return null;const declarator=def.node;const declaration=def.parent;if(declaration?.type==="VariableDeclaration"&&declaration.declare){return null}const init=assignedValue(declarator.init);if(init===null){const declParent=declarator.parent;const loopParent=declParent?.parent;if(loopParent?.type==="ForOfStatement"||loopParent?.type==="ForInStatement"){return null}const written=variable.references.some(reference=>reference.isWrite()&&!reference.init&&reference.identifier.range[0]<readAt);if(written)return null;return"declared-without-initializer"}const reassigned=variable.references.some(reference=>reference.isWrite()&&!reference.init&&reference.identifier.range[0]<readAt);if(reassigned)return null;if(init.type==="Literal"&&init.value===null)return"assigned-null";if(init.type==="Identifier"&&init.name==="undefined"&&!isShadowedBinding(s,"undefined")){return"assigned-null"}if(init.type==="Identifier"){return nullabilityEvidence(init,s,seen)}if(init.type==="ConditionalExpression"){for(const arm of[init.consequent,init.alternate]){if(arm.type==="Literal"&&arm.value===null)return"assigned-null";if(arm.type==="Identifier"&&arm.name==="undefined"&&!isShadowedBinding(s,"undefined")){return"assigned-null"}if(arm.type==="Identifier"){const viaArm=nullabilityEvidence(arm,s,seen);if(viaArm)return viaArm}if(arm.type==="CallExpression"&&arm.callee.type==="MemberExpression"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(arm.callee),NULLABLE_RETURNS)){return"nullable-return"}}return null}if(init.type==="CallExpression"&&init.callee.type==="MemberExpression"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(init.callee),NULLABLE_RETURNS)){return"nullable-return"}return null}return null}function baseHasNullabilityEvidence(objectNode,scope){let base=objectNode;while(base.type==="MemberExpression"){base=base.object}if(base.type!=="Identifier")return false;return nullabilityEvidence(base,scope)!==null}function hasNullCheck(node,sourceCode){if(node.optional){return true}const parent=node.parent;if(parent&&parent.type==="ChainExpression"){return true}if(usesNullishCoalescing(node)){return true}const objectText=sourceCode.getText(node.object);{let cur=node;for(let depth=0;depth<16;depth++){const up=cur.parent;if(!up)break;if(up.type==="FunctionDeclaration"||up.type==="FunctionExpression"||up.type==="ArrowFunctionExpression"){break}if(up.type==="LogicalExpression"&&up.operator==="&&"&&up.right===cur){const leftText=sourceCode.getText(up.left);if(chainStartsWith(leftText,objectText))return true}cur=up}}{let cur=node;for(let depth=0;depth<24;depth++){const p=cur.parent;if(!p)break;if(p.type==="FunctionDeclaration"||p.type==="FunctionExpression"||p.type==="ArrowFunctionExpression"){break}if(p.type==="ConditionalExpression"){const ternary=p;const test=ternary.test;if(ternary.consequent===cur&&(chainStartsWith(sourceCode.getText(test),objectText)||isNullCheckForObject(test,node.object,sourceCode))){return true}if(ternary.alternate===cur&&falsyGuardTargets(test).some(target=>guardsSameValue(target,node.object,sourceCode))){return true}}cur=p}}if(hasExplicitNullCheck(node,sourceCode)){return true}if(isGuardedByEarlyExit(node,sourceCode)){return true}return false}function endsInExit(stmt){switch(stmt.type){case"ReturnStatement":case"ThrowStatement":case"ContinueStatement":case"BreakStatement":return true;case"BlockStatement":{const last=stmt.body[stmt.body.length-1];return last!==void 0&&endsInExit(last)}case"TryStatement":{if(stmt.finalizer!==null&&endsInExit(stmt.finalizer))return true;if(!endsInExit(stmt.block))return false;return stmt.handler===null||endsInExit(stmt.handler.body)}default:return false}}function falsyGuardTargets(test){if(test.type==="UnaryExpression"&&test.operator==="!"){return[test.argument]}if(test.type==="BinaryExpression"&&(test.operator==="=="||test.operator==="===")){const isNil=e=>e.type==="Literal"&&e.value===null||e.type==="Identifier"&&e.name==="undefined";if(isNil(test.right))return[test.left];if(isNil(test.left))return[test.right];return[]}if(test.type==="LogicalExpression"&&test.operator==="||"){return[...falsyGuardTargets(test.left),...falsyGuardTargets(test.right)]}return[]}function rootBinding(expr,sourceCode){let base=expr;while(base.type==="MemberExpression")base=base.object;const name=base.name;for(let s=sourceCode.getScope(base);s;s=s.upper){const variable=s.variables.find(v=>v.name===name);if(variable)return variable}return null}function guardsSameValue(guarded,object,sourceCode){const binding=rootBinding(guarded,sourceCode);return binding!==null&&binding===rootBinding(object,sourceCode)&&chainStartsWith(sourceCode.getText(guarded),sourceCode.getText(object))}function statementListOf(node){switch(node.type){case"Program":case"BlockStatement":return node.body;case"SwitchCase":return node.consequent;default:return null}}function isGuardedByEarlyExit(node,sourceCode){let child=node;let parent=child.parent;while(parent){const list=statementListOf(parent);if(list){const index=list.indexOf(child);for(let i=0;i<index;i++){const stmt=list[i];if(stmt.type==="IfStatement"&&endsInExit(stmt.consequent)&&falsyGuardTargets(stmt.test).some(guarded=>guardsSameValue(guarded,node.object,sourceCode))){return true}}}child=parent;parent=child.parent}return false}function hasExplicitNullCheck(node,sourceCode){let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(parent&&parent.type==="IfStatement"){const test=parent.test;if(isNullCheckForObject(test,node.object,sourceCode)){return true}}current=parent;depth++}return false}function rootText(expr,sourceCode){let base=expr;for(;;){if(base.type==="MemberExpression")base=base.object;else if(base.type==="CallExpression")base=base.callee;else break}return sourceCode.getText(base)}function chainStartsWith(guard,objectText){if(guard===objectText)return true;if(!objectText.startsWith(guard))return false;const rest=objectText.slice(guard.length);return rest.startsWith(".")||rest.startsWith("[")}function rootGuards(root,object,sourceCode){return chainStartsWith(root,sourceCode.getText(object))}function isOptionalChainRootedAt(expr,object,sourceCode){return expr.type==="ChainExpression"&&rootGuards(rootText(expr.expression,sourceCode),object,sourceCode)}function narrowsToObject(test,object,sourceCode){if(test.type==="BinaryExpression"&&test.operator==="in"){return rootGuards(sourceCode.getText(test.right),object,sourceCode)}if(isOptionalChainRootedAt(test,object,sourceCode)){return true}if(test.type==="BinaryExpression"&&(test.operator==="!=="||test.operator==="!=")){const isNil=e=>e.type==="Literal"&&e.value===null||e.type==="Identifier"&&e.name==="undefined";if(isNil(test.right)){return isOptionalChainRootedAt(test.left,object,sourceCode)}if(isNil(test.left)){return isOptionalChainRootedAt(test.right,object,sourceCode)}}return false}function isNullCheckForObject(test,object,sourceCode){const objectText=sourceCode.getText(object);if(narrowsToObject(test,object,sourceCode)){return true}if(test.type==="Identifier"||test.type==="MemberExpression"){const testText=sourceCode.getText(test);if(testText===objectText||objectText.startsWith(testText+".")){return true}}if(test.type==="BinaryExpression"){const{left,right,operator}=test;if(operator==="!=="||operator==="!="||operator==="==="||operator==="=="){const leftText=sourceCode.getText(left);const rightText=sourceCode.getText(right);if(leftText===objectText&&(rightText==="null"||rightText==="undefined")||rightText===objectText&&(leftText==="null"||leftText==="undefined")){return true}}}if(test.type==="LogicalExpression"){return isNullCheckForObject(test.left,object,sourceCode)||isNullCheckForObject(test.right,object,sourceCode)}return false}function usesNullishCoalescing(node){let current=node;let depth=0;const maxDepth=5;while(current&&depth<maxDepth){const parent=current.parent;if(parent&&parent.type==="LogicalExpression"&&parent.operator==="??"){return true}current=parent;depth++}return false}exports.noMissingNullChecks=(0,eslint_devkit_2.createRule)({name:"no-missing-null-checks",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-missing-null-checks.md",description:"Detects potential null pointer dereferences",cwe:"CWE-476",cvss:7.5},hasSuggestions:true,messages:{missingNullCheck:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Missing null check",cwe:"CWE-476",description:"Potential null/undefined dereference detected",severity:"HIGH",fix:"Use optional chaining (?.) or add explicit null check",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-2259/"}),useOptionalChaining:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Optional Chaining",description:"Use optional chaining operator",severity:"LOW",fix:"obj?.property?.method()",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining"}),useNullishCoalescing:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Nullish Coalescing",description:"Use nullish coalescing operator",severity:"LOW",fix:"value ?? defaultValue",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing"}),addExplicitCheck:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Explicit Check",description:"Add explicit null check",severity:"LOW",fix:"if (obj !== null) { obj.property }",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"},requireExplicitChecks:{type:"boolean",default:false,description:"Require explicit null checks"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,requireExplicitChecks:false}],create(context,[options={}]){const{ignoreInTests=true}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);if(isTestFile){return{}}const sourceCode=context.sourceCode;const services=(0,eslint_devkit_1.hasParserServices)(context)?(0,eslint_devkit_1.getParserServices)(context):null;function typeSaysNonNullable(objectNode){if(!services)return false;const type=(0,eslint_devkit_1.getTypeOfNode)(objectNode,services);if((0,eslint_devkit_1.isAnyType)(type)||(0,eslint_devkit_1.isUnknownType)(type))return false;return!(0,eslint_devkit_1.isNullableType)(type)}const reportedMemberExpressions=new Set;function getMemberExpressionKey(node){if(node.range&&Array.isArray(node.range)&&node.range.length>=2){return`me-${node.range[0]}-${node.range[1]}`}const loc=node.loc;if(loc&&loc.start){return`me-${loc.start.line}-${loc.start.column}-${loc.end?.line||loc.start.line}-${loc.end?.column||loc.start.column}`}return`me-${JSON.stringify(node).slice(0,50)}`}function checkMemberExpression(node){if(node.optional){return}const parent=node.parent;if(parent&&parent.type==="ChainExpression"){return}if(parent&&parent.type==="MemberExpression"&&parent.object===node){return}const objectNode=node.object;let shouldCheck=false;shouldCheck=baseHasNullabilityEvidence(objectNode,sourceCode.getScope(node))&&!typeSaysNonNullable(objectNode);if(shouldCheck&&!hasNullCheck(node,sourceCode)){const nodeKey=getMemberExpressionKey(node);if(reportedMemberExpressions.has(nodeKey)){return}try{reportedMemberExpressions.add(nodeKey);context.report({node,messageId:"missingNullCheck",suggest:[{messageId:"useOptionalChaining",fix:()=>null},{messageId:"useNullishCoalescing",fix:()=>null},{messageId:"addExplicitCheck",fix:()=>null}]})}catch{return}}}function checkCallExpression(node){if(node.type!=="CallExpression"){return}if(node.callee.type==="MemberExpression"){const memberExpr=node.callee;if(memberExpr.optional){return}const parent=memberExpr.parent;if(parent&&parent.type==="ChainExpression"){return}const objectNode=memberExpr.object;let shouldCheck=false;shouldCheck=baseHasNullabilityEvidence(objectNode,sourceCode.getScope(memberExpr))&&!typeSaysNonNullable(objectNode);if(shouldCheck&&!hasNullCheck(memberExpr,sourceCode)){const nodeKey=getMemberExpressionKey(memberExpr);if(reportedMemberExpressions.has(nodeKey)){return}try{reportedMemberExpressions.add(nodeKey);context.report({node:memberExpr,messageId:"missingNullCheck",suggest:[{messageId:"useOptionalChaining",fix:()=>null},{messageId:"useNullishCoalescing",fix:()=>null},{messageId:"addExplicitCheck",fix:()=>null}]})}catch{return}}}}return{MemberExpression:checkMemberExpression,CallExpression:checkCallExpression}}});
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noMissingNullChecks=void 0;exports.hasNullCheck=hasNullCheck;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function assignedValue(init){let current=init;while(current?.type==="AssignmentExpression"&¤t.operator==="="){current=current.right}return current}const NULLABLE_RETURNS=new Set(["find","findLast","pop","shift","match","exec","getElementById","querySelector","closest","getAttribute","getNamedItem"]);function isShadowedBinding(scope,name){for(let s=scope;s;s=s.upper){const variable=s.variables.find(v=>v.name===name);if(variable)return variable.defs.length>0}return false}function nullabilityEvidence(ident,scope,seen=new Set){const readAt=ident.range?.[0]??Number.POSITIVE_INFINITY;if(seen.has(ident.name))return null;seen.add(ident.name);let s=scope;while(s){const variable=s.variables.find(v=>v.name===ident.name);if(!variable){s=s.upper;continue}if(variable.defs.length!==1)return null;const[def]=variable.defs;if(def.type!=="Variable"||def.node?.type!=="VariableDeclarator")return null;const declarator=def.node;const declaration=def.parent;if(declaration?.type==="VariableDeclaration"&&declaration.declare){return null}const init=assignedValue(declarator.init);if(init===null){const declParent=declarator.parent;const loopParent=declParent?.parent;if(loopParent?.type==="ForOfStatement"||loopParent?.type==="ForInStatement"){return null}const written=variable.references.some(reference=>reference.isWrite()&&!reference.init&&reference.identifier.range[0]<readAt);if(written)return null;return"declared-without-initializer"}const reassigned=variable.references.some(reference=>reference.isWrite()&&!reference.init&&reference.identifier.range[0]<readAt);if(reassigned)return null;if(init.type==="Literal"&&init.value===null)return"assigned-null";if(init.type==="Identifier"&&init.name==="undefined"&&!isShadowedBinding(s,"undefined")){return"assigned-null"}if(init.type==="Identifier"){return nullabilityEvidence(init,s,seen)}if(init.type==="ConditionalExpression"){for(const arm of[init.consequent,init.alternate]){if(arm.type==="Literal"&&arm.value===null)return"assigned-null";if(arm.type==="Identifier"&&arm.name==="undefined"&&!isShadowedBinding(s,"undefined")){return"assigned-null"}if(arm.type==="Identifier"){const viaArm=nullabilityEvidence(arm,s,seen);if(viaArm)return viaArm}if(arm.type==="CallExpression"&&arm.callee.type==="MemberExpression"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(arm.callee),NULLABLE_RETURNS)){return"nullable-return"}}return null}if(init.type==="CallExpression"&&init.callee.type==="MemberExpression"&&(0,eslint_devkit_1.namesOneOf)((0,eslint_devkit_1.propertyName)(init.callee),NULLABLE_RETURNS)){return"nullable-return"}return null}return null}function baseHasNullabilityEvidence(objectNode,scope){let base=objectNode;while(base.type==="MemberExpression"){base=base.object}if(base.type!=="Identifier")return false;return nullabilityEvidence(base,scope)!==null}function hasNullCheck(node,sourceCode){if(node.optional){return true}const parent=node.parent;if(parent&&parent.type==="ChainExpression"){return true}if(usesNullishCoalescing(node)){return true}const objectText=sourceCode.getText(node.object);{let cur=node;for(let depth=0;depth<16;depth++){const up=cur.parent;if(!up)break;if(up.type==="FunctionDeclaration"||up.type==="FunctionExpression"||up.type==="ArrowFunctionExpression"){break}if(up.type==="LogicalExpression"&&up.operator==="&&"&&up.right===cur){const leftText=sourceCode.getText(up.left);if(chainStartsWith(leftText,objectText))return true}cur=up}}{let cur=node;for(let depth=0;depth<24;depth++){const p=cur.parent;if(!p)break;if(p.type==="FunctionDeclaration"||p.type==="FunctionExpression"||p.type==="ArrowFunctionExpression"){break}if(p.type==="ConditionalExpression"){const ternary=p;const test=ternary.test;if(ternary.consequent===cur&&(chainStartsWith(sourceCode.getText(test),objectText)||isNullCheckForObject(test,node.object,sourceCode))){return true}if(ternary.alternate===cur&&falsyGuardTargets(test).some(target=>guardsSameValue(target,node.object,sourceCode))){return true}}cur=p}}if(hasExplicitNullCheck(node,sourceCode)){return true}if(isGuardedByEarlyExit(node,sourceCode)){return true}return false}function endsInExit(stmt){switch(stmt.type){case"ReturnStatement":case"ThrowStatement":case"ContinueStatement":case"BreakStatement":return true;case"BlockStatement":{const last=stmt.body[stmt.body.length-1];return last!==void 0&&endsInExit(last)}case"TryStatement":{if(stmt.finalizer!==null&&endsInExit(stmt.finalizer))return true;if(!endsInExit(stmt.block))return false;return stmt.handler===null||endsInExit(stmt.handler.body)}default:return false}}function falsyGuardTargets(test){if(test.type==="UnaryExpression"&&test.operator==="!"){return[test.argument]}if(test.type==="BinaryExpression"&&(test.operator==="=="||test.operator==="===")){const isNil=e=>e.type==="Literal"&&e.value===null||e.type==="Identifier"&&e.name==="undefined";if(isNil(test.right))return[test.left];if(isNil(test.left))return[test.right];return[]}if(test.type==="LogicalExpression"&&test.operator==="||"){return[...falsyGuardTargets(test.left),...falsyGuardTargets(test.right)]}return[]}function rootBinding(expr,sourceCode){let base=expr;while(base.type==="MemberExpression")base=base.object;const name=base.name;for(let s=sourceCode.getScope(base);s;s=s.upper){const variable=s.variables.find(v=>v.name===name);if(variable)return variable}return null}function guardsSameValue(guarded,object,sourceCode){const binding=rootBinding(guarded,sourceCode);return binding!==null&&binding===rootBinding(object,sourceCode)&&chainStartsWith(sourceCode.getText(guarded),sourceCode.getText(object))}function statementListOf(node){switch(node.type){case"Program":case"BlockStatement":return node.body;case"SwitchCase":return node.consequent;default:return null}}function isGuardedByEarlyExit(node,sourceCode){let child=node;let parent=child.parent;while(parent){const list=statementListOf(parent);if(list){const index=list.indexOf(child);for(let i=0;i<index;i++){const stmt=list[i];if(stmt.type==="IfStatement"&&endsInExit(stmt.consequent)&&falsyGuardTargets(stmt.test).some(guarded=>guardsSameValue(guarded,node.object,sourceCode))){return true}}}child=parent;parent=child.parent}return false}function hasExplicitNullCheck(node,sourceCode){let current=node;let depth=0;const maxDepth=10;while(current&&depth<maxDepth){const parent=current.parent;if(parent&&parent.type==="IfStatement"){const test=parent.test;if(isNullCheckForObject(test,node.object,sourceCode)){return true}}current=parent;depth++}return false}function rootText(expr,sourceCode){let base=expr;for(;;){if(base.type==="MemberExpression")base=base.object;else if(base.type==="CallExpression")base=base.callee;else break}return sourceCode.getText(base)}function chainStartsWith(guard,objectText){if(guard===objectText)return true;if(!objectText.startsWith(guard))return false;const rest=objectText.slice(guard.length);return rest.startsWith(".")||rest.startsWith("[")}function rootGuards(root,object,sourceCode){return chainStartsWith(root,sourceCode.getText(object))}function isOptionalChainRootedAt(expr,object,sourceCode){return expr.type==="ChainExpression"&&rootGuards(rootText(expr.expression,sourceCode),object,sourceCode)}function narrowsToObject(test,object,sourceCode){if(test.type==="BinaryExpression"&&test.operator==="in"){return rootGuards(sourceCode.getText(test.right),object,sourceCode)}if(isOptionalChainRootedAt(test,object,sourceCode)){return true}if(test.type==="BinaryExpression"&&(test.operator==="!=="||test.operator==="!=")){const isNil=e=>e.type==="Literal"&&e.value===null||e.type==="Identifier"&&e.name==="undefined";if(isNil(test.right)){return isOptionalChainRootedAt(test.left,object,sourceCode)}if(isNil(test.left)){return isOptionalChainRootedAt(test.right,object,sourceCode)}}return false}function isNullCheckForObject(test,object,sourceCode){const objectText=sourceCode.getText(object);if(narrowsToObject(test,object,sourceCode)){return true}if(test.type==="Identifier"||test.type==="MemberExpression"){const testText=sourceCode.getText(test);if(testText===objectText||objectText.startsWith(testText+".")){return true}}if(test.type==="BinaryExpression"){const{left,right,operator}=test;if(operator==="!=="||operator==="!="||operator==="==="||operator==="=="){const leftText=sourceCode.getText(left);const rightText=sourceCode.getText(right);if(leftText===objectText&&(rightText==="null"||rightText==="undefined")||rightText===objectText&&(leftText==="null"||leftText==="undefined")){return true}}}if(test.type==="LogicalExpression"){return isNullCheckForObject(test.left,object,sourceCode)||isNullCheckForObject(test.right,object,sourceCode)}return false}function usesNullishCoalescing(node){let current=node;let depth=0;const maxDepth=5;while(current&&depth<maxDepth){const parent=current.parent;if(parent&&parent.type==="LogicalExpression"&&parent.operator==="??"){return true}current=parent;depth++}return false}exports.noMissingNullChecks=(0,eslint_devkit_2.createRule)({name:"no-missing-null-checks",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-missing-null-checks.md",description:"Detects potential null pointer dereferences",cwe:"CWE-476",cvss:7.5},hasSuggestions:true,messages:{missingNullCheck:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Missing null check",cwe:"CWE-476",description:"Potential null/undefined dereference detected",severity:"HIGH",fix:"Use optional chaining (?.) or add explicit null check",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-2259/"}),useOptionalChaining:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Optional Chaining",description:"Use optional chaining operator",severity:"LOW",fix:"obj?.property?.method()",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining"}),useNullishCoalescing:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Nullish Coalescing",description:"Use nullish coalescing operator",severity:"LOW",fix:"value ?? defaultValue",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing"}),addExplicitCheck:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Add Explicit Check",description:"Add explicit null check",severity:"LOW",fix:"if (obj !== null) { obj.property }",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"},requireExplicitChecks:{type:"boolean",default:false,description:"Require explicit null checks"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,requireExplicitChecks:false}],create(context,[options={}]){const{ignoreInTests=true}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.[cm]?[jt]sx?$/.test(filename);if(isTestFile){return{}}const sourceCode=context.sourceCode;const services=(0,eslint_devkit_1.hasParserServices)(context)?(0,eslint_devkit_1.getParserServices)(context):null;function typeSaysNonNullable(objectNode){if(!services)return false;const type=(0,eslint_devkit_1.getTypeOfNode)(objectNode,services);if((0,eslint_devkit_1.isAnyType)(type)||(0,eslint_devkit_1.isUnknownType)(type))return false;return!(0,eslint_devkit_1.isNullableType)(type)}const reportedMemberExpressions=new Set;function getMemberExpressionKey(node){if(node.range&&Array.isArray(node.range)&&node.range.length>=2){return`me-${node.range[0]}-${node.range[1]}`}const loc=node.loc;if(loc&&loc.start){return`me-${loc.start.line}-${loc.start.column}-${loc.end?.line||loc.start.line}-${loc.end?.column||loc.start.column}`}return`me-${JSON.stringify(node).slice(0,50)}`}function checkMemberExpression(node){if(node.optional){return}const parent=node.parent;if(parent&&parent.type==="ChainExpression"){return}if(parent&&parent.type==="MemberExpression"&&parent.object===node){return}const objectNode=node.object;let shouldCheck=false;shouldCheck=baseHasNullabilityEvidence(objectNode,sourceCode.getScope(node))&&!typeSaysNonNullable(objectNode);if(shouldCheck&&!hasNullCheck(node,sourceCode)){const nodeKey=getMemberExpressionKey(node);if(reportedMemberExpressions.has(nodeKey)){return}try{reportedMemberExpressions.add(nodeKey);context.report({node,messageId:"missingNullCheck",suggest:[{messageId:"useOptionalChaining",fix:()=>null},{messageId:"useNullishCoalescing",fix:()=>null},{messageId:"addExplicitCheck",fix:()=>null}]})}catch{return}}}function checkCallExpression(node){if(node.type!=="CallExpression"){return}if(node.callee.type==="MemberExpression"){const memberExpr=node.callee;if(memberExpr.optional){return}const parent=memberExpr.parent;if(parent&&parent.type==="ChainExpression"){return}const objectNode=memberExpr.object;let shouldCheck=false;shouldCheck=baseHasNullabilityEvidence(objectNode,sourceCode.getScope(memberExpr))&&!typeSaysNonNullable(objectNode);if(shouldCheck&&!hasNullCheck(memberExpr,sourceCode)){const nodeKey=getMemberExpressionKey(memberExpr);if(reportedMemberExpressions.has(nodeKey)){return}try{reportedMemberExpressions.add(nodeKey);context.report({node:memberExpr,messageId:"missingNullCheck",suggest:[{messageId:"useOptionalChaining",fix:()=>null},{messageId:"useNullishCoalescing",fix:()=>null},{messageId:"addExplicitCheck",fix:()=>null}]})}catch{return}}}}return{MemberExpression:checkMemberExpression,CallExpression:checkCallExpression}}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUnsafeTypeNarrowing=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function isUnsafeTypeAssertion(node){if(node.expression.type==="TSAsExpression"){const innerAssertion=node.expression;if(innerAssertion.typeAnnotation.type==="TSUnknownKeyword"){return true}if(innerAssertion.typeAnnotation.type==="TSAnyKeyword"){return true}}return false}function hasExplanatoryComment(node,sourceCode){const comments=sourceCode.getAllComments();const nodeStart=node.loc?.start;if(!nodeStart||!comments.length){return false}const explanatoryPatterns=[/type.?guard/i,/validated/i,/checked/i,/\bsafe\b/i,/\bknown\b/i,/intentional/i,/necessary/i,/framework/i,/library/i,/third.?party/i,/legacy/i,/todo/i,/fixme/i];for(const comment of comments){const distance=nodeStart.line-comment.loc.end.line;if(distance<0||distance>1)continue;if(distance===1){const before=sourceCode.getTokenBefore(comment,{includeComments:true});if(before&&before.loc.end.line===comment.loc.start.line)continue}const commentText=comment.value.toLowerCase();if(explanatoryPatterns.some(pattern=>pattern.test(commentText))){return true}}return false}exports.noUnsafeTypeNarrowing=(0,eslint_devkit_2.createRule)({name:"no-unsafe-type-narrowing",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-unsafe-type-narrowing.md",description:"Detects unsafe type narrowing patterns"},hasSuggestions:true,messages:{unsafeTypeNarrowing:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Unsafe type narrowing",description:"Unsafe type assertion or narrowing detected",severity:"MEDIUM",fix:"Use type guards or proper validation before type assertion",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-4326/"}),useTypeGuard:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Type Guard",description:"Use type guard function",severity:"LOW",fix:"function isType(value: unknown): value is Type { return ... }",documentationLink:"https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates"}),useProperNarrowing:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Type Narrowing",description:"Use proper type narrowing",severity:"LOW",fix:'if (typeof value === "string") { ... }',documentationLink:"https://www.typescriptlang.org/docs/handbook/2/narrowing.html"}),validateBeforeAssert:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Validate First",description:"Validate before type assertion",severity:"LOW",fix:"if (isValid(value)) { const typed = value as Type; }",documentationLink:"https://www.typescriptlang.org/docs/handbook/2/narrowing.html"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"},allowWithComment:{type:"boolean",default:false,description:"Allow type assertions with comments"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,allowWithComment:false}],create(context,[options={}]){const{ignoreInTests=true,allowWithComment=false}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.noUnsafeTypeNarrowing=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");function isUnsafeTypeAssertion(node){if(node.expression.type==="TSAsExpression"){const innerAssertion=node.expression;if(innerAssertion.typeAnnotation.type==="TSUnknownKeyword"){return true}if(innerAssertion.typeAnnotation.type==="TSAnyKeyword"){return true}}return false}function hasExplanatoryComment(node,sourceCode){const comments=sourceCode.getAllComments();const nodeStart=node.loc?.start;if(!nodeStart||!comments.length){return false}const explanatoryPatterns=[/type.?guard/i,/validated/i,/checked/i,/\bsafe\b/i,/\bknown\b/i,/intentional/i,/necessary/i,/framework/i,/library/i,/third.?party/i,/legacy/i,/todo/i,/fixme/i];for(const comment of comments){const distance=nodeStart.line-comment.loc.end.line;if(distance<0||distance>1)continue;if(distance===1){const before=sourceCode.getTokenBefore(comment,{includeComments:true});if(before&&before.loc.end.line===comment.loc.start.line)continue}const commentText=comment.value.toLowerCase();if(explanatoryPatterns.some(pattern=>pattern.test(commentText))){return true}}return false}exports.noUnsafeTypeNarrowing=(0,eslint_devkit_2.createRule)({name:"no-unsafe-type-narrowing",meta:{type:"problem",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-reliability/docs/rules/no-unsafe-type-narrowing.md",description:"Detects unsafe type narrowing patterns"},hasSuggestions:true,messages:{unsafeTypeNarrowing:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.WARNING,issueName:"Unsafe type narrowing",description:"Unsafe type assertion or narrowing detected",severity:"MEDIUM",fix:"Use type guards or proper validation before type assertion",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-4326/"}),useTypeGuard:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Type Guard",description:"Use type guard function",severity:"LOW",fix:"function isType(value: unknown): value is Type { return ... }",documentationLink:"https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates"}),useProperNarrowing:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Type Narrowing",description:"Use proper type narrowing",severity:"LOW",fix:'if (typeof value === "string") { ... }',documentationLink:"https://www.typescriptlang.org/docs/handbook/2/narrowing.html"}),validateBeforeAssert:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Validate First",description:"Validate before type assertion",severity:"LOW",fix:"if (isValid(value)) { const typed = value as Type; }",documentationLink:"https://www.typescriptlang.org/docs/handbook/2/narrowing.html"})},schema:[{type:"object",properties:{ignoreInTests:{type:"boolean",default:true,description:"Ignore in test files"},allowWithComment:{type:"boolean",default:false,description:"Allow type assertions with comments"}},additionalProperties:false}]},defaultOptions:[{ignoreInTests:true,allowWithComment:false}],create(context,[options={}]){const{ignoreInTests=true,allowWithComment=false}=options||{};const filename=context.filename;const isTestFile=ignoreInTests&&/\.(test|spec)\.[cm]?[jt]sx?$/.test(filename);if(isTestFile){return{}}const sourceCode=context.sourceCode;function checkTypeAssertion(node){if(!isUnsafeTypeAssertion(node)){return}if(allowWithComment&&hasExplanatoryComment(node,sourceCode)){return}context.report({node,messageId:"unsafeTypeNarrowing",suggest:[{messageId:"useTypeGuard",fix:()=>null},{messageId:"useProperNarrowing",fix:()=>null},{messageId:"validateBeforeAssert",fix:()=>null}]})}return{TSAsExpression:checkTypeAssertion}}});
|