@blumintinc/eslint-plugin-blumint 1.15.0 → 1.16.1

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 (45) hide show
  1. package/README.md +28 -5
  2. package/lib/index.js +7 -1
  3. package/lib/rules/consistent-callback-naming.js +26 -0
  4. package/lib/rules/dynamic-https-errors.js +5 -26
  5. package/lib/rules/enforce-assert-safe-object-key.js +29 -0
  6. package/lib/rules/enforce-boolean-naming-prefixes.d.ts +1 -0
  7. package/lib/rules/enforce-boolean-naming-prefixes.js +86 -119
  8. package/lib/rules/enforce-dynamic-imports.d.ts +5 -1
  9. package/lib/rules/enforce-dynamic-imports.js +90 -19
  10. package/lib/rules/enforce-memoize-async.js +1 -4
  11. package/lib/rules/enforce-mui-rounded-icons.js +42 -1
  12. package/lib/rules/enforce-verb-noun-naming.js +3 -0
  13. package/lib/rules/global-const-style.js +9 -0
  14. package/lib/rules/logical-top-to-bottom-grouping.js +80 -2
  15. package/lib/rules/memo-compare-deeply-complex-props.js +167 -3
  16. package/lib/rules/memo-nested-react-components.js +143 -8
  17. package/lib/rules/no-array-length-in-deps.js +74 -3
  18. package/lib/rules/no-circular-references.js +145 -482
  19. package/lib/rules/no-compositing-layer-props.js +31 -0
  20. package/lib/rules/no-entire-object-hook-deps.js +132 -97
  21. package/lib/rules/no-explicit-return-type.js +6 -0
  22. package/lib/rules/no-hungarian.js +119 -24
  23. package/lib/rules/no-margin-properties.js +7 -38
  24. package/lib/rules/no-unnecessary-verb-suffix.js +79 -0
  25. package/lib/rules/no-unused-props.js +215 -37
  26. package/lib/rules/no-useless-fragment.js +10 -2
  27. package/lib/rules/parallelize-async-operations.js +1 -3
  28. package/lib/rules/prefer-type-alias-over-typeof-constant.js +73 -11
  29. package/lib/rules/prefer-use-deep-compare-memo.js +8 -14
  30. package/lib/rules/react-memoize-literals.js +87 -1
  31. package/lib/rules/require-memo.js +8 -0
  32. package/lib/rules/require-migration-script-metadata.d.ts +9 -0
  33. package/lib/rules/require-migration-script-metadata.js +206 -0
  34. package/lib/rules/warn-https-error-message-user-friendly.d.ts +1 -0
  35. package/lib/rules/warn-https-error-message-user-friendly.js +239 -0
  36. package/lib/utils/ASTHelpers.d.ts +15 -0
  37. package/lib/utils/ASTHelpers.js +48 -0
  38. package/package.json +7 -6
  39. package/release-manifest.json +196 -0
  40. package/lib/rules/prefer-memoized-props.d.ts +0 -3
  41. package/lib/rules/prefer-memoized-props.js +0 -445
  42. package/lib/rules/prefer-nullish-coalescing-override.d.ts +0 -7
  43. package/lib/rules/prefer-nullish-coalescing-override.js +0 -188
  44. package/lib/rules/require-usememo-object-literals.d.ts +0 -4
  45. package/lib/rules/require-usememo-object-literals.js +0 -64
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.warnHttpsErrorMessageUserFriendly = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ const ASTHelpers_1 = require("../utils/ASTHelpers");
7
+ exports.warnHttpsErrorMessageUserFriendly = (0, createRule_1.createRule)({
8
+ name: 'warn-https-error-message-user-friendly',
9
+ meta: {
10
+ type: 'suggestion',
11
+ docs: {
12
+ description: 'Warn when messageUserFriendly is used in HttpsError or toHttpsError to ensure it is truly a user-caused error.',
13
+ recommended: 'warn',
14
+ },
15
+ fixable: undefined,
16
+ schema: [],
17
+ messages: {
18
+ warnHttpsErrorMessageUserFriendly: "What's wrong: '{{propertyName}}' is set on an HttpsError/toHttpsError options object. " +
19
+ 'Why it matters: this marks the error as user-caused and can suppress automated error monitoring and QA issue creation for real defects. ' +
20
+ "How to fix: remove '{{propertyName}}' unless the error is genuinely user-caused; if it is, keep it and add // eslint-disable-next-line to document the exception.",
21
+ },
22
+ },
23
+ defaultOptions: [],
24
+ create(context) {
25
+ /**
26
+ * Extracts problematic property definitions to provide precise reporting locations,
27
+ * even when properties are deeply nested or mixed via spread elements.
28
+ */
29
+ const findMessageUserFriendlyProperties = (node, visited = new Set()) => {
30
+ const matches = [];
31
+ for (const prop of node.properties) {
32
+ if (prop.type === utils_1.AST_NODE_TYPES.Property) {
33
+ if ((!prop.computed &&
34
+ ((prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
35
+ prop.key.name === 'messageUserFriendly') ||
36
+ (prop.key.type === utils_1.AST_NODE_TYPES.Literal &&
37
+ prop.key.value === 'messageUserFriendly'))) ||
38
+ (prop.computed &&
39
+ prop.key.type === utils_1.AST_NODE_TYPES.Literal &&
40
+ prop.key.value === 'messageUserFriendly')) {
41
+ matches.push(prop);
42
+ }
43
+ }
44
+ else if (prop.type === utils_1.AST_NODE_TYPES.SpreadElement) {
45
+ if (checkNode(prop.argument, visited)) {
46
+ matches.push(prop);
47
+ }
48
+ }
49
+ }
50
+ return matches;
51
+ };
52
+ /**
53
+ * Analyzes variable assignments to prevent rule bypass via intermediate identifiers,
54
+ * ensuring that 'messageUserFriendly' cannot be hidden behind a variable name.
55
+ */
56
+ const traceVariable = (identifier, visited = new Set()) => {
57
+ if (visited.has(identifier.name))
58
+ return false;
59
+ visited.add(identifier.name);
60
+ const scope = ASTHelpers_1.ASTHelpers.getScope(context, identifier);
61
+ const variable = ASTHelpers_1.ASTHelpers.findVariableInScope(scope, identifier.name);
62
+ if (!variable)
63
+ return false;
64
+ for (const def of variable.defs) {
65
+ if (def.type === 'Variable' && def.node.init) {
66
+ if (checkNode(def.node.init, visited)) {
67
+ return true;
68
+ }
69
+ }
70
+ else if (def.type === 'FunctionName' &&
71
+ def.node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
72
+ if (traceFunctionReturn(def.node, visited)) {
73
+ return true;
74
+ }
75
+ }
76
+ }
77
+ return false;
78
+ };
79
+ /**
80
+ * Inspects function bodies to detect cases where the options object is generated
81
+ * dynamically, closing a common bypass pattern where factories or helpers are used.
82
+ */
83
+ const traceFunctionReturn = (node, visited = new Set()) => {
84
+ if (!node.body)
85
+ return false;
86
+ const sourceCode = context.sourceCode ?? context.getSourceCode();
87
+ const walk = (current) => {
88
+ if (current !== node &&
89
+ (current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
90
+ current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
91
+ current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression)) {
92
+ return false;
93
+ }
94
+ if (current.type === utils_1.AST_NODE_TYPES.ReturnStatement &&
95
+ current.argument) {
96
+ return checkNode(current.argument, visited);
97
+ }
98
+ const keys = sourceCode.visitorKeys[current.type] ?? [];
99
+ return keys.some((key) => {
100
+ const value = current[key];
101
+ if (Array.isArray(value)) {
102
+ return value.some((child) => child &&
103
+ typeof child === 'object' &&
104
+ 'type' in child &&
105
+ walk(child));
106
+ }
107
+ if (value && typeof value === 'object' && 'type' in value) {
108
+ return walk(value);
109
+ }
110
+ return false;
111
+ });
112
+ };
113
+ if (node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
114
+ return walk(node.body);
115
+ }
116
+ else {
117
+ // Arrow function with expression body
118
+ return checkNode(node.body, visited);
119
+ }
120
+ };
121
+ /**
122
+ * Recursively evaluates diverse AST structures (logical, conditional, calls) to ensure
123
+ * that the presence of 'messageUserFriendly' is detected regardless of expression complexity.
124
+ *
125
+ * Note: This implementation intentionally covers only Identifier callees for simplicity,
126
+ * as MemberExpression callees (e.g., optionsFactory.build()) are not currently used in
127
+ * the codebase for HttpsError options. Handling for MemberExpression callees can be
128
+ * added as a future enhancement if such patterns emerge.
129
+ */
130
+ const checkNode = (node, visited = new Set()) => {
131
+ const unwrappedNode = ASTHelpers_1.ASTHelpers.unwrapTSAssertions(node);
132
+ if (unwrappedNode.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
133
+ return (findMessageUserFriendlyProperties(unwrappedNode, visited).length > 0);
134
+ }
135
+ else if (unwrappedNode.type === utils_1.AST_NODE_TYPES.Identifier) {
136
+ return traceVariable(unwrappedNode, visited);
137
+ }
138
+ else if (unwrappedNode.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
139
+ return (checkNode(unwrappedNode.left, visited) ||
140
+ checkNode(unwrappedNode.right, visited));
141
+ }
142
+ else if (unwrappedNode.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
143
+ return (checkNode(unwrappedNode.consequent, visited) ||
144
+ checkNode(unwrappedNode.alternate, visited));
145
+ }
146
+ else if (unwrappedNode.type === utils_1.AST_NODE_TYPES.CallExpression ||
147
+ unwrappedNode.type === utils_1.AST_NODE_TYPES.NewExpression) {
148
+ if (unwrappedNode.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
149
+ const calleeName = unwrappedNode.callee.name;
150
+ if (visited.has(calleeName))
151
+ return false;
152
+ visited.add(calleeName);
153
+ const scope = ASTHelpers_1.ASTHelpers.getScope(context, unwrappedNode.callee);
154
+ const variable = ASTHelpers_1.ASTHelpers.findVariableInScope(scope, calleeName);
155
+ if (variable) {
156
+ for (const def of variable.defs) {
157
+ if (def.type === 'FunctionName' &&
158
+ def.node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration) {
159
+ if (traceFunctionReturn(def.node, visited)) {
160
+ return true;
161
+ }
162
+ }
163
+ else if (def.type === 'Variable' && def.node.init) {
164
+ if (def.node.init.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
165
+ def.node.init.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
166
+ if (traceFunctionReturn(def.node.init, visited)) {
167
+ return true;
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ }
174
+ }
175
+ return false;
176
+ };
177
+ const validateOptions = (node) => {
178
+ const unwrappedNode = ASTHelpers_1.ASTHelpers.unwrapTSAssertions(node);
179
+ if (unwrappedNode.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
180
+ findMessageUserFriendlyProperties(unwrappedNode).forEach((prop) => {
181
+ context.report({
182
+ node: prop.type === utils_1.AST_NODE_TYPES.Property ? prop.key : prop,
183
+ messageId: 'warnHttpsErrorMessageUserFriendly',
184
+ data: { propertyName: 'messageUserFriendly' },
185
+ });
186
+ });
187
+ }
188
+ else if (unwrappedNode.type === utils_1.AST_NODE_TYPES.Identifier) {
189
+ if (traceVariable(unwrappedNode)) {
190
+ context.report({
191
+ node: unwrappedNode,
192
+ messageId: 'warnHttpsErrorMessageUserFriendly',
193
+ data: { propertyName: 'messageUserFriendly' },
194
+ });
195
+ }
196
+ }
197
+ else if (unwrappedNode.type === utils_1.AST_NODE_TYPES.CallExpression ||
198
+ unwrappedNode.type === utils_1.AST_NODE_TYPES.NewExpression) {
199
+ if (checkNode(unwrappedNode)) {
200
+ context.report({
201
+ node,
202
+ messageId: 'warnHttpsErrorMessageUserFriendly',
203
+ data: { propertyName: 'messageUserFriendly' },
204
+ });
205
+ }
206
+ }
207
+ else if (unwrappedNode.type === utils_1.AST_NODE_TYPES.LogicalExpression) {
208
+ validateOptions(unwrappedNode.left);
209
+ validateOptions(unwrappedNode.right);
210
+ }
211
+ else if (unwrappedNode.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
212
+ validateOptions(unwrappedNode.consequent);
213
+ validateOptions(unwrappedNode.alternate);
214
+ }
215
+ };
216
+ return {
217
+ NewExpression(node) {
218
+ if (ASTHelpers_1.ASTHelpers.isHttpsErrorCall(node.callee)) {
219
+ if (node.arguments.length > 0) {
220
+ validateOptions(node.arguments[0]);
221
+ }
222
+ }
223
+ },
224
+ CallExpression(node) {
225
+ if (ASTHelpers_1.ASTHelpers.isHttpsErrorCall(node.callee)) {
226
+ if (node.arguments.length > 0) {
227
+ validateOptions(node.arguments[0]);
228
+ }
229
+ }
230
+ else if (ASTHelpers_1.ASTHelpers.isToHttpsErrorCall(node.callee)) {
231
+ if (node.arguments.length > 1) {
232
+ validateOptions(node.arguments[1]);
233
+ }
234
+ }
235
+ },
236
+ };
237
+ },
238
+ });
239
+ //# sourceMappingURL=warn-https-error-message-user-friendly.js.map
@@ -46,6 +46,21 @@ export declare class ASTHelpers {
46
46
  * Compatibility wrapper for getting declared variables across ESLint versions.
47
47
  */
48
48
  static getDeclaredVariables(context: Readonly<TSESLint.RuleContext<string, readonly unknown[]>>, node: TSESTree.Node): readonly TSESLint.Scope.Variable[];
49
+ /**
50
+ * Checks if a call expression or new expression is a call to HttpsError.
51
+ * Handles both 'HttpsError' and 'https.HttpsError'.
52
+ */
53
+ static isHttpsErrorCall(callee: TSESTree.LeftHandSideExpression): boolean;
54
+ /**
55
+ * Checks if a call expression is a call to toHttpsError.
56
+ * Handles both 'toHttpsError' and 'https.toHttpsError'.
57
+ */
58
+ static isToHttpsErrorCall(callee: TSESTree.LeftHandSideExpression): boolean;
59
+ /**
60
+ * Unwraps TypeScript-specific nodes (assertions, non-null, satisfies) and
61
+ * parenthesized expressions to get to the underlying expression.
62
+ */
63
+ static unwrapTSAssertions(node: TSESTree.Node): TSESTree.Node;
49
64
  /**
50
65
  * Helper to get ancestors of a node in a way that is compatible with both ESLint v8 and v9.
51
66
  * In ESLint v9, context.getAncestors() is deprecated and moved to context.sourceCode.getAncestors(node).
@@ -613,6 +613,54 @@ class ASTHelpers {
613
613
  }
614
614
  return fn(node);
615
615
  }
616
+ /**
617
+ * Checks if a call expression or new expression is a call to HttpsError.
618
+ * Handles both 'HttpsError' and 'https.HttpsError'.
619
+ */
620
+ static isHttpsErrorCall(callee) {
621
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
622
+ return (callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
623
+ callee.object.name === 'https' &&
624
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
625
+ callee.property.name === 'HttpsError');
626
+ }
627
+ else if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
628
+ return callee.name === 'HttpsError';
629
+ }
630
+ return false;
631
+ }
632
+ /**
633
+ * Checks if a call expression is a call to toHttpsError.
634
+ * Handles both 'toHttpsError' and 'https.toHttpsError'.
635
+ */
636
+ static isToHttpsErrorCall(callee) {
637
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
638
+ return (callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
639
+ callee.object.name === 'https' &&
640
+ callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
641
+ callee.property.name === 'toHttpsError');
642
+ }
643
+ else if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
644
+ return callee.name === 'toHttpsError';
645
+ }
646
+ return false;
647
+ }
648
+ /**
649
+ * Unwraps TypeScript-specific nodes (assertions, non-null, satisfies) and
650
+ * parenthesized expressions to get to the underlying expression.
651
+ */
652
+ static unwrapTSAssertions(node) {
653
+ let inner = node;
654
+ while (inner &&
655
+ (inner.type === utils_1.AST_NODE_TYPES.TSAsExpression ||
656
+ inner.type === utils_1.AST_NODE_TYPES.TSSatisfiesExpression ||
657
+ inner.type === utils_1.AST_NODE_TYPES.TSNonNullExpression ||
658
+ inner.type === utils_1.AST_NODE_TYPES.TSTypeAssertion ||
659
+ inner.type === 'ParenthesizedExpression')) {
660
+ inner = inner.expression;
661
+ }
662
+ return inner;
663
+ }
616
664
  /**
617
665
  * Helper to get ancestors of a node in a way that is compatible with both ESLint v8 and v9.
618
666
  * In ESLint v9, context.getAncestors() is deprecated and moved to context.sourceCode.getAncestors(node).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blumintinc/eslint-plugin-blumint",
3
- "version": "1.15.0",
3
+ "version": "1.16.1",
4
4
  "description": "Custom eslint rules for use within BluMint",
5
5
  "author": {
6
6
  "name": "Brodie McGuire",
@@ -27,7 +27,7 @@
27
27
  "private": false,
28
28
  "scripts": {
29
29
  "lint": "npm-run-all \"lint:*\"",
30
- "lint:eslint-docs": "npm-run-all \"update:eslint-docs -- --check\"",
30
+ "lint:eslint-docs": "eslint-doc-generator --check",
31
31
  "lint:js": "eslint ./src",
32
32
  "lint:fix": "tsc && eslint \"./src/**/*\" --quiet --fix",
33
33
  "test": "jest --passWithNoTests --reporters=default --reporters=jest-junit",
@@ -36,6 +36,7 @@
36
36
  "address-merge-conflicts": "tsx ./.github/scripts/address-merge-conflicts.ts",
37
37
  "check-merge-conflicts": "bash ./scripts/check-merge-conflicts.sh",
38
38
  "merge-review": "tsx ./.github/scripts/merge-review.ts",
39
+ "pr-autopilot": "tsx ./.github/scripts/pr-autopilot.ts",
39
40
  "fetch-unresolved-comments": "./scripts/pr-check-comments.sh",
40
41
  "fetch-unresolved-bot-comments": "./scripts/pr-check-bot-comments.sh",
41
42
  "resolve-comments": "./scripts/pr-resolve-comments.sh",
@@ -69,22 +70,22 @@
69
70
  "@semantic-release/release-notes-generator": "14.1.0",
70
71
  "@types/eslint": "8.37.0",
71
72
  "@types/jest": "29.5.14",
72
- "@types/node": "18.7.13",
73
+ "@types/node": "22.20.0",
73
74
  "@typescript-eslint/eslint-plugin": "5.34.0",
74
75
  "@typescript-eslint/parser": "5.48.0",
75
76
  "@typescript-eslint/utils": "5.59.6",
76
- "chai": "4.3.6",
77
+ "chai": "4.5.0",
77
78
  "chai-as-promised": "7.1.1",
78
79
  "cross-env": "7.0.3",
79
80
  "cz-conventional-changelog": "3.3.0",
80
81
  "del-cli": "4.0.1",
81
82
  "dotenv-cli": "5.0.0",
82
83
  "eslint": "8.57.1",
83
- "eslint-config-prettier": "8.5.0",
84
+ "eslint-config-prettier": "9.1.2",
84
85
  "eslint-doc-generator": "2.2.0",
85
86
  "eslint-import-resolver-typescript": "3.5.5",
86
87
  "eslint-plugin-eslint-plugin": "5.0.0",
87
- "eslint-plugin-import": "2.26.0",
88
+ "eslint-plugin-import": "2.32.0",
88
89
  "eslint-plugin-jsdoc": "44.0.0",
89
90
  "eslint-plugin-node": "11.1.0",
90
91
  "eslint-plugin-prettier": "4.2.1",
@@ -0,0 +1,196 @@
1
+ [
2
+ {
3
+ "version": "1.16.1",
4
+ "date": "2026-06-30T15:58:17.558Z",
5
+ "rules": [
6
+ {
7
+ "name": "enforce-assert-safe-object-key",
8
+ "changeType": "fix",
9
+ "issues": [
10
+ 1245
11
+ ],
12
+ "summary": "treat assertSafe-cached variables as validated (closes #1245)"
13
+ },
14
+ {
15
+ "name": "enforce-dynamic-imports",
16
+ "changeType": "fix",
17
+ "issues": [
18
+ 1244
19
+ ],
20
+ "summary": "restore libraries whitelist mode and exempt builtins/internal paths (closes #1244)"
21
+ },
22
+ {
23
+ "name": "require-memo",
24
+ "changeType": "fix",
25
+ "issues": [
26
+ 1243
27
+ ],
28
+ "summary": "exempt camelCase render-prop callbacks (closes #1243)"
29
+ }
30
+ ]
31
+ },
32
+ {
33
+ "version": "1.16.0",
34
+ "date": "2026-06-29T19:34:27.787Z",
35
+ "rules": [
36
+ {
37
+ "name": "consistent-callback-naming",
38
+ "changeType": "fix",
39
+ "issues": [
40
+ 1182
41
+ ],
42
+ "summary": "skip union props with non-function members (closes #1182)"
43
+ },
44
+ {
45
+ "name": "enforce-boolean-naming-prefixes",
46
+ "changeType": "fix",
47
+ "issues": [
48
+ 1219
49
+ ],
50
+ "summary": "make property-signature checks opt-in (closes #1219)"
51
+ },
52
+ {
53
+ "name": "enforce-mui-rounded-icons",
54
+ "changeType": "fix",
55
+ "issues": [
56
+ 1218
57
+ ],
58
+ "summary": "strip variant suffix in fix and skip brand icons (closes #1218)"
59
+ },
60
+ {
61
+ "name": "enforce-verb-noun-naming",
62
+ "changeType": "fix",
63
+ "issues": [
64
+ 1177,
65
+ 1225
66
+ ],
67
+ "summary": "add 'bucket'/'bucketize' to verbs allowlist (closes #1225); allow `main` as a function name (closes #1177)"
68
+ },
69
+ {
70
+ "name": "global-const-style",
71
+ "changeType": "fix",
72
+ "issues": [
73
+ 1186
74
+ ],
75
+ "summary": "stop flagging null and boolean literals for `as const` (closes #1186)"
76
+ },
77
+ {
78
+ "name": "logical-top-to-bottom-grouping",
79
+ "changeType": "fix",
80
+ "issues": [
81
+ 1191
82
+ ],
83
+ "summary": "keep sibling destructures from the same source declarator grouped (closes #1191)"
84
+ },
85
+ {
86
+ "name": "memo-compare-deeply-complex-props",
87
+ "changeType": "fix",
88
+ "issues": [
89
+ 1179,
90
+ 1224
91
+ ],
92
+ "summary": "skip reserved React ref/key slots (closes #1224); exclude React render types from complex-prop detection"
93
+ },
94
+ {
95
+ "name": "memo-nested-react-components",
96
+ "changeType": "fix",
97
+ "issues": [
98
+ 1185
99
+ ],
100
+ "summary": "skip HOC factories and render-prop callbacks (closes #1185)"
101
+ },
102
+ {
103
+ "name": "no-array-length-in-deps",
104
+ "changeType": "fix",
105
+ "issues": [
106
+ 1196
107
+ ],
108
+ "summary": "allow array.length in deps when the body uses only .length (closes #1196)"
109
+ },
110
+ {
111
+ "name": "no-circular-references",
112
+ "changeType": "fix",
113
+ "issues": [],
114
+ "summary": "fix recursive member resolution and use cross-version scope helper; fix false positive for function parameters"
115
+ },
116
+ {
117
+ "name": "no-compositing-layer-props",
118
+ "changeType": "fix",
119
+ "issues": [
120
+ 1228
121
+ ],
122
+ "summary": "don't flag CSS reset/identity values (closes #1228)"
123
+ },
124
+ {
125
+ "name": "no-entire-object-hook-deps",
126
+ "changeType": "fix",
127
+ "issues": [
128
+ 1176
129
+ ],
130
+ "summary": "lock in shorthand/JSX usage detection for as-const memo returns (closes #1176); correctly handle TS assertions and optional chaining in parent traversal; resolve PR review comments on TS assertion handling in object literals"
131
+ },
132
+ {
133
+ "name": "no-explicit-return-type",
134
+ "changeType": "fix",
135
+ "issues": [
136
+ 1216
137
+ ],
138
+ "summary": "exempt explicit `never` return types (closes #1216)"
139
+ },
140
+ {
141
+ "name": "no-hungarian",
142
+ "changeType": "fix",
143
+ "issues": [
144
+ 1217
145
+ ],
146
+ "summary": "exempt generic type parameters and semantic type-concept names (closes #1217)"
147
+ },
148
+ {
149
+ "name": "no-margin-properties",
150
+ "changeType": "fix",
151
+ "issues": [
152
+ 1214
153
+ ],
154
+ "summary": "don't flag margins inside createTheme() overrides (closes #1214)"
155
+ },
156
+ {
157
+ "name": "no-unnecessary-verb-suffix",
158
+ "changeType": "fix",
159
+ "issues": [
160
+ 1227
161
+ ],
162
+ "summary": "exempt phrasal-verb particle endings (closes #1227)"
163
+ },
164
+ {
165
+ "name": "no-unused-props",
166
+ "changeType": "fix",
167
+ "issues": [
168
+ 1215
169
+ ],
170
+ "summary": "track props through generic wrappers and body destructuring (closes #1215)"
171
+ },
172
+ {
173
+ "name": "no-useless-fragment",
174
+ "changeType": "fix",
175
+ "issues": [
176
+ 1195
177
+ ],
178
+ "summary": "keep fragments wrapping a single expression container (closes #1195)"
179
+ },
180
+ {
181
+ "name": "react-memoize-literals",
182
+ "changeType": "fix",
183
+ "issues": [
184
+ 1169
185
+ ],
186
+ "summary": "extend sx/style exemption to conditional, logical, and array values (refs #1169); exempt inline sx/style JSX attribute literals (closes #1169)"
187
+ },
188
+ {
189
+ "name": "require-migration-script-metadata",
190
+ "changeType": "fix",
191
+ "issues": [],
192
+ "summary": "fix @migrationDependencies logic and update filename access for ESLint v9 compatibility; address review comments on JSDoc tag parsing and validation"
193
+ }
194
+ ]
195
+ }
196
+ ]
@@ -1,3 +0,0 @@
1
- type MessageIds = 'memoizeReferenceProp' | 'avoidPrimitiveMemo';
2
- export declare const preferMemoizedProps: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
3
- export {};