eslint-plugin-maintainability 3.0.8 β†’ 3.0.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.
Files changed (31) hide show
  1. package/package.json +21 -21
  2. package/src/index.d.ts +15 -250
  3. package/src/index.js +1 -17
  4. package/src/oxlint.d.ts +17 -20
  5. package/src/oxlint.js +0 -18
  6. package/src/rules/error-handling/error-message.js +0 -17
  7. package/src/rules/error-handling/no-missing-error-context.js +1 -21
  8. package/src/rules/error-handling/no-silent-errors.js +1 -21
  9. package/src/rules/error-handling/no-unhandled-promise.js +4 -100
  10. package/src/rules/maintainability/cognitive-complexity.js +3 -46
  11. package/src/rules/maintainability/consistent-function-scoping.js +0 -45
  12. package/src/rules/maintainability/identical-functions.js +0 -43
  13. package/src/rules/maintainability/max-parameters.js +2 -17
  14. package/src/rules/maintainability/nested-complexity-hotspots.js +1 -55
  15. package/src/rules/maintainability/no-lonely-if.js +0 -15
  16. package/src/rules/maintainability/no-nested-ternary.js +0 -27
  17. package/src/rules/maintainability/no-unreadable-iife.js +1 -21
  18. package/src/types/index.js +0 -5
  19. package/CHANGELOG.md +0 -173
  20. package/src/rules/error-handling/error-message.d.ts +0 -20
  21. package/src/rules/error-handling/no-missing-error-context.d.ts +0 -26
  22. package/src/rules/error-handling/no-silent-errors.d.ts +0 -24
  23. package/src/rules/error-handling/no-unhandled-promise.d.ts +0 -41
  24. package/src/rules/maintainability/cognitive-complexity.d.ts +0 -28
  25. package/src/rules/maintainability/consistent-function-scoping.d.ts +0 -20
  26. package/src/rules/maintainability/identical-functions.d.ts +0 -33
  27. package/src/rules/maintainability/max-parameters.d.ts +0 -29
  28. package/src/rules/maintainability/nested-complexity-hotspots.d.ts +0 -31
  29. package/src/rules/maintainability/no-lonely-if.d.ts +0 -19
  30. package/src/rules/maintainability/no-nested-ternary.d.ts +0 -19
  31. package/src/rules/maintainability/no-unreadable-iife.d.ts +0 -24
@@ -1,20 +1,10 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.identicalFunctions = void 0;
9
4
  exports.buildGenericName = buildGenericName;
10
5
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
11
6
  const eslint_devkit_2 = require("@interlace/eslint-devkit");
12
7
  const eslint_devkit_3 = require("@interlace/eslint-devkit");
13
- /**
14
- * Build the generic extracted-function name suggested for a duplication group.
15
- * Shared by the unified-function template and the report data so the two can
16
- * never drift apart.
17
- */
18
8
  function buildGenericName(firstFunctionName) {
19
9
  const baseName = firstFunctionName.replace(/^(handle|process|get|set|create|update|delete)/, '');
20
10
  return `handle${baseName || 'Generic'}`;
@@ -28,7 +18,6 @@ exports.identicalFunctions = (0, eslint_devkit_2.createRule)({
28
18
  description: 'Detects duplicate function implementations with DRY refactoring suggestions',
29
19
  },
30
20
  messages: {
31
- // 🎯 Token optimization: 43% reduction (56β†’32 tokens) - DRY principle violation detected
32
21
  identicalFunctions: (0, eslint_devkit_1.formatLLMMessage)({
33
22
  icon: eslint_devkit_1.MessageIcons.DUPLICATION,
34
23
  issueName: 'Code duplication',
@@ -99,46 +88,27 @@ exports.identicalFunctions = (0, eslint_devkit_2.createRule)({
99
88
  const { minLines = 3, similarityThreshold = 0.9, ignoreTestFiles = true, } = context.options[0] || {};
100
89
  const sourceCode = context.sourceCode;
101
90
  const filename = context.filename;
102
- // Skip test files if configured
103
91
  if (ignoreTestFiles && /\.(test|spec)\.[jt]sx?$/.test(filename)) {
104
92
  return {};
105
93
  }
106
94
  const functions = [];
107
- /**
108
- * Normalize function body for comparison
109
- * Remove variable names, keep structure
110
- */
111
- // oxlint-disable-next-line consistent-function-scoping
112
95
  function normalizeBody(body) {
113
96
  return (body
114
- // Remove whitespace
115
97
  .replace(/\s+/g, ' ')
116
- // Normalize string quotes
117
98
  .replace(/["'`]/g, '"')
118
- // Normalize variable names to generic identifiers
119
99
  .replace(/\b[a-z_$][a-zA-Z0-9_$]*\b/g, 'VAR')
120
- // Remove comments
121
100
  .replace(/\/\*[\s\S]*?\*\//g, '')
122
101
  .replace(/\/\/.*/g, '')
123
102
  .trim());
124
103
  }
125
- /**
126
- * Calculate similarity between two normalized strings
127
- * Using Levenshtein distance ratio
128
- */
129
104
  function calculateSimilarity(str1, str2) {
130
105
  if (str1 === str2)
131
106
  return 1.0;
132
- // str1 !== str2 here, so the longer string is never empty.
133
107
  const longer = str1.length > str2.length ? str1 : str2;
134
108
  const shorter = str1.length > str2.length ? str2 : str1;
135
109
  const editDistance = levenshteinDistance(longer, shorter);
136
110
  return (longer.length - editDistance) / longer.length;
137
111
  }
138
- /**
139
- * Levenshtein distance algorithm
140
- */
141
- // oxlint-disable-next-line consistent-function-scoping
142
112
  function levenshteinDistance(str1, str2) {
143
113
  const matrix = [];
144
114
  for (let i = 0; i <= str2.length; i++) {
@@ -159,9 +129,6 @@ exports.identicalFunctions = (0, eslint_devkit_2.createRule)({
159
129
  }
160
130
  return matrix[str2.length][str1.length];
161
131
  }
162
- /**
163
- * Find groups of similar functions
164
- */
165
132
  function findDuplicationGroups() {
166
133
  const groups = [];
167
134
  const processed = new Set();
@@ -196,10 +163,6 @@ exports.identicalFunctions = (0, eslint_devkit_2.createRule)({
196
163
  }
197
164
  return groups;
198
165
  }
199
- /**
200
- * Suggest refactoring approach
201
- */
202
- // oxlint-disable-next-line consistent-function-scoping
203
166
  function suggestRefactoringApproach(group) {
204
167
  const funcNames = group.functions.map((f) => f.name);
205
168
  const hasRolePattern = funcNames.some((name) => /user|admin|guest|customer/i.test(name));
@@ -224,9 +187,6 @@ exports.identicalFunctions = (0, eslint_devkit_2.createRule)({
224
187
  complexity: 'simple',
225
188
  };
226
189
  }
227
- /**
228
- * Store function information
229
- */
230
190
  function storeFunctionInfo(node) {
231
191
  const body = node.body ? sourceCode.getText(node.body) : '';
232
192
  const lines = body.split('\n').length;
@@ -246,9 +206,6 @@ exports.identicalFunctions = (0, eslint_devkit_2.createRule)({
246
206
  params,
247
207
  });
248
208
  }
249
- /**
250
- * Report duplications after analyzing all functions
251
- */
252
209
  function reportDuplications() {
253
210
  const groups = findDuplicationGroups();
254
211
  groups.forEach((group) => {
@@ -1,17 +1,9 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.maxParameters = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
5
  const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
6
  const eslint_devkit_3 = require("@interlace/eslint-devkit");
12
- /**
13
- * Count function parameters
14
- */
15
7
  function countParameters(node) {
16
8
  return node.params.length;
17
9
  }
@@ -87,20 +79,13 @@ exports.maxParameters = (0, eslint_devkit_2.createRule)({
87
79
  },
88
80
  ],
89
81
  create(context, [options = {}]) {
90
- const { max = 4, ignoreConstructors = false,
91
- // ignoreOverriddenMethods = false, // Not used
92
- } = options || {};
93
- /**
94
- * Check function parameters
95
- */
82
+ const { max = 4, ignoreConstructors = false, } = options || {};
96
83
  function checkFunction(node) {
97
- // Check if it's a constructor
98
84
  if (ignoreConstructors) {
99
85
  if (node.type === 'FunctionDeclaration' &&
100
86
  node.id &&
101
87
  node.id.name &&
102
88
  /^[A-Z]/.test(node.id.name)) {
103
- // Likely a constructor
104
89
  return;
105
90
  }
106
91
  }
@@ -122,7 +107,7 @@ exports.maxParameters = (0, eslint_devkit_2.createRule)({
122
107
  suggest: [
123
108
  {
124
109
  messageId: 'useObjectParameter',
125
- fix: () => null, // Complex refactoring, cannot auto-fix
110
+ fix: () => null,
126
111
  },
127
112
  {
128
113
  messageId: 'extractToClass',
@@ -1,52 +1,8 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.nestedComplexityHotspots = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
5
  const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
- /**
12
- * Calculate nesting depth for a node
13
- * Note: Currently unused, keeping for future implementation
14
- */
15
- /*
16
- function calculateNestingDepth(
17
- node: TSESTree.Node,
18
- sourceCode: TSESLint.SourceCode
19
- ): number {
20
- let depth = 0;
21
- let current: TSESTree.Node | null = node;
22
- const maxDepth = 20;
23
-
24
- // Start from the node itself and traverse up
25
- while (current && depth < maxDepth) {
26
- const parent = (current as any).parent;
27
-
28
- if (!parent) break;
29
-
30
- // Count nested control structures
31
- if (
32
- parent.type === 'IfStatement' ||
33
- parent.type === 'ForStatement' ||
34
- parent.type === 'ForInStatement' ||
35
- parent.type === 'ForOfStatement' ||
36
- parent.type === 'WhileStatement' ||
37
- parent.type === 'DoWhileStatement' ||
38
- parent.type === 'SwitchStatement' ||
39
- parent.type === 'TryStatement'
40
- ) {
41
- depth++;
42
- }
43
-
44
- current = parent as TSESTree.Node;
45
- }
46
-
47
- return depth;
48
- }
49
- */
50
6
  exports.nestedComplexityHotspots = (0, eslint_devkit_2.createRule)({
51
7
  name: 'nested-complexity-hotspots',
52
8
  meta: {
@@ -123,22 +79,15 @@ exports.nestedComplexityHotspots = (0, eslint_devkit_2.createRule)({
123
79
  ],
124
80
  create(context, [options = {}]) {
125
81
  const { maxDepth = 4, countConditionals = true, countLoops = true, } = options || {};
126
- // const sourceCode = context.sourceCode; // Not used
127
- /**
128
- * Check control structures
129
- */
130
82
  function checkControlStructure(node) {
131
- // Count how many control structures are nested above this node
132
83
  let depth = 0;
133
84
  let current = node;
134
85
  const maxDepthCheck = 20;
135
- // Start from the node itself and traverse up
136
86
  while (current && depth < maxDepthCheck) {
137
87
  const parent = current
138
88
  .parent;
139
89
  if (!parent)
140
90
  break;
141
- // Count nested control structures above this node
142
91
  if (parent.type === 'IfStatement' ||
143
92
  parent.type === 'ForStatement' ||
144
93
  parent.type === 'ForInStatement' ||
@@ -151,15 +100,12 @@ exports.nestedComplexityHotspots = (0, eslint_devkit_2.createRule)({
151
100
  }
152
101
  current = parent;
153
102
  }
154
- // depth now represents how many control structures are nested above this node
155
- // For 5 nested ifs, the innermost if will have depth = 4 (4 ifs above it)
156
- // So we check if depth >= maxDepth (not >)
157
103
  if (depth >= maxDepth) {
158
104
  context.report({
159
105
  node,
160
106
  messageId: 'nestedComplexity',
161
107
  data: {
162
- depth: String(depth + 1), // +1 to include the current node
108
+ depth: String(depth + 1),
163
109
  max: String(maxDepth),
164
110
  },
165
111
  suggest: [
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noLonelyIf = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -47,10 +42,7 @@ exports.noLonelyIf = (0, eslint_devkit_1.createRule)({
47
42
  const { allow = [] } = options || {};
48
43
  const allowedContexts = new Set(allow);
49
44
  function isInAllowedContext() {
50
- // Check if we're in an allowed context
51
- // This is a simple implementation - could be extended for more complex cases
52
45
  for (const allowedContext of allowedContexts) {
53
- // For now, just check if the context string appears anywhere in the source
54
46
  const sourceCode = context.sourceCode;
55
47
  const sourceText = sourceCode.getText();
56
48
  if (sourceText.includes(allowedContext)) {
@@ -59,18 +51,13 @@ exports.noLonelyIf = (0, eslint_devkit_1.createRule)({
59
51
  }
60
52
  return false;
61
53
  }
62
- // oxlint-disable-next-line consistent-function-scoping
63
54
  function isLonelyIf(node) {
64
- // Check if this if statement is inside an else block (not a proper else if)
65
55
  const parent = node.parent;
66
- // If parent is a BlockStatement, check if that block is an else block
67
56
  if (parent?.type === 'BlockStatement') {
68
57
  const grandParent = parent.parent;
69
- // Check if the block is the alternate (else) of an if statement
70
58
  return (grandParent?.type === 'IfStatement' &&
71
59
  grandParent.alternate === parent);
72
60
  }
73
- // If not in a block, it's not a lonely if (it's either a proper else if or top-level)
74
61
  return false;
75
62
  }
76
63
  return {
@@ -88,7 +75,6 @@ exports.noLonelyIf = (0, eslint_devkit_1.createRule)({
88
75
  messageId: 'noLonelyIf',
89
76
  fix(fixer) {
90
77
  const sourceCode = context.sourceCode;
91
- // Find the else keyword
92
78
  let elseToken = null;
93
79
  const tokens = sourceCode.getTokensBefore(node, 10);
94
80
  for (let i = tokens.length - 1; i >= 0; i--) {
@@ -98,7 +84,6 @@ exports.noLonelyIf = (0, eslint_devkit_1.createRule)({
98
84
  }
99
85
  }
100
86
  if (elseToken) {
101
- // Remove 'else' and replace 'if' with 'else if'
102
87
  const ifToken = sourceCode.getTokenAfter(elseToken);
103
88
  if (ifToken && ifToken.value === 'if') {
104
89
  return [
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noNestedTernary = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -45,37 +40,25 @@ exports.noNestedTernary = (0, eslint_devkit_1.createRule)({
45
40
  create(context) {
46
41
  const [options] = context.options;
47
42
  const { allow = [] } = options || {};
48
- /**
49
- * Check if node is in an allowed context based on the allow option.
50
- * Supported contexts:
51
- * - 'jsx': Allow nested ternaries in JSX expressions
52
- * - 'variable': Allow nested ternaries in variable declarations
53
- * - 'return': Allow nested ternaries in return statements
54
- * - 'argument': Allow nested ternaries in function arguments
55
- */
56
43
  function isInAllowedContext(node) {
57
44
  if (allow.length === 0) {
58
45
  return false;
59
46
  }
60
47
  let current = node.parent;
61
48
  while (current) {
62
- // Check for JSX context
63
49
  if (allow.includes('jsx') &&
64
50
  (current.type === 'JSXExpressionContainer' ||
65
51
  current.type === 'JSXElement' ||
66
52
  current.type === 'JSXFragment')) {
67
53
  return true;
68
54
  }
69
- // Check for variable declaration context
70
55
  if (allow.includes('variable') &&
71
56
  current.type === 'VariableDeclarator') {
72
57
  return true;
73
58
  }
74
- // Check for return statement context
75
59
  if (allow.includes('return') && current.type === 'ReturnStatement') {
76
60
  return true;
77
61
  }
78
- // Check for function argument context
79
62
  if (allow.includes('argument') && current.type === 'CallExpression') {
80
63
  return true;
81
64
  }
@@ -84,12 +67,10 @@ exports.noNestedTernary = (0, eslint_devkit_1.createRule)({
84
67
  return false;
85
68
  }
86
69
  function hasNestedTernary(node) {
87
- // Check if the consequent or alternate contains another ternary
88
70
  function containsTernary(expr) {
89
71
  if (expr.type === 'ConditionalExpression') {
90
72
  return true;
91
73
  }
92
- // For other expression types, check their child expressions
93
74
  switch (expr.type) {
94
75
  case 'ArrayExpression':
95
76
  return expr.elements.some((element) => element &&
@@ -115,21 +96,17 @@ exports.noNestedTernary = (0, eslint_devkit_1.createRule)({
115
96
  return containsTernary(expr.argument);
116
97
  case 'AssignmentExpression':
117
98
  return containsTernary(expr.right);
118
- // For literals and identifiers, no nested expressions
119
99
  case 'Literal':
120
100
  case 'Identifier':
121
101
  case 'ThisExpression':
122
102
  case 'Super':
123
103
  case 'MetaProperty':
124
104
  return false;
125
- // For template literals, check expressions
126
105
  case 'TemplateLiteral':
127
106
  return expr.expressions.some((exp) => containsTernary(exp));
128
- // For tagged templates, check tag and expressions
129
107
  case 'TaggedTemplateExpression':
130
108
  return (containsTernary(expr.tag) ||
131
109
  expr.quasi.expressions.some((exp) => containsTernary(exp)));
132
- // Default: assume no nested expressions for unknown types
133
110
  default:
134
111
  return false;
135
112
  }
@@ -150,10 +127,6 @@ exports.noNestedTernary = (0, eslint_devkit_1.createRule)({
150
127
  {
151
128
  messageId: 'noNestedTernary',
152
129
  fix() {
153
- // This is a complex fix that would require:
154
- // 1. Extracting the nested ternary to a variable
155
- // 2. Replacing the nested part
156
- // For now, just provide the suggestion
157
130
  return null;
158
131
  },
159
132
  },
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
8
3
  exports.noUnreadableIife = void 0;
9
4
  const eslint_devkit_1 = require("@interlace/eslint-devkit");
@@ -78,16 +73,11 @@ exports.noUnreadableIife = (0, eslint_devkit_1.createRule)({
78
73
  create(context) {
79
74
  const [options] = context.options;
80
75
  const { maxStatements = 3, maxDepth = 2, allowReturningIIFE = true } = options || {};
81
- // oxlint-disable-next-line consistent-function-scoping
82
76
  function getIifeFunctionNode(node) {
83
- // Check if this is a call expression with a function expression or arrow function
84
- // Note: In typescript-eslint AST, parentheses don't create a separate node type,
85
- // so (function(){})() will have callee.type === 'FunctionExpression' directly
86
77
  if (node.callee.type === 'FunctionExpression' ||
87
78
  node.callee.type === 'ArrowFunctionExpression') {
88
79
  return node.callee;
89
80
  }
90
- // Check for unary operator IIFEs like (!function(){})(), (+function(){})(), (void function(){})()
91
81
  if (node.callee.type === 'UnaryExpression' &&
92
82
  (node.callee.argument.type === 'FunctionExpression' ||
93
83
  node.callee.argument.type === 'ArrowFunctionExpression')) {
@@ -95,22 +85,18 @@ exports.noUnreadableIife = (0, eslint_devkit_1.createRule)({
95
85
  }
96
86
  return null;
97
87
  }
98
- // oxlint-disable-next-line consistent-function-scoping
99
88
  function countStatements(body) {
100
89
  if (body.type === 'BlockStatement') {
101
90
  return body.body.length;
102
91
  }
103
- return 1; // Single expression
92
+ return 1;
104
93
  }
105
94
  function calculateDepth(node) {
106
- // AST-based nesting depth calculation β€” no regex needed
107
95
  const CONTROL_FLOW_TYPES = new Set([
108
96
  'IfStatement', 'ForStatement', 'ForInStatement', 'ForOfStatement',
109
97
  'WhileStatement', 'DoWhileStatement', 'SwitchStatement', 'TryStatement',
110
98
  ]);
111
- // Non-AST keys that should never be traversed
112
99
  const SKIP_KEYS = new Set(['parent', 'loc', 'range', 'tokens', 'comments', 'leadingComments', 'trailingComments']);
113
- // oxlint-disable-next-line consistent-function-scoping
114
100
  function isASTNode(value) {
115
101
  return !!value && typeof value === 'object' && 'type' in value && 'loc' in value;
116
102
  }
@@ -138,11 +124,9 @@ exports.noUnreadableIife = (0, eslint_devkit_1.createRule)({
138
124
  if (body.type !== 'BlockStatement') {
139
125
  return false;
140
126
  }
141
- // Check for multiple statements
142
127
  if (body.body.length > maxStatements) {
143
128
  return true;
144
129
  }
145
- // Check for complex constructs
146
130
  for (const statement of body.body) {
147
131
  if (statement.type === 'IfStatement' ||
148
132
  statement.type === 'ForStatement' ||
@@ -151,7 +135,6 @@ exports.noUnreadableIife = (0, eslint_devkit_1.createRule)({
151
135
  statement.type === 'TryStatement') {
152
136
  return true;
153
137
  }
154
- // Check for nested function declarations
155
138
  if (statement.type === 'FunctionDeclaration' ||
156
139
  statement.type === 'VariableDeclaration' &&
157
140
  statement.declarations.some((decl) => decl.init?.type === 'FunctionExpression' ||
@@ -168,11 +151,9 @@ exports.noUnreadableIife = (0, eslint_devkit_1.createRule)({
168
151
  const hasComplex = hasComplexLogic(body);
169
152
  const returnsValue = body.type === 'BlockStatement' &&
170
153
  body.body.some((stmt) => stmt.type === 'ReturnStatement');
171
- // Skip IIFEs that return values if allowed
172
154
  if (allowReturningIIFE && returnsValue && !hasComplex) {
173
155
  return;
174
156
  }
175
- // Check various conditions for unreadable IIFE
176
157
  const issues = [];
177
158
  if (statementCount > maxStatements) {
178
159
  issues.push(`too many statements (${statementCount} > ${maxStatements})`);
@@ -200,7 +181,6 @@ exports.noUnreadableIife = (0, eslint_devkit_1.createRule)({
200
181
  {
201
182
  messageId: 'suggestNamedFunction',
202
183
  fix(fixer) {
203
- // Complex fix - would need to generate a function name and hoist it
204
184
  return fixer.insertTextBefore(node, '// TODO: Extract complex IIFE to named function\n');
205
185
  },
206
186
  },
@@ -1,7 +1,2 @@
1
1
  "use strict";
2
- /**
3
- * Copyright (c) 2025 Ofri Peretz
4
- * Licensed under the MIT License. Use of this source code is governed by the
5
- * MIT license that can be found in the LICENSE file.
6
- */
7
2
  Object.defineProperty(exports, "__esModule", { value: true });
package/CHANGELOG.md DELETED
@@ -1,173 +0,0 @@
1
- ## [3.0.3] - 2026-02-08
2
-
3
- ## 3.0.8
4
-
5
- ### Patch Changes
6
-
7
- - [#269](https://github.com/ofri-peretz/eslint/pull/269) [`7028fe2`](https://github.com/ofri-peretz/eslint/commit/7028fe2668a42266d831014184dcef70e73101ad) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - docs: dual-logo README header (Interlace mark + ESLint mark side by side) and closing Interlace footer β€” refreshes the README rendered on npmjs.com. No runtime changes.
8
-
9
- - Updated dependencies [[`7028fe2`](https://github.com/ofri-peretz/eslint/commit/7028fe2668a42266d831014184dcef70e73101ad)]:
10
- - @interlace/eslint-devkit@1.4.2
11
-
12
- ## 3.0.7
13
-
14
- ### Patch Changes
15
-
16
- - [#252](https://github.com/ofri-peretz/eslint/pull/252) [`d67e395`](https://github.com/ofri-peretz/eslint/commit/d67e3953c2748ad36e6aebe0f24b1d04e518b4d0) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - Fix Codecov badge showing "unknown" β€” switch from flag to component URL format
17
-
18
- ## 3.0.6
19
-
20
- ### Patch Changes
21
-
22
- - [#225](https://github.com/ofri-peretz/eslint/pull/225) [`34ff5a8`](https://github.com/ofri-peretz/eslint/commit/34ff5a8e6f5126c5d1c0a524759e0af2b5476b46) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - CI-only: pin all coverage thresholds at 100% (integration target, merges last).
23
-
24
- ## 3.0.5
25
-
26
- ### Patch Changes
27
-
28
- - [#200](https://github.com/ofri-peretz/eslint/pull/200) [`02e0baf`](https://github.com/ofri-peretz/eslint/commit/02e0baf7a4e8ba83e8b2ec2b82169f733e4f4d87) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - fix: republish `recommended` preset with the correct plugin namespace
29
-
30
- The published builds of `eslint-plugin-maintainability` and
31
- `eslint-plugin-operability` shipped a `recommended` config whose plugin KEY
32
- (`@interlace/maintainability`) did not match its rule PREFIX
33
- (`@interlace/maintainability/maintainability/…` β€” doubled). ESLint cannot
34
- resolve that, so spreading `...configs.recommended` throws
35
- "could not find plugin" the moment a consumer lints a file β€” under both
36
- ESLint 9 and 10.
37
-
38
- The source was corrected in the 2026-05-16 namespace cleanup (alongside
39
- `react-features`, which has since been republished via other changesets), but
40
- these two plugins were never bumped β€” so npm still serves the broken builds and
41
- they are the only two doubled-namespace plugins still unfixed downstream. This
42
- republishes them from the corrected source.
43
-
44
- Regression lock: `packages/eslint-config-interlace/src/ecosystem-integrity.test.ts`
45
- loads every plugin's every config preset into a real ESLint instance and fails
46
- if any rule→plugin reference cannot be resolved. Run it against the built
47
- `dist/` in the release pipeline (pre-publish) to also catch a stale-artifact
48
- publish β€” the failure mode that let these two ship broken.
49
-
50
- ## 3.0.4
51
-
52
- ### Patch Changes
53
-
54
- - [#197](https://github.com/ofri-peretz/eslint/pull/197) [`ecb8491`](https://github.com/ofri-peretz/eslint/commit/ecb849121833bf63b00256fa837f329bb721fbac) Thanks [@ofri-peretz](https://github.com/ofri-peretz)! - fix: republish `recommended` with correctly-namespaced, unscoped rule ids
55
-
56
- `eslint-plugin-maintainability@3.0.3` and `eslint-plugin-operability@3.0.5`
57
- shipped a `recommended` preset whose rule ids carried a doubled, scoped plugin
58
- segment (`@interlace/maintainability/maintainability/cognitive-complexity`)
59
- that no registered plugin key matched. Enabling the preset alongside any other
60
- config made ESLint throw at load:
61
-
62
- ```text
63
- Could not find plugin "@interlace/maintainability/maintainability".
64
- ```
65
-
66
- (and the equivalent `@interlace/operability/operability` for operability.)
67
-
68
- The source was already corrected to the bare, unscoped form
69
- (`maintainability/cognitive-complexity` under a `maintainability` plugin key)
70
- but was never republished, so npm still served the broken build. This release
71
- ships the corrected build. `plugin.meta.name` is also fixed to the unscoped
72
- `eslint-plugin-maintainability` (was `@interlace/eslint-plugin-maintainability`,
73
- which drifted from the package name and every other plugin).
74
-
75
- Each plugin is configured on its own β€” there is no unified config. No rule
76
- behaviour changes.
77
-
78
- New regression locks in each plugin's `index.test.ts` reproduce ESLint's rule-id
79
- resolution, pin the plugin name and key as unscoped, and load each `recommended`
80
- preset in a real ESLint instance β€” failing closed if a scoped or doubly
81
- namespaced config could ship again.
82
-
83
- ### Bug Fixes
84
-
85
- - align codecov component IDs with full package names ([2831b968](https://github.com/ofri-peretz/eslint/commit/2831b968))
86
-
87
- ### Documentation
88
-
89
- - fix changelog header format across all packages ([c3a15082](https://github.com/ofri-peretz/eslint/commit/c3a15082))
90
-
91
- ### ❀️ Thank You
92
-
93
- - Ofri Peretz
94
-
95
- ## [3.0.2] - 2026-02-06
96
-
97
- ### Bug Fixes
98
-
99
- - align codecov component names and update docs components ([0a59a86c](https://github.com/ofri-peretz/eslint/commit/0a59a86c))
100
-
101
- ### ❀️ Thank You
102
-
103
- - Ofri Peretz
104
-
105
- ## [3.0.1] - 2026-02-02
106
-
107
- This was a version bump only for eslint-plugin-maintainability to align it with other projects, there were no code changes.
108
-
109
- # Changelog
110
-
111
- All notable changes to `@interlace/eslint-plugin-maintainability` will be documented in this file.
112
-
113
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
114
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
115
-
116
- ### Documentation
117
-
118
- - πŸ“˜ Launched new documentation site: [eslint.interlace.tools](https://eslint.interlace.tools/)
119
- - πŸ“ Achieved 100% documentation parity (both .md and .mdx files)
120
-
121
- ## [3.0.0] - 2026-02-02
122
-
123
- This was a version bump only for eslint-plugin-maintainability to align it with other projects, there were no code changes.
124
-
125
- ## [1.0.0] - 2026-01-26
126
-
127
- ### Added
128
-
129
- - Initial stable release with 12 maintainability rules
130
- - LLM-optimized error messages for AI-assisted development
131
- - 100% test coverage across all rules
132
- - ESLint 9 flat config support
133
- - TypeScript type definitions for all rule options
134
-
135
- ### Rules
136
-
137
- #### Complexity Rules
138
-
139
- | Rule | Description |
140
- | :-------------------------- | :------------------------------- |
141
- | `max-cognitive-complexity` | Limit cognitive complexity score |
142
- | `max-cyclomatic-complexity` | Limit cyclomatic complexity |
143
- | `max-depth` | Limit nesting depth |
144
- | `max-lines` | Limit file length |
145
- | `max-lines-per-function` | Limit function length |
146
- | `max-params` | Limit function parameters |
147
-
148
- #### Code Smell Rules
149
-
150
- | Rule | Description |
151
- | :------------------------- | :---------------------------------- |
152
- | `no-magic-numbers` | Disallow magic numbers |
153
- | `no-nested-ternary` | Disallow nested ternary expressions |
154
- | `no-deep-callback-nesting` | Disallow deeply nested callbacks |
155
- | `no-long-parameter-list` | Disallow long parameter lists |
156
-
157
- #### Clean Code Rules
158
-
159
- | Rule | Description |
160
- | :-------------------- | :------------------------------------------ |
161
- | `prefer-early-return` | Prefer early returns over nested conditions |
162
- | `no-duplicate-logic` | Detect duplicated logic blocks (DRY) |
163
-
164
- ### Presets
165
-
166
- - `recommended` - Balanced maintainability thresholds
167
-
168
- ### SOLID Principles Mapping
169
-
170
- Rules are annotated with SOLID principle alignment:
171
-
172
- - Single Responsibility: `max-lines-per-function`, `max-lines`
173
- - Open/Closed: `prefer-early-return`