eslint-plugin-maintainability 3.2.6 → 3.2.8

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
@@ -123,6 +123,7 @@ Part of the **Interlace ESLint ecosystem** — AI-native rules with LLM-optimize
123
123
  | [`eslint-plugin-secure-coding`](https://www.npmjs.com/package/eslint-plugin-secure-coding) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-secure-coding.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-secure-coding) | Injection prevention. |
124
124
  | [`eslint-plugin-sequelize-security`](https://www.npmjs.com/package/eslint-plugin-sequelize-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-sequelize-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-sequelize-security) | Sequelize ORM security. |
125
125
  | [`eslint-plugin-sqlite-security`](https://www.npmjs.com/package/eslint-plugin-sqlite-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-sqlite-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-sqlite-security) | SQLite security. |
126
+ | [`eslint-plugin-supabase-security`](https://www.npmjs.com/package/eslint-plugin-supabase-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-supabase-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-supabase-security) | Supabase security. |
126
127
  | [`eslint-plugin-typeorm-security`](https://www.npmjs.com/package/eslint-plugin-typeorm-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-typeorm-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-typeorm-security) | TypeORM security. |
127
128
  | [`eslint-plugin-vercel-ai-security`](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | [![downloads](https://img.shields.io/npm/dt/eslint-plugin-vercel-ai-security.svg?style=flat-square)](https://www.npmjs.com/package/eslint-plugin-vercel-ai-security) | AI SDK security. |
128
129
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-maintainability",
3
- "version": "3.2.6",
3
+ "version": "3.2.8",
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",
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"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}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-maintainability",exports.rules);exports.plugin={meta:{name:"eslint-plugin-maintainability",version:"3.2.6"},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;const eslint_devkit_1=require("@interlace/eslint-devkit");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}};(0,eslint_devkit_1.withCanonicalDocsUrls)("plugin-maintainability",exports.rules);exports.plugin={meta:{name:"eslint-plugin-maintainability",version:"3.2.8"},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.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){if(comment.loc&&catchStart.line-comment.loc.end.line<=2){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-maintainability/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)\.(ts|tsx|js|jsx)$/.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
+ "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-maintainability/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)\.(ts|tsx|js|jsx)$/.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.consistentFunctionScoping=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const BINDING_WRAPPERS=new Set(["VariableDeclarator","VariableDeclaration","TSAsExpression","TSSatisfiesExpression","TSNonNullExpression","TSTypeAssertion"]);exports.consistentFunctionScoping=(0,eslint_devkit_1.createRule)({name:"consistent-function-scoping",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-maintainability/docs/rules/consistent-function-scoping.md",description:"Move function definitions to the highest possible scope to improve readability and performance"},hasSuggestions:true,messages:{inconsistentFunctionScoping:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Inconsistent Function Scoping",description:"Function can be moved to higher scope as it doesn't capture outer variables",severity:"MEDIUM",fix:"Move function declaration to module scope",documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-function-scoping.md"}),moveToModuleScope:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Function Scoping Optimization",description:"Function does not use variables from its containing scope and can be moved to module level",severity:"MEDIUM",fix:'Move function outside current scope: extract `function helper() { return "value"; }` to module level before the containing function/class',documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-function-scoping.md"})},schema:[{type:"object",properties:{checkArrowFunctions:{type:"boolean",default:true}},additionalProperties:false}]},defaultOptions:[{checkArrowFunctions:true}],create(context){const[options]=context.options;const{checkArrowFunctions=true}=options||{};const scopeStack=[new Set];function enterScope(){scopeStack.push(new Set)}function exitScope(){scopeStack.pop()}function addVariableToCurrentScope(name){const currentScope=scopeStack[scopeStack.length-1];if(currentScope){currentScope.add(name)}}function addBindingToCurrentScope(target){switch(target.type){case"Identifier":addVariableToCurrentScope(target.name);break;case"ObjectPattern":for(const prop of target.properties){addBindingToCurrentScope(prop.type==="RestElement"?prop.argument:prop.value)}break;case"ArrayPattern":for(const element of target.elements){if(element)addBindingToCurrentScope(element)}break;case"AssignmentPattern":addBindingToCurrentScope(target.left);break;case"RestElement":addBindingToCurrentScope(target.argument);break;case"TSParameterProperty":addBindingToCurrentScope(target.parameter);break}}function getOuterScopeVariables(){const outerScopes=scopeStack.slice(0,-1);const outerVars=new Set;for(const scope of outerScopes){for(const varName of scope){outerVars.add(varName)}}return outerVars}function capturesThis(node){if(node.type==="ThisExpression")return true;for(const key in node){if(key==="parent")continue;const value=node[key];const children=Array.isArray(value)?value:[value];for(const child of children){if(!child||typeof child!=="object")continue;const childNode=child;if(typeof childNode.type!=="string")continue;if(childNode.type==="ClassDeclaration"||childNode.type==="ClassExpression"){if(childNode.superClass&&capturesThis(childNode.superClass)){return true}for(const member of childNode.body.body){if("computed"in member&&member.computed&&capturesThis(member.key)){return true}}continue}if(childNode.type==="FunctionDeclaration"||childNode.type==="FunctionExpression"){continue}if(capturesThis(childNode))return true}}return false}function analyzeFunction(node){let ancestor=node.parent;while(ancestor&&BINDING_WRAPPERS.has(ancestor.type)){ancestor=ancestor.parent}if(ancestor?.type==="Program"||ancestor?.type==="ExportNamedDeclaration"||ancestor?.type==="ExportDefaultDeclaration"){return}const p=node.parent;if(p?.type==="MethodDefinition"||p?.type==="PropertyDefinition"){return}if(node.type==="ArrowFunctionExpression"&&capturesThis(node)){return}if(p?.type==="CallExpression"||p?.type==="NewExpression"){return}if(p?.type==="Property"){return}const referencedVars=new Set;function collectReferences(astNode,depth=0){if(astNode.type==="Identifier"){referencedVars.add(astNode.name)}if(depth<10){for(const key in astNode){if(key==="parent")continue;const child=astNode[key];if(child&&typeof child==="object"){if(Array.isArray(child)){child.forEach(item=>{if(item&&typeof item==="object"&&"type"in item){collectReferences(item,depth+1)}})}else if("type"in child){collectReferences(child,depth+1)}}}}}if(node.body.type==="BlockStatement"){node.body.body.forEach(stmt=>collectReferences(stmt))}else{collectReferences(node.body)}node.params.forEach(param=>{collectReferences(param)});const outerVars=getOuterScopeVariables();const boundNames=new Set(context.sourceCode.getDeclaredVariables(node).map(v=>v.name));let capturesOuterVar=false;for(const ref of referencedVars){if(!boundNames.has(ref)&&outerVars.has(ref)){capturesOuterVar=true;break}}if(!capturesOuterVar){const functionName=node.type==="FunctionDeclaration"?node.id?.name:void 0;const moduleScope=scopeStack[0];if(!functionName||!moduleScope.has(functionName)){context.report({node,messageId:"inconsistentFunctionScoping",data:{functionName:functionName||"anonymous function"},suggest:[{messageId:"moveToModuleScope",fix(fixer){return fixer.insertTextBefore(node,"// TODO: Move this function to module scope - it doesn't capture outer variables\n")}}]})}}}return{Program(){enterScope()},"Program:exit"(){exitScope()},FunctionDeclaration(node){enterScope();node.params.forEach(param=>{addBindingToCurrentScope(param)});analyzeFunction(node)},"FunctionDeclaration:exit"(){exitScope()},FunctionExpression(node){enterScope();node.params.forEach(param=>{addBindingToCurrentScope(param)});analyzeFunction(node)},"FunctionExpression:exit"(){exitScope()},ArrowFunctionExpression(node){enterScope();node.params.forEach(param=>{addBindingToCurrentScope(param)});if(checkArrowFunctions){analyzeFunction(node)}},"ArrowFunctionExpression:exit"(){exitScope()},VariableDeclaration(node){node.declarations.forEach(decl=>{addBindingToCurrentScope(decl.id)})}}}});
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.consistentFunctionScoping=void 0;const eslint_devkit_1=require("@interlace/eslint-devkit");const eslint_devkit_2=require("@interlace/eslint-devkit");const BINDING_WRAPPERS=new Set(["VariableDeclarator","VariableDeclaration","TSAsExpression","TSSatisfiesExpression","TSNonNullExpression","TSTypeAssertion"]);exports.consistentFunctionScoping=(0,eslint_devkit_1.createRule)({name:"consistent-function-scoping",meta:{type:"suggestion",docs:{url:"https://github.com/ofri-peretz/eslint/blob/main/packages/eslint-plugin-maintainability/docs/rules/consistent-function-scoping.md",description:"Move function definitions to the highest possible scope to improve readability and performance"},hasSuggestions:true,messages:{inconsistentFunctionScoping:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Inconsistent Function Scoping",description:"Function can be moved to higher scope as it doesn't capture outer variables",severity:"MEDIUM",fix:"Move function declaration to module scope",documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-function-scoping.md"}),moveToModuleScope:(0,eslint_devkit_2.formatLLMMessage)({icon:eslint_devkit_2.MessageIcons.ARCHITECTURE,issueName:"Function Scoping Optimization",description:"Function does not use variables from its containing scope and can be moved to module level",severity:"MEDIUM",fix:'Move function outside current scope: extract `function helper() { return "value"; }` to module level before the containing function/class',documentationLink:"https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-function-scoping.md"})},schema:[{type:"object",properties:{checkArrowFunctions:{type:"boolean",default:true}},additionalProperties:false}]},defaultOptions:[{checkArrowFunctions:true}],create(context){const[options]=context.options;const{checkArrowFunctions=true}=options||{};const scopeStack=[new Set];function enterScope(){scopeStack.push(new Set)}function exitScope(){scopeStack.pop()}function addVariableToCurrentScope(name){const currentScope=scopeStack[scopeStack.length-1];if(currentScope){currentScope.add(name)}}function hoistFunctionDeclarations(body){for(const statement of body){if(statement.type===eslint_devkit_1.AST_NODE_TYPES.FunctionDeclaration&&statement.id){addVariableToCurrentScope(statement.id.name)}}}function addBindingToCurrentScope(target){switch(target.type){case"Identifier":addVariableToCurrentScope(target.name);break;case"ObjectPattern":for(const prop of target.properties){addBindingToCurrentScope(prop.type==="RestElement"?prop.argument:prop.value)}break;case"ArrayPattern":for(const element of target.elements){if(element)addBindingToCurrentScope(element)}break;case"AssignmentPattern":addBindingToCurrentScope(target.left);break;case"RestElement":addBindingToCurrentScope(target.argument);break;case"TSParameterProperty":addBindingToCurrentScope(target.parameter);break}}function getOuterScopeVariables(){const outerScopes=scopeStack.slice(0,-1);const outerVars=new Set;for(const scope of outerScopes){for(const varName of scope){outerVars.add(varName)}}return outerVars}function capturesThis(node){if(node.type==="ThisExpression")return true;for(const key in node){if(key==="parent")continue;const value=node[key];const children=Array.isArray(value)?value:[value];for(const child of children){if(!child||typeof child!=="object")continue;const childNode=child;if(typeof childNode.type!=="string")continue;if(childNode.type==="ClassDeclaration"||childNode.type==="ClassExpression"){if(childNode.superClass&&capturesThis(childNode.superClass)){return true}for(const member of childNode.body.body){if("computed"in member&&member.computed&&capturesThis(member.key)){return true}}continue}if(childNode.type==="FunctionDeclaration"||childNode.type==="FunctionExpression"){continue}if(capturesThis(childNode))return true}}return false}function analyzeFunction(node){let ancestor=node.parent;while(ancestor&&BINDING_WRAPPERS.has(ancestor.type)){ancestor=ancestor.parent}if(ancestor?.type==="Program"||ancestor?.type==="ExportNamedDeclaration"||ancestor?.type==="ExportDefaultDeclaration"){return}const p=node.parent;if(p?.type==="MethodDefinition"||p?.type==="PropertyDefinition"){return}if(node.type==="ArrowFunctionExpression"&&capturesThis(node)){return}if(p?.type==="CallExpression"||p?.type==="NewExpression"){return}if(p?.type==="Property"){return}const referencedVars=new Set;function collectReferences(astNode,depth=0){if(astNode.type==="Identifier"){referencedVars.add(astNode.name)}if(depth<10){for(const key in astNode){if(key==="parent")continue;const child=astNode[key];if(child&&typeof child==="object"){if(Array.isArray(child)){child.forEach(item=>{if(item&&typeof item==="object"&&"type"in item){collectReferences(item,depth+1)}})}else if("type"in child){collectReferences(child,depth+1)}}}}}if(node.body.type==="BlockStatement"){node.body.body.forEach(stmt=>collectReferences(stmt))}else{collectReferences(node.body)}node.params.forEach(param=>{collectReferences(param)});const outerVars=getOuterScopeVariables();const boundNames=new Set(context.sourceCode.getDeclaredVariables(node).map(v=>v.name));let capturesOuterVar=false;for(const ref of referencedVars){if(!boundNames.has(ref)&&outerVars.has(ref)){capturesOuterVar=true;break}}if(!capturesOuterVar){const functionName=node.type==="FunctionDeclaration"?node.id?.name:void 0;const moduleScope=scopeStack[0];if(!functionName||!moduleScope.has(functionName)){context.report({node,messageId:"inconsistentFunctionScoping",data:{functionName:functionName||"anonymous function"},suggest:[{messageId:"moveToModuleScope",fix(fixer){return fixer.insertTextBefore(node,"// TODO: Move this function to module scope - it doesn't capture outer variables\n")}}]})}}}return{Program(){enterScope()},"Program:exit"(){exitScope()},FunctionDeclaration(node){enterScope();node.params.forEach(param=>{addBindingToCurrentScope(param)});hoistFunctionDeclarations(node.body.body);analyzeFunction(node)},"FunctionDeclaration:exit"(){exitScope()},FunctionExpression(node){enterScope();node.params.forEach(param=>{addBindingToCurrentScope(param)});hoistFunctionDeclarations(node.body.body);analyzeFunction(node)},"FunctionExpression:exit"(){exitScope()},ArrowFunctionExpression(node){enterScope();node.params.forEach(param=>{addBindingToCurrentScope(param)});if(checkArrowFunctions){analyzeFunction(node)}},"ArrowFunctionExpression:exit"(){exitScope()},VariableDeclaration(node){node.declarations.forEach(decl=>{addBindingToCurrentScope(decl.id)})}}}});