eslint-plugin-maintainability 3.0.16 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,6 +16,7 @@
16
16
  <a href="https://opensource.org/licenses/MIT" target="_blank"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="Package License" /></a>
17
17
  <a href="https://app.codecov.io/gh/ofri-peretz/eslint/components?components%5B0%5D=eslint-plugin-maintainability" target="_blank"><img src="https://codecov.io/gh/ofri-peretz/eslint/graph/badge.svg?component=eslint-plugin-maintainability" alt="Codecov" /></a>
18
18
  <a href="https://github.com/ofri-peretz/eslint" target="_blank"><img src="https://img.shields.io/badge/Since-Dec_2025-blue?logo=rocket&logoColor=white" alt="Since Dec 2025" /></a>
19
+ <a href="https://scorecard.dev/viewer/?uri=github.com/ofri-peretz/eslint" target="_blank"><img src="https://api.securityscorecards.dev/projects/github.com/ofri-peretz/eslint/badge" alt="OpenSSF Scorecard" /></a>
19
20
  </p>
20
21
 
21
22
  > ⭐ If this plugin caught a real bug for you, [**star the repo**](https://github.com/ofri-peretz/eslint) — it's the signal that keeps these rules maintained.
@@ -28,6 +29,45 @@ This plugin provides Maintainability rules — complexity ceilings, dead code, a
28
29
 
29
30
  **Interlace** fosters **strength through integration**. Instead of stacking isolated rules, we **interlace** security directly into your workflow to create a resilient fabric of code. We believe tools should **guide rather than gatekeep**, providing educational feedback that strengthens the developer with every interaction.
30
31
 
32
+ <!-- AUTO-GENERATED:DOCTRINE:START - Do not edit manually -->
33
+
34
+ ## Why these rules are quiet
35
+
36
+ **Noise creates apathy, and apathy is not a security posture.** A linter that reports
37
+ a thousand things a week gets switched off in a month, and the real finding goes with
38
+ it. So every rule here is built to be worth reading: we would rather miss a finding
39
+ than spend your attention on one that was never real.
40
+
41
+ That is a trade, and it is made deliberately. It costs recall, and we measure what it
42
+ costs rather than assuming it is free.
43
+
44
+ ## How the rules decide
45
+
46
+ **Evidence, not names.** A rule fires on what the code *does*, resolved through the
47
+ AST and ESLint's own scope analysis — not on an identifier that happens to contain
48
+ `query`, a method called `setItem`, or a file whose path contains `key`. Every one of
49
+ those was a real false positive in this ecosystem, found by reading our own output on
50
+ open-source projects and fixed with a test that fails on the unfixed rule.
51
+
52
+ Where a rule has known false-positive shapes, its page carries a **Not a finding**
53
+ section: what it deliberately stays quiet on, and what to check first when it fires
54
+ and you disagree.
55
+
56
+ ## What you get
57
+
58
+ The rules below. Security rules carry a CWE mapping and, where one is assigned, a
59
+ CVSS score; every rule carries a fix on its message — in prose for a human and as
60
+ structured JSON for an agent. Install it, enable
61
+ `recommended`, and read the findings. If one of them is wrong,
62
+ [open an issue](https://github.com/ofri-peretz/eslint/issues) — a false positive is a
63
+ bug here, not a tuning exercise for you.
64
+
65
+ How that is measured, on which projects, and where it falls short:
66
+ [benchmark methodology](https://github.com/ofri-peretz/eslint/blob/main/BENCHMARK-METHODOLOGY.md)
67
+ and [results](https://github.com/ofri-peretz/eslint/blob/main/BENCHMARK-RESULTS.md).
68
+
69
+ <!-- AUTO-GENERATED:DOCTRINE:END -->
70
+
31
71
  ## Getting Started
32
72
 
33
73
  - To check out the [guide](https://eslint.interlace.tools/docs/quality/plugin-maintainability?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-maintainability), visit [eslint.interlace.tools](https://eslint.interlace.tools/?utm_source=github&utm_medium=referral&utm_campaign=eslint-plugin-maintainability). 📚
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-maintainability",
3
- "version": "3.0.16",
3
+ "version": "3.1.0",
4
4
  "description": "ESLint plugin for maintainable code — limits cognitive complexity, nesting depth, parameter counts, duplicate functions, and unhandled or silent errors.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -63,7 +63,7 @@
63
63
  }
64
64
  },
65
65
  "dependencies": {
66
- "@interlace/eslint-devkit": "^1.11.0"
66
+ "@interlace/eslint-devkit": "^1.17.0"
67
67
  },
68
68
  "repository": {
69
69
  "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;exports.rules={get"cognitive-complexity"(){return require("./rules/maintainability/cognitive-complexity").cognitiveComplexity},get"nested-complexity-hotspots"(){return require("./rules/maintainability/nested-complexity-hotspots").nestedComplexityHotspots},get"identical-functions"(){return require("./rules/maintainability/identical-functions").identicalFunctions},get"max-parameters"(){return require("./rules/maintainability/max-parameters").maxParameters},get"no-lonely-if"(){return require("./rules/maintainability/no-lonely-if").noLonelyIf},get"no-nested-ternary"(){return require("./rules/maintainability/no-nested-ternary").noNestedTernary},get"consistent-function-scoping"(){return require("./rules/maintainability/consistent-function-scoping").consistentFunctionScoping},get"no-unreadable-iife"(){return require("./rules/maintainability/no-unreadable-iife").noUnreadableIife},get"error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"maintainability/cognitive-complexity"(){return require("./rules/maintainability/cognitive-complexity").cognitiveComplexity},get"maintainability/nested-complexity-hotspots"(){return require("./rules/maintainability/nested-complexity-hotspots").nestedComplexityHotspots},get"maintainability/identical-functions"(){return require("./rules/maintainability/identical-functions").identicalFunctions},get"maintainability/max-parameters"(){return require("./rules/maintainability/max-parameters").maxParameters},get"maintainability/no-lonely-if"(){return require("./rules/maintainability/no-lonely-if").noLonelyIf},get"maintainability/no-nested-ternary"(){return require("./rules/maintainability/no-nested-ternary").noNestedTernary},get"maintainability/consistent-function-scoping"(){return require("./rules/maintainability/consistent-function-scoping").consistentFunctionScoping},get"maintainability/no-unreadable-iife"(){return require("./rules/maintainability/no-unreadable-iife").noUnreadableIife},get"maintainability/error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"maintainability/no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"maintainability/no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"maintainability/no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise}};exports.plugin={meta:{name:"eslint-plugin-maintainability",version:"3.0.16"},rules:exports.rules};exports.configs={recommended:{plugins:{"maintainability":exports.plugin},rules:{"maintainability/cognitive-complexity":"warn","maintainability/identical-functions":"warn","maintainability/max-parameters":"warn"}}};exports.default=exports.plugin;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.configs=exports.plugin=exports.rules=void 0;exports.rules={get"cognitive-complexity"(){return require("./rules/maintainability/cognitive-complexity").cognitiveComplexity},get"nested-complexity-hotspots"(){return require("./rules/maintainability/nested-complexity-hotspots").nestedComplexityHotspots},get"identical-functions"(){return require("./rules/maintainability/identical-functions").identicalFunctions},get"max-parameters"(){return require("./rules/maintainability/max-parameters").maxParameters},get"no-lonely-if"(){return require("./rules/maintainability/no-lonely-if").noLonelyIf},get"no-nested-ternary"(){return require("./rules/maintainability/no-nested-ternary").noNestedTernary},get"consistent-function-scoping"(){return require("./rules/maintainability/consistent-function-scoping").consistentFunctionScoping},get"no-unreadable-iife"(){return require("./rules/maintainability/no-unreadable-iife").noUnreadableIife},get"error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise},get"maintainability/cognitive-complexity"(){return require("./rules/maintainability/cognitive-complexity").cognitiveComplexity},get"maintainability/nested-complexity-hotspots"(){return require("./rules/maintainability/nested-complexity-hotspots").nestedComplexityHotspots},get"maintainability/identical-functions"(){return require("./rules/maintainability/identical-functions").identicalFunctions},get"maintainability/max-parameters"(){return require("./rules/maintainability/max-parameters").maxParameters},get"maintainability/no-lonely-if"(){return require("./rules/maintainability/no-lonely-if").noLonelyIf},get"maintainability/no-nested-ternary"(){return require("./rules/maintainability/no-nested-ternary").noNestedTernary},get"maintainability/consistent-function-scoping"(){return require("./rules/maintainability/consistent-function-scoping").consistentFunctionScoping},get"maintainability/no-unreadable-iife"(){return require("./rules/maintainability/no-unreadable-iife").noUnreadableIife},get"maintainability/error-message"(){return require("./rules/error-handling/error-message").errorMessage},get"maintainability/no-missing-error-context"(){return require("./rules/error-handling/no-missing-error-context").noMissingErrorContext},get"maintainability/no-silent-errors"(){return require("./rules/error-handling/no-silent-errors").noSilentErrors},get"maintainability/no-unhandled-promise"(){return require("./rules/error-handling/no-unhandled-promise").noUnhandledPromise}};exports.plugin={meta:{name:"eslint-plugin-maintainability",version:"3.1.0"},rules:exports.rules};exports.configs={recommended:{plugins:{"maintainability":exports.plugin},rules:{"maintainability/cognitive-complexity":"warn","maintainability/identical-functions":"warn","maintainability/max-parameters":"warn"}}};exports.default=exports.plugin;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.cognitiveComplexity=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const eslint_devkit_3=require("@interlace/eslint-devkit");exports.cognitiveComplexity=(0,eslint_devkit_2.createRule)({name:"cognitive-complexity",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-maintainability/docs/rules/cognitive-complexity.md",description:"Enforces a maximum cognitive complexity threshold with refactoring guidance",cwe:"CWE-1104",cvss:7.5},hasSuggestions:true,messages:{highCognitiveComplexity:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.COMPLEXITY,issueName:"High cognitive complexity",cwe:"CWE-1104",description:"{{functionName}}: {{complexity}}/{{max}} ({{overBy}} over)",severity:"HIGH",fix:"Extract logic to helpers",documentationLink:"https://en.wikipedia.org/wiki/Cognitive_complexity"}),extractMethod:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Extract Method",description:"Extract nested logic to separate method",severity:"LOW",fix:'Extract to "{{methodName}}" (reduces complexity by ~{{reduction}})',documentationLink:"https://refactoring.guru/extract-method"}),simplifyLogic:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Simplify Logic",description:"Simplify conditional logic",severity:"LOW",fix:"Use guard clauses and early returns",documentationLink:"https://refactoring.guru/replace-nested-conditional-with-guard-clauses"}),useStrategy:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Strategy Pattern",description:"Apply design pattern to reduce complexity",severity:"LOW",fix:"Apply {{pattern}} pattern",documentationLink:"https://refactoring.guru/design-patterns/strategy"})},schema:[{type:"object",properties:{maxComplexity:{type:"number",default:15,minimum:1},includeMetrics:{type:"boolean",default:true}},additionalProperties:false}]},defaultOptions:[{maxComplexity:15,includeMetrics:true}],create(context){const{maxComplexity=15}=context.options[0]||{};const filename=context.filename;function calculateCognitiveComplexity(node){let complexity=0;const breakdown={conditionals:0,loops:0,switches:0,nesting:0,logicalOperators:0,catches:0,recursion:0};const functionName=node.type==="FunctionDeclaration"&&node.id?node.id.name:"anonymous";function traverse(n,currentNesting){if(n.type==="IfStatement"){complexity+=1+currentNesting;breakdown.conditionals++;traverse(n.test,currentNesting);traverse(n.consequent,currentNesting+1);if(n.alternate){if(n.alternate.type==="IfStatement"){traverse(n.alternate,currentNesting)}else{complexity+=1;traverse(n.alternate,currentNesting+1)}}return}if(n.type==="ForStatement"||n.type==="ForInStatement"||n.type==="ForOfStatement"||n.type==="WhileStatement"||n.type==="DoWhileStatement"){complexity+=1+currentNesting;breakdown.loops++;if(n.type==="ForStatement"){if(n.init)traverse(n.init,currentNesting);if(n.test)traverse(n.test,currentNesting);if(n.update)traverse(n.update,currentNesting);traverse(n.body,currentNesting+1)}else if(n.type==="WhileStatement"||n.type==="DoWhileStatement"){traverse(n.test,currentNesting);traverse(n.body,currentNesting+1)}else{if("left"in n)traverse(n.left,currentNesting);if("right"in n)traverse(n.right,currentNesting);traverse(n.body,currentNesting+1)}return}if(n.type==="SwitchStatement"){complexity+=1+currentNesting;breakdown.switches++;traverse(n.discriminant,currentNesting);n.cases.forEach(c=>traverse(c,currentNesting+1));return}if(n.type==="LogicalExpression"){if(n.operator==="&&"||n.operator==="||"||n.operator==="??"){complexity+=1;breakdown.logicalOperators++}}if(n.type==="CatchClause"){complexity+=1+currentNesting;breakdown.catches++;traverse(n.body,currentNesting+1);return}if(n.type==="ConditionalExpression"){complexity+=1+currentNesting;breakdown.conditionals++}if(n.type==="CallExpression"){if(n.callee.type==="Identifier"&&n.callee.name===functionName){complexity+=1;breakdown.recursion++}}if(n.type==="BlockStatement"||n.type==="FunctionDeclaration"||n.type==="FunctionExpression"||n.type==="ArrowFunctionExpression"){breakdown.nesting=Math.max(breakdown.nesting,currentNesting)}const visited=new Set;function traverseChild(child){if(child&&typeof child==="object"&&"type"in child){const childNode=child;if(!visited.has(childNode)){visited.add(childNode);traverse(childNode,currentNesting)}}}const childKeys=["body","test","consequent","alternate","init","update","left","right","argument","arguments","callee","object","property","elements","properties","expression","expressions","declarations","declaration","specifiers","source","key","value","handler","block","finalizer"];for(const key of childKeys){const child=n[key];if(child){if(Array.isArray(child)){child.forEach(traverseChild)}else{traverseChild(child)}}}}if(node.body){traverse(node.body,0)}return{total:complexity,breakdown}}function suggestExtractions(node,breakdown){const suggestions=[];if(breakdown.nesting>=4&&node.loc){suggestions.push({name:"extractNestedLogic",reason:`Nesting depth of ${breakdown.nesting} makes code hard to follow`,lineRange:[node.loc.start.line,node.loc.end.line],estimatedComplexityReduction:Math.floor(breakdown.nesting*1.5)})}if(breakdown.switches>=2&&node.loc){suggestions.push({name:"refactorSwitchToStrategy",reason:`${breakdown.switches} switch statements suggest strategy pattern`,lineRange:[node.loc.start.line,node.loc.end.line],estimatedComplexityReduction:breakdown.switches*2})}if(breakdown.loops>=3&&node.loc){suggestions.push({name:"extractLoopLogic",reason:`${breakdown.loops} loops can be extracted to separate methods`,lineRange:[node.loc.start.line,node.loc.end.line],estimatedComplexityReduction:breakdown.loops*2})}if(breakdown.conditionals>=5&&node.loc){suggestions.push({name:"simplifyConditionals",reason:`${breakdown.conditionals} conditional branches could use Guard Clauses`,lineRange:[node.loc.start.line,node.loc.end.line],estimatedComplexityReduction:Math.floor(breakdown.conditionals*.8)})}return suggestions}function suggestPattern(breakdown){if(breakdown.switches>=2)return"Strategy Pattern";if(breakdown.conditionals>=5)return"Guard Clauses + Early Return";if(breakdown.loops>=3)return"Extract Method + Pipeline";if(breakdown.nesting>=4)return"Extract Method + Composed Functions";return"Extract Method"}function estimateRefactoringTime(complexity,breakdown){const baseTime=Math.floor((complexity-maxComplexity)*3);const nestingPenalty=breakdown.nesting>=4?15:0;const totalMinutes=baseTime+nestingPenalty;if(totalMinutes<30)return`${totalMinutes} minutes`;if(totalMinutes<60)return"30-60 minutes";return`${Math.ceil(totalMinutes/60)} hours`}function checkFunction(node){const{total:complexity,breakdown}=calculateCognitiveComplexity(node);if(complexity<=maxComplexity)return;const functionSignature=(0,eslint_devkit_3.extractFunctionSignature)(node);const suggestions=suggestExtractions(node,breakdown);const pattern=suggestPattern(breakdown);const estimatedTime=estimateRefactoringTime(complexity,breakdown);context.report({node,messageId:"highCognitiveComplexity",data:{functionName:functionSignature,complexity:String(complexity),max:String(maxComplexity),overBy:String(complexity-maxComplexity),current:String(complexity),filePath:filename,line:String(node.loc?.start.line??0),conditionals:String(breakdown.conditionals),loops:String(breakdown.loops),nesting:String(breakdown.nesting),pattern,estimatedTime},suggest:suggestions.length>0?suggestions.map((suggestion,index)=>{const messageId=index===0?"extractMethod":index===1?"useStrategy":"simplifyLogic";return{messageId,data:{methodName:suggestion.name,pattern,reduction:String(suggestion.estimatedComplexityReduction)},fix:()=>null}}):void 0})}return{FunctionDeclaration:checkFunction,FunctionExpression:checkFunction,ArrowFunctionExpression:checkFunction}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.cognitiveComplexity=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const eslint_devkit_3=require("@interlace/eslint-devkit");exports.cognitiveComplexity=(0,eslint_devkit_2.createRule)({name:"cognitive-complexity",skipGeneratedFiles:true,meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-maintainability/docs/rules/cognitive-complexity.md",description:"Enforces a maximum cognitive complexity threshold with refactoring guidance",cwe:"CWE-1104",cvss:7.5},hasSuggestions:true,messages:{highCognitiveComplexity:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.COMPLEXITY,issueName:"High cognitive complexity",cwe:"CWE-1104",description:"{{functionName}}: {{complexity}}/{{max}} ({{overBy}} over)",severity:"HIGH",fix:"Extract logic to helpers",documentationLink:"https://en.wikipedia.org/wiki/Cognitive_complexity"}),extractMethod:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Extract Method",description:"Extract nested logic to separate method",severity:"LOW",fix:'Extract to "{{methodName}}" (reduces complexity by ~{{reduction}})',documentationLink:"https://refactoring.guru/extract-method"}),simplifyLogic:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Simplify Logic",description:"Simplify conditional logic",severity:"LOW",fix:"Use guard clauses and early returns",documentationLink:"https://refactoring.guru/replace-nested-conditional-with-guard-clauses"}),useStrategy:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Strategy Pattern",description:"Apply design pattern to reduce complexity",severity:"LOW",fix:"Apply {{pattern}} pattern",documentationLink:"https://refactoring.guru/design-patterns/strategy"})},schema:[{type:"object",properties:{maxComplexity:{type:"number",default:15,minimum:1},includeMetrics:{type:"boolean",default:true}},additionalProperties:false}]},defaultOptions:[{maxComplexity:15,includeMetrics:true}],create(context){const{maxComplexity=15}=context.options[0]||{};const filename=context.filename;function calculateCognitiveComplexity(node){let complexity=0;const breakdown={conditionals:0,loops:0,switches:0,nesting:0,logicalOperators:0,catches:0,recursion:0};const functionName=node.type==="FunctionDeclaration"&&node.id?node.id.name:"anonymous";function traverse(n,currentNesting){if(n.type==="IfStatement"){complexity+=1+currentNesting;breakdown.conditionals++;traverse(n.test,currentNesting);traverse(n.consequent,currentNesting+1);if(n.alternate){if(n.alternate.type==="IfStatement"){traverse(n.alternate,currentNesting)}else{complexity+=1;traverse(n.alternate,currentNesting+1)}}return}if(n.type==="ForStatement"||n.type==="ForInStatement"||n.type==="ForOfStatement"||n.type==="WhileStatement"||n.type==="DoWhileStatement"){complexity+=1+currentNesting;breakdown.loops++;if(n.type==="ForStatement"){if(n.init)traverse(n.init,currentNesting);if(n.test)traverse(n.test,currentNesting);if(n.update)traverse(n.update,currentNesting);traverse(n.body,currentNesting+1)}else if(n.type==="WhileStatement"||n.type==="DoWhileStatement"){traverse(n.test,currentNesting);traverse(n.body,currentNesting+1)}else{if("left"in n)traverse(n.left,currentNesting);if("right"in n)traverse(n.right,currentNesting);traverse(n.body,currentNesting+1)}return}if(n.type==="SwitchStatement"){complexity+=1+currentNesting;breakdown.switches++;traverse(n.discriminant,currentNesting);n.cases.forEach(c=>traverse(c,currentNesting+1));return}if(n.type==="LogicalExpression"){if(n.operator==="&&"||n.operator==="||"||n.operator==="??"){complexity+=1;breakdown.logicalOperators++}}if(n.type==="CatchClause"){complexity+=1+currentNesting;breakdown.catches++;traverse(n.body,currentNesting+1);return}if(n.type==="ConditionalExpression"){complexity+=1+currentNesting;breakdown.conditionals++}if(n.type==="CallExpression"){if(n.callee.type==="Identifier"&&n.callee.name===functionName){complexity+=1;breakdown.recursion++}}if(n.type==="BlockStatement"||n.type==="FunctionDeclaration"||n.type==="FunctionExpression"||n.type==="ArrowFunctionExpression"){breakdown.nesting=Math.max(breakdown.nesting,currentNesting)}const visited=new Set;function traverseChild(child){if(child&&typeof child==="object"&&"type"in child){const childNode=child;if(!visited.has(childNode)){visited.add(childNode);traverse(childNode,currentNesting)}}}const childKeys=["body","test","consequent","alternate","init","update","left","right","argument","arguments","callee","object","property","elements","properties","expression","expressions","declarations","declaration","specifiers","source","key","value","handler","block","finalizer"];for(const key of childKeys){const child=n[key];if(child){if(Array.isArray(child)){child.forEach(traverseChild)}else{traverseChild(child)}}}}if(node.body){traverse(node.body,0)}return{total:complexity,breakdown}}function suggestExtractions(node,breakdown){const suggestions=[];if(breakdown.nesting>=4&&node.loc){suggestions.push({name:"extractNestedLogic",reason:`Nesting depth of ${breakdown.nesting} makes code hard to follow`,lineRange:[node.loc.start.line,node.loc.end.line],estimatedComplexityReduction:Math.floor(breakdown.nesting*1.5)})}if(breakdown.switches>=2&&node.loc){suggestions.push({name:"refactorSwitchToStrategy",reason:`${breakdown.switches} switch statements suggest strategy pattern`,lineRange:[node.loc.start.line,node.loc.end.line],estimatedComplexityReduction:breakdown.switches*2})}if(breakdown.loops>=3&&node.loc){suggestions.push({name:"extractLoopLogic",reason:`${breakdown.loops} loops can be extracted to separate methods`,lineRange:[node.loc.start.line,node.loc.end.line],estimatedComplexityReduction:breakdown.loops*2})}if(breakdown.conditionals>=5&&node.loc){suggestions.push({name:"simplifyConditionals",reason:`${breakdown.conditionals} conditional branches could use Guard Clauses`,lineRange:[node.loc.start.line,node.loc.end.line],estimatedComplexityReduction:Math.floor(breakdown.conditionals*.8)})}return suggestions}function suggestPattern(breakdown){if(breakdown.switches>=2)return"Strategy Pattern";if(breakdown.conditionals>=5)return"Guard Clauses + Early Return";if(breakdown.loops>=3)return"Extract Method + Pipeline";if(breakdown.nesting>=4)return"Extract Method + Composed Functions";return"Extract Method"}function estimateRefactoringTime(complexity,breakdown){const baseTime=Math.floor((complexity-maxComplexity)*3);const nestingPenalty=breakdown.nesting>=4?15:0;const totalMinutes=baseTime+nestingPenalty;if(totalMinutes<30)return`${totalMinutes} minutes`;if(totalMinutes<60)return"30-60 minutes";return`${Math.ceil(totalMinutes/60)} hours`}function checkFunction(node){const{total:complexity,breakdown}=calculateCognitiveComplexity(node);if(complexity<=maxComplexity)return;const functionSignature=(0,eslint_devkit_3.extractFunctionSignature)(node);const suggestions=suggestExtractions(node,breakdown);const pattern=suggestPattern(breakdown);const estimatedTime=estimateRefactoringTime(complexity,breakdown);context.report({node,messageId:"highCognitiveComplexity",data:{functionName:functionSignature,complexity:String(complexity),max:String(maxComplexity),overBy:String(complexity-maxComplexity),current:String(complexity),filePath:filename,line:String(node.loc?.start.line??0),conditionals:String(breakdown.conditionals),loops:String(breakdown.loops),nesting:String(breakdown.nesting),pattern,estimatedTime},suggest:suggestions.length>0?suggestions.map((suggestion,index)=>{const messageId=index===0?"extractMethod":index===1?"useStrategy":"simplifyLogic";return{messageId,data:{methodName:suggestion.name,pattern,reduction:String(suggestion.estimatedComplexityReduction)},fix:()=>null}}):void 0})}return{FunctionDeclaration:checkFunction,FunctionExpression:checkFunction,ArrowFunctionExpression:checkFunction}}});
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.identicalFunctions=void 0;exports.buildGenericName=buildGenericName;exports.calculateSimilarity=calculateSimilarity;exports.levenshteinDistance=levenshteinDistance;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const eslint_devkit_3=require("@interlace/eslint-devkit");function buildGenericName(firstFunctionName){const baseName=firstFunctionName.replace(/^(handle|process|get|set|create|update|delete)/,"");return`handle${baseName||"Generic"}`}function calculateSimilarity(str1,str2,similarityThreshold){if(str1===str2)return 1;const longer=str1.length>str2.length?str1:str2;const shorter=str1.length>str2.length?str2:str1;if(shorter.length/longer.length<similarityThreshold)return 0;const budget=longer.length-Math.ceil(longer.length*similarityThreshold);const editDistance=levenshteinDistance(longer,shorter,budget);if(editDistance<0)return 0;return(longer.length-editDistance)/longer.length}function levenshteinDistance(str1,str2,budget){let previous=[];let current=[];for(let j=0;j<=str1.length;j++)previous[j]=j;for(let i=1;i<=str2.length;i++){current[0]=i;let rowMin=current[0];for(let j=1;j<=str1.length;j++){current[j]=str2.charAt(i-1)===str1.charAt(j-1)?previous[j-1]:Math.min(previous[j-1]+1,current[j-1]+1,previous[j]+1);if(current[j]<rowMin)rowMin=current[j]}if(rowMin>budget)return-1;const swap=previous;previous=current;current=swap}return previous[str1.length]}exports.identicalFunctions=(0,eslint_devkit_2.createRule)({name:"identical-functions",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-maintainability/docs/rules/identical-functions.md",description:"Detects duplicate function implementations with DRY refactoring suggestions"},hasSuggestions:true,messages:{identicalFunctions:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.DUPLICATION,issueName:"Code duplication",description:"{{count}} duplicates ({{similarity}}% similar)",severity:"MEDIUM",fix:"Extract to reusable function",documentationLink:"https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"}),extractGeneric:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Extract Generic",description:"Extract to generic function",severity:"LOW",fix:"Create shared function with parameters",documentationLink:"https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"}),useHigherOrder:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Higher-Order",description:"Use higher-order function pattern",severity:"LOW",fix:"Create factory function that returns specialized functions",documentationLink:"https://developer.mozilla.org/en-US/docs/Glossary/Higher-order_function"}),applyInheritance:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Composition",description:"Use inheritance/composition",severity:"LOW",fix:"Extract common behavior to base class or mixin",documentationLink:"https://en.wikipedia.org/wiki/Composition_over_inheritance"})},schema:[{type:"object",properties:{minLines:{type:"number",default:3,minimum:1,description:"Minimum lines to consider for duplication"},similarityThreshold:{type:"number",default:.9,minimum:.5,maximum:1,description:"Similarity threshold (0.5-1.0)"},ignoreTestFiles:{type:"boolean",default:true}},additionalProperties:false}]},defaultOptions:[{minLines:3,similarityThreshold:.9,ignoreTestFiles:true}],create(context){const{minLines=3,similarityThreshold=.9,ignoreTestFiles=true}=context.options[0]||{};const sourceCode=context.sourceCode;const filename=context.filename;if(ignoreTestFiles&&/\.(test|spec)\.[jt]sx?$/.test(filename)){return{}}const functions=[];function normalizeBody(body){return body.replace(/\s+/g," ").replace(/["'`]/g,'"').replace(/\b[a-z_$][a-zA-Z0-9_$]*\b/g,"VAR").replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/.*/g,"").trim()}function findDuplicationGroups(){const groups=[];const processed=new Set;for(let i=0;i<functions.length;i++){if(processed.has(i))continue;const group=[functions[i]];processed.add(i);for(let j=i+1;j<functions.length;j++){if(processed.has(j))continue;const similarity=calculateSimilarity(functions[i].normalizedBody,functions[j].normalizedBody,similarityThreshold);if(similarity>=similarityThreshold){group.push(functions[j]);processed.add(j)}}if(group.length>=2){const avgSimilarity=group.reduce((sum,func,idx)=>{if(idx===0)return 0;return sum+calculateSimilarity(group[0].normalizedBody,func.normalizedBody,similarityThreshold)},0)/(group.length-1);groups.push({functions:group,similarityScore:avgSimilarity,commonPattern:functions[i].normalizedBody})}}return groups}function suggestRefactoringApproach(group){const funcNames=group.functions.map(f=>f.name);const hasRolePattern=funcNames.some(name=>/user|admin|guest|customer/i.test(name));const hasTypePattern=funcNames.some(name=>/payment|shipping|billing|email|sms/i.test(name));if(hasRolePattern||hasTypePattern){return{approach:"Parameter Object + Strategy Pattern",pattern:"Extract discriminator as parameter",complexity:"moderate"}}if(group.functions[0].params.length>0){return{approach:"Higher-Order Function",pattern:"Extract common logic, inject differences",complexity:"simple"}}return{approach:"Extract Method",pattern:"DRY - Single source of truth",complexity:"simple"}}function storeFunctionInfo(node){const body=node.body?sourceCode.getText(node.body):"";const lines=body.split("\n").length;if(lines<minLines)return;const name=(0,eslint_devkit_3.extractFunctionSignature)(node).split("(")[0].replace("function ","");const params=node.params.map(p=>p.type==="Identifier"?p.name:sourceCode.getText(p));functions.push({node,name:name||"anonymous",body,normalizedBody:normalizeBody(body),lines,location:`${filename}:${node.loc?.start.line}`,params})}function reportDuplications(){const groups=findDuplicationGroups();groups.forEach(group=>{const refactoringApproach=suggestRefactoringApproach(group);const primaryFunction=group.functions[0];const similarityPercent=Math.round(group.similarityScore*100);context.report({node:primaryFunction.node,messageId:"identicalFunctions",data:{count:String(group.functions.length),similarity:String(similarityPercent),filePath:filename,line:String(primaryFunction.node.loc?.start.line??0)},suggest:[{messageId:"extractGeneric",data:{functionName:buildGenericName(primaryFunction.name)},fix:()=>null},...refactoringApproach.approach.includes("Higher-Order")?[{messageId:"useHigherOrder",fix:()=>null}]:[],...refactoringApproach.approach.includes("Strategy")?[{messageId:"applyInheritance",fix:()=>null}]:[]]})})}return{FunctionDeclaration:storeFunctionInfo,FunctionExpression:storeFunctionInfo,ArrowFunctionExpression:storeFunctionInfo,"Program:exit":reportDuplications}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.identicalFunctions=void 0;exports.buildGenericName=buildGenericName;exports.calculateSimilarity=calculateSimilarity;exports.levenshteinDistance=levenshteinDistance;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const eslint_devkit_3=require("@interlace/eslint-devkit");function buildGenericName(firstFunctionName){const baseName=firstFunctionName.replace(/^(handle|process|get|set|create|update|delete)/,"");return`handle${baseName||"Generic"}`}const RESERVED_WORDS=new Set(["await","break","case","catch","class","const","continue","debugger","default","delete","do","else","export","extends","finally","for","function","if","import","in","instanceof","let","new","of","return","super","switch","this","throw","try","typeof","var","void","while","with","yield","true","false","null","undefined"]);function calculateSimilarity(str1,str2,similarityThreshold){if(str1===str2)return 1;const longer=str1.length>str2.length?str1:str2;const shorter=str1.length>str2.length?str2:str1;if(shorter.length/longer.length<similarityThreshold)return 0;const budget=longer.length-Math.ceil(longer.length*similarityThreshold);const editDistance=levenshteinDistance(longer,shorter,budget);if(editDistance<0)return 0;return(longer.length-editDistance)/longer.length}function levenshteinDistance(str1,str2,budget){let previous=[];let current=[];for(let j=0;j<=str1.length;j++)previous[j]=j;for(let i=1;i<=str2.length;i++){current[0]=i;let rowMin=current[0];for(let j=1;j<=str1.length;j++){current[j]=str2.charAt(i-1)===str1.charAt(j-1)?previous[j-1]:Math.min(previous[j-1]+1,current[j-1]+1,previous[j]+1);if(current[j]<rowMin)rowMin=current[j]}if(rowMin>budget)return-1;const swap=previous;previous=current;current=swap}return previous[str1.length]}exports.identicalFunctions=(0,eslint_devkit_2.createRule)({name:"identical-functions",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-maintainability/docs/rules/identical-functions.md",description:"Detects duplicate function implementations with DRY refactoring suggestions"},hasSuggestions:true,messages:{identicalFunctions:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.DUPLICATION,issueName:"Code duplication",description:"{{count}} duplicates ({{similarity}}% similar)",severity:"MEDIUM",fix:"Extract to reusable function",documentationLink:"https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"}),extractGeneric:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Extract Generic",description:"Extract to generic function",severity:"LOW",fix:"Create shared function with parameters",documentationLink:"https://en.wikipedia.org/wiki/Don%27t_repeat_yourself"}),useHigherOrder:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Higher-Order",description:"Use higher-order function pattern",severity:"LOW",fix:"Create factory function that returns specialized functions",documentationLink:"https://developer.mozilla.org/en-US/docs/Glossary/Higher-order_function"}),applyInheritance:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Composition",description:"Use inheritance/composition",severity:"LOW",fix:"Extract common behavior to base class or mixin",documentationLink:"https://en.wikipedia.org/wiki/Composition_over_inheritance"})},schema:[{type:"object",properties:{minLines:{type:"number",default:3,minimum:1,description:"Minimum lines to consider for duplication"},similarityThreshold:{type:"number",default:.9,minimum:.5,maximum:1,description:"Similarity threshold (0.5-1.0)"},ignoreTestFiles:{type:"boolean",default:true},ignoreGeneratedFiles:{type:"boolean",default:true}},additionalProperties:false}]},defaultOptions:[{minLines:3,similarityThreshold:.9,ignoreTestFiles:true,ignoreGeneratedFiles:true}],create(context){const{minLines=3,similarityThreshold=.9,ignoreTestFiles=true,ignoreGeneratedFiles=true}=context.options[0]||{};const sourceCode=context.sourceCode;const filename=context.filename;if(ignoreTestFiles&&(0,eslint_devkit_3.isTestFilePath)(filename)){return{}}if(ignoreGeneratedFiles&&(0,eslint_devkit_3.isGeneratedFile)(sourceCode)){return{}}const functions=[];function normalizeBody(body){const literals=[];const stash=match=>{literals.push(`"${match.slice(1,-1)}"`);return`\uE000${literals.length-1}\uE000`};let text=body.replace(/`(?:[^\\`]|\\[\s\S])*`/g,stash).replace(/(["'])(?:[^\\\n]|\\.)*?\1/g,stash).replace(/(^|[=(,:[!&|?{};+\-*%<>~^]\s*)(\/(?:[^/\\\n[]|\\.|\[(?:[^\]\\]|\\.)*\])+\/[dgimsuvy]*)/g,(_match,prefix,pattern)=>{literals.push(pattern);return`${prefix}\uE000${literals.length-1}\uE000`});text=text.replace(/\/\*[\s\S]*?\*\//g,"").replace(/\/\/[^\n]*/g,"").replace(/\s+/g," ").replace(/(\.\s*)?\b[a-z_$][a-zA-Z0-9_$]*\b(\s*:)?/g,(match,memberPrefix,keySuffix)=>{if(memberPrefix||keySuffix)return match;return RESERVED_WORDS.has(match)?match:"VAR"});return text.replace(/\uE000(\d+)\uE000/g,(_match,index)=>literals[Number(index)]).trim()}function findDuplicationGroups(){const groups=[];const processed=new Set;for(let i=0;i<functions.length;i++){if(processed.has(i))continue;const group=[functions[i]];processed.add(i);for(let j=i+1;j<functions.length;j++){if(processed.has(j))continue;if(functions[i].isAsync!==functions[j].isAsync||functions[i].isGenerator!==functions[j].isGenerator){continue}const similarity=calculateSimilarity(functions[i].normalizedBody,functions[j].normalizedBody,similarityThreshold);const SHORT_BODY_CHARS=120;const longer=Math.max(functions[i].normalizedBody.length,functions[j].normalizedBody.length);const required=longer<SHORT_BODY_CHARS?1:similarityThreshold;if(similarity>=required){group.push(functions[j]);processed.add(j)}}if(group.length>=2){const avgSimilarity=group.reduce((sum,func,idx)=>{if(idx===0)return 0;return sum+calculateSimilarity(group[0].normalizedBody,func.normalizedBody,similarityThreshold)},0)/(group.length-1);groups.push({functions:group,similarityScore:avgSimilarity,commonPattern:functions[i].normalizedBody})}}return groups}function suggestRefactoringApproach(group){const funcNames=group.functions.map(f=>f.name);const hasRolePattern=funcNames.some(name=>/user|admin|guest|customer/i.test(name));const hasTypePattern=funcNames.some(name=>/payment|shipping|billing|email|sms/i.test(name));if(hasRolePattern||hasTypePattern){return{approach:"Parameter Object + Strategy Pattern",pattern:"Extract discriminator as parameter",complexity:"moderate"}}if(group.functions[0].params.length>0){return{approach:"Higher-Order Function",pattern:"Extract common logic, inject differences",complexity:"simple"}}return{approach:"Extract Method",pattern:"DRY - Single source of truth",complexity:"simple"}}function storeFunctionInfo(node){const body=node.body?sourceCode.getText(node.body):"";const lines=body.split("\n").length;if(lines<minLines)return;const name=(0,eslint_devkit_3.extractFunctionSignature)(node).split("(")[0].replace("function ","");const params=node.params.map(p=>p.type==="Identifier"?p.name:sourceCode.getText(p));functions.push({node,name:name||"anonymous",body,normalizedBody:normalizeBody(body),isAsync:Boolean(node.async),isGenerator:"generator"in node&&Boolean(node.generator),lines,location:`${filename}:${node.loc?.start.line}`,params})}function reportDuplications(){const groups=findDuplicationGroups();groups.forEach(group=>{const refactoringApproach=suggestRefactoringApproach(group);const primaryFunction=group.functions[0];const similarityPercent=Math.round(group.similarityScore*100);context.report({node:primaryFunction.node,messageId:"identicalFunctions",data:{count:String(group.functions.length),similarity:String(similarityPercent),filePath:filename,line:String(primaryFunction.node.loc?.start.line??0)},suggest:[{messageId:"extractGeneric",data:{functionName:buildGenericName(primaryFunction.name)},fix:()=>null},...refactoringApproach.approach.includes("Higher-Order")?[{messageId:"useHigherOrder",fix:()=>null}]:[],...refactoringApproach.approach.includes("Strategy")?[{messageId:"applyInheritance",fix:()=>null}]:[]]})})}return{FunctionDeclaration:storeFunctionInfo,FunctionExpression:storeFunctionInfo,ArrowFunctionExpression:storeFunctionInfo,"Program:exit":reportDuplications}}});
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.maxParameters=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const eslint_devkit_3=require("@interlace/eslint-devkit");function countParameters(node){return node.params.length}exports.maxParameters=(0,eslint_devkit_2.createRule)({name:"max-parameters",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-maintainability/docs/rules/max-parameters.md",description:"Detects functions with too many parameters"},hasSuggestions:true,messages:{tooManyParameters:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.COMPLEXITY,issueName:"Too many parameters",description:"{{functionName}}: {{count}} parameters (max: {{max}})",severity:"MEDIUM",fix:"Refactor to use object parameter or split function",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-107/"}),useObjectParameter:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Object Parameter",description:"Use object parameter pattern",severity:"LOW",fix:"function({ param1, param2, param3 })",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment"}),extractToClass:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Extract to Class",description:"Extract to class with properties",severity:"LOW",fix:"Create class to hold related parameters",documentationLink:"https://refactoring.guru/introduce-parameter-object"}),splitFunction:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Split Function",description:"Split into smaller functions",severity:"LOW",fix:"Extract logic into separate focused functions",documentationLink:"https://refactoring.guru/smells/long-parameter-list"})},schema:[{type:"object",properties:{max:{type:"number",default:4,minimum:1},ignoreConstructors:{type:"boolean",default:false},ignoreOverriddenMethods:{type:"boolean",default:false}},additionalProperties:false}]},defaultOptions:[{max:4,ignoreConstructors:false,ignoreOverriddenMethods:false}],create(context,[options={}]){const{max=4,ignoreConstructors=false}=options||{};function checkFunction(node){if(ignoreConstructors){if(node.type==="FunctionDeclaration"&&node.id&&node.id.name&&/^[A-Z]/.test(node.id.name)){return}}const paramCount=countParameters(node);if(paramCount<=max){return}const functionSignature=(0,eslint_devkit_3.extractFunctionSignature)(node);const overBy=paramCount-max;context.report({node,messageId:"tooManyParameters",data:{functionName:functionSignature,count:String(paramCount),max:String(max),overBy:String(overBy)},suggest:[{messageId:"useObjectParameter",fix:()=>null},{messageId:"extractToClass",fix:()=>null},{messageId:"splitFunction",fix:()=>null}]})}return{FunctionDeclaration:checkFunction,FunctionExpression:checkFunction,ArrowFunctionExpression:checkFunction}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.maxParameters=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const eslint_devkit_3=require("@interlace/eslint-devkit");function countParameters(node){return node.params.length}exports.maxParameters=(0,eslint_devkit_2.createRule)({name:"max-parameters",skipGeneratedFiles:true,meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-maintainability/docs/rules/max-parameters.md",description:"Detects functions with too many parameters"},hasSuggestions:true,messages:{tooManyParameters:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.COMPLEXITY,issueName:"Too many parameters",description:"{{functionName}}: {{count}} parameters (max: {{max}})",severity:"MEDIUM",fix:"Refactor to use object parameter or split function",documentationLink:"https://rules.sonarsource.com/javascript/RSPEC-107/"}),useObjectParameter:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Use Object Parameter",description:"Use object parameter pattern",severity:"LOW",fix:"function({ param1, param2, param3 })",documentationLink:"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment"}),extractToClass:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Extract to Class",description:"Extract to class with properties",severity:"LOW",fix:"Create class to hold related parameters",documentationLink:"https://refactoring.guru/introduce-parameter-object"}),splitFunction:(0,eslint_devkit_1.formatLLMMessage)({icon:eslint_devkit_1.MessageIcons.INFO,issueName:"Split Function",description:"Split into smaller functions",severity:"LOW",fix:"Extract logic into separate focused functions",documentationLink:"https://refactoring.guru/smells/long-parameter-list"})},schema:[{type:"object",properties:{max:{type:"number",default:4,minimum:1},ignoreConstructors:{type:"boolean",default:false},ignoreOverriddenMethods:{type:"boolean",default:false}},additionalProperties:false}]},defaultOptions:[{max:4,ignoreConstructors:false,ignoreOverriddenMethods:false}],create(context,[options={}]){const{max=4,ignoreConstructors=false}=options||{};function checkFunction(node){if(ignoreConstructors){if(node.type==="FunctionDeclaration"&&node.id&&node.id.name&&/^[A-Z]/.test(node.id.name)){return}}const paramCount=countParameters(node);if(paramCount<=max){return}const functionSignature=(0,eslint_devkit_3.extractFunctionSignature)(node);const overBy=paramCount-max;context.report({node,messageId:"tooManyParameters",data:{functionName:functionSignature,count:String(paramCount),max:String(max),overBy:String(overBy)},suggest:[{messageId:"useObjectParameter",fix:()=>null},{messageId:"extractToClass",fix:()=>null},{messageId:"splitFunction",fix:()=>null}]})}return{FunctionDeclaration:checkFunction,FunctionExpression:checkFunction,ArrowFunctionExpression:checkFunction}}});