@blumintinc/eslint-plugin-blumint 1.7.3 → 1.8.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 (70) hide show
  1. package/README.md +83 -61
  2. package/lib/index.js +85 -1
  3. package/lib/rules/enforce-assert-throws.js +48 -3
  4. package/lib/rules/enforce-assertSafe-object-key.d.ts +2 -0
  5. package/lib/rules/enforce-assertSafe-object-key.js +284 -0
  6. package/lib/rules/enforce-centralized-mock-firestore.js +192 -14
  7. package/lib/rules/enforce-css-media-queries.d.ts +5 -0
  8. package/lib/rules/enforce-css-media-queries.js +87 -0
  9. package/lib/rules/enforce-dynamic-imports.d.ts +9 -0
  10. package/lib/rules/enforce-dynamic-imports.js +84 -0
  11. package/lib/rules/enforce-firestore-facade.js +8 -0
  12. package/lib/rules/enforce-id-capitalization.d.ts +6 -0
  13. package/lib/rules/enforce-id-capitalization.js +78 -0
  14. package/lib/rules/enforce-microdiff.d.ts +3 -0
  15. package/lib/rules/enforce-microdiff.js +379 -0
  16. package/lib/rules/enforce-mui-rounded-icons.d.ts +1 -0
  17. package/lib/rules/enforce-mui-rounded-icons.js +54 -0
  18. package/lib/rules/enforce-object-literal-as-const.d.ts +4 -0
  19. package/lib/rules/enforce-object-literal-as-const.js +88 -0
  20. package/lib/rules/enforce-positive-naming.d.ts +1 -0
  21. package/lib/rules/enforce-positive-naming.js +387 -0
  22. package/lib/rules/enforce-props-argument-name.d.ts +8 -0
  23. package/lib/rules/enforce-props-argument-name.js +182 -0
  24. package/lib/rules/enforce-react-type-naming.d.ts +3 -0
  25. package/lib/rules/enforce-react-type-naming.js +191 -0
  26. package/lib/rules/enforce-render-hits-memoization.js +155 -26
  27. package/lib/rules/enforce-singular-type-names.d.ts +2 -0
  28. package/lib/rules/enforce-singular-type-names.js +112 -0
  29. package/lib/rules/enforce-timestamp-now.d.ts +1 -0
  30. package/lib/rules/enforce-timestamp-now.js +223 -0
  31. package/lib/rules/enforce-verb-noun-naming.js +5 -2
  32. package/lib/rules/ensure-pointer-events-none.d.ts +1 -0
  33. package/lib/rules/ensure-pointer-events-none.js +211 -0
  34. package/lib/rules/extract-global-constants.d.ts +1 -1
  35. package/lib/rules/extract-global-constants.js +109 -1
  36. package/lib/rules/global-const-style.js +3 -2
  37. package/lib/rules/key-only-outermost-element.d.ts +3 -0
  38. package/lib/rules/key-only-outermost-element.js +152 -0
  39. package/lib/rules/no-always-true-false-conditions.d.ts +3 -0
  40. package/lib/rules/no-always-true-false-conditions.js +1221 -0
  41. package/lib/rules/no-circular-references.d.ts +1 -0
  42. package/lib/rules/no-circular-references.js +523 -0
  43. package/lib/rules/no-firestore-jest-mock.js +27 -4
  44. package/lib/rules/no-hungarian.d.ts +5 -0
  45. package/lib/rules/no-hungarian.js +223 -0
  46. package/lib/rules/no-mock-firebase-admin.js +20 -7
  47. package/lib/rules/no-object-values-on-strings.d.ts +2 -0
  48. package/lib/rules/no-object-values-on-strings.js +294 -0
  49. package/lib/rules/no-type-assertion-returns.d.ts +9 -0
  50. package/lib/rules/no-type-assertion-returns.js +299 -0
  51. package/lib/rules/no-unnecessary-destructuring.d.ts +5 -0
  52. package/lib/rules/no-unnecessary-destructuring.js +69 -0
  53. package/lib/rules/no-unnecessary-verb-suffix.d.ts +1 -0
  54. package/lib/rules/no-unnecessary-verb-suffix.js +203 -0
  55. package/lib/rules/no-unused-props.js +10 -5
  56. package/lib/rules/no-unused-usestate.d.ts +8 -0
  57. package/lib/rules/no-unused-usestate.js +84 -0
  58. package/lib/rules/omit-index-html.d.ts +7 -0
  59. package/lib/rules/omit-index-html.js +91 -0
  60. package/lib/rules/prefer-clone-deep.js +294 -22
  61. package/lib/rules/prefer-fragment-component.js +265 -34
  62. package/lib/rules/prefer-global-router-state-key.d.ts +5 -0
  63. package/lib/rules/prefer-global-router-state-key.js +89 -0
  64. package/lib/rules/prefer-usememo-over-useeffect-usestate.d.ts +5 -0
  65. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +129 -0
  66. package/lib/rules/prefer-utility-function-over-private-static.d.ts +1 -0
  67. package/lib/rules/prefer-utility-function-over-private-static.js +77 -0
  68. package/lib/rules/react-usememo-should-be-component.d.ts +1 -0
  69. package/lib/rules/react-usememo-should-be-component.js +623 -0
  70. package/package.json +10 -7
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RULE_NAME = void 0;
4
+ const createRule_1 = require("../utils/createRule");
5
+ exports.RULE_NAME = 'enforce-dynamic-imports';
6
+ exports.default = (0, createRule_1.createRule)({
7
+ name: exports.RULE_NAME,
8
+ meta: {
9
+ type: 'suggestion',
10
+ docs: {
11
+ description: 'Enforce dynamic imports for specified libraries to optimize bundle size',
12
+ recommended: 'error',
13
+ },
14
+ schema: [
15
+ {
16
+ type: 'object',
17
+ properties: {
18
+ libraries: {
19
+ type: 'array',
20
+ items: {
21
+ type: 'string',
22
+ },
23
+ },
24
+ allowImportType: {
25
+ type: 'boolean',
26
+ },
27
+ },
28
+ additionalProperties: false,
29
+ },
30
+ ],
31
+ messages: {
32
+ dynamicImportRequired: 'Static imports from "{{source}}" are not allowed to reduce initial bundle size. Use dynamic imports instead: const Component = useDynamic(() => import("{{source}}").then(mod => mod.Component))',
33
+ },
34
+ },
35
+ defaultOptions: [
36
+ {
37
+ libraries: ['@stream-io/video-react-sdk'],
38
+ allowImportType: true,
39
+ },
40
+ ],
41
+ create(context, [options]) {
42
+ const { libraries, allowImportType = true } = options;
43
+ // Check if the import source matches any of the specified libraries
44
+ const isLibraryMatch = (source) => {
45
+ return libraries.some((lib) => {
46
+ // Simple glob pattern matching
47
+ if (lib.includes('*')) {
48
+ const pattern = lib.replace(/\*/g, '.*');
49
+ const regex = new RegExp(`^${pattern}$`);
50
+ return regex.test(source);
51
+ }
52
+ return source === lib;
53
+ });
54
+ };
55
+ return {
56
+ ImportDeclaration(node) {
57
+ const importSource = node.source.value;
58
+ // Skip type-only imports if allowed
59
+ if (allowImportType) {
60
+ // Check if it's a type-only import declaration
61
+ if (node.importKind === 'type') {
62
+ return;
63
+ }
64
+ // Check if all specifiers are type imports
65
+ if (node.specifiers.length > 0 &&
66
+ node.specifiers.every(spec => (spec.type === 'ImportSpecifier' && spec.importKind === 'type'))) {
67
+ return;
68
+ }
69
+ }
70
+ // Check if the import is from a library that should be dynamically imported
71
+ if (typeof importSource === 'string' && isLibraryMatch(importSource)) {
72
+ context.report({
73
+ node,
74
+ messageId: 'dynamicImportRequired',
75
+ data: {
76
+ source: importSource,
77
+ },
78
+ });
79
+ }
80
+ },
81
+ };
82
+ },
83
+ });
84
+ //# sourceMappingURL=enforce-dynamic-imports.js.map
@@ -27,6 +27,10 @@ const isFirestoreMethodCall = (node) => {
27
27
  return true;
28
28
  }
29
29
  }
30
+ // Handle type assertions (as in the bug report)
31
+ if (object.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
32
+ return true;
33
+ }
30
34
  // Check if it's a Firestore reference
31
35
  let current = object;
32
36
  let foundDocOrCollection = false;
@@ -44,6 +48,10 @@ const isFirestoreMethodCall = (node) => {
44
48
  if (isMemberExpression(current)) {
45
49
  current = current.object;
46
50
  }
51
+ else if (current.type === utils_1.AST_NODE_TYPES.TSAsExpression) {
52
+ // Handle nested type assertions
53
+ current = current.expression;
54
+ }
47
55
  else {
48
56
  break;
49
57
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * This rule ensures consistency in user-facing text by enforcing the use of "ID"
3
+ * instead of "id" when referring to identifiers in UI labels, instructions,
4
+ * error messages, and other visible strings.
5
+ */
6
+ export declare const enforceIdCapitalization: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"enforceIdCapitalization", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforceIdCapitalization = void 0;
4
+ const createRule_1 = require("../utils/createRule");
5
+ /**
6
+ * This rule ensures consistency in user-facing text by enforcing the use of "ID"
7
+ * instead of "id" when referring to identifiers in UI labels, instructions,
8
+ * error messages, and other visible strings.
9
+ */
10
+ exports.enforceIdCapitalization = (0, createRule_1.createRule)({
11
+ name: 'enforce-id-capitalization',
12
+ meta: {
13
+ type: 'suggestion',
14
+ docs: {
15
+ description: 'Enforce the use of "ID" instead of "id" in user-facing text',
16
+ recommended: 'error',
17
+ },
18
+ fixable: 'code',
19
+ schema: [],
20
+ messages: {
21
+ enforceIdCapitalization: 'Use "ID" instead of "id" in user-facing text for better readability',
22
+ },
23
+ },
24
+ defaultOptions: [],
25
+ create(context) {
26
+ // Regular expression to match standalone "id" surrounded by whitespace or punctuation
27
+ // This ensures we only match "id" as a word, not as part of another word
28
+ const idRegex = /(^|\s|[.,;:!?'"()\[\]{}])id(\s|$|[.,;:!?'"()\[\]{}])/g;
29
+ /**
30
+ * Check if a string contains "id" as a standalone word and report if found
31
+ */
32
+ function checkForIdInString(node, value) {
33
+ if (typeof value !== 'string')
34
+ return;
35
+ // Reset the regex lastIndex to ensure consistent behavior
36
+ idRegex.lastIndex = 0;
37
+ // Check if the string contains "id" as a standalone word
38
+ if (idRegex.test(value)) {
39
+ // Reset the regex lastIndex again before replacing
40
+ idRegex.lastIndex = 0;
41
+ const fixedText = value.replace(idRegex, (_match, prefix, suffix) => {
42
+ return `${prefix}ID${suffix}`;
43
+ });
44
+ context.report({
45
+ node,
46
+ messageId: 'enforceIdCapitalization',
47
+ fix: (fixer) => {
48
+ if (node.type === 'Literal') {
49
+ return fixer.replaceText(node, JSON.stringify(fixedText));
50
+ }
51
+ else if (node.type === 'JSXText') {
52
+ return fixer.replaceText(node, fixedText);
53
+ }
54
+ else if (node.type === 'TemplateElement') {
55
+ return fixer.replaceText(node, fixedText);
56
+ }
57
+ return fixer.replaceText(node, JSON.stringify(fixedText));
58
+ },
59
+ });
60
+ }
61
+ }
62
+ return {
63
+ // Check string literals
64
+ Literal(node) {
65
+ if (typeof node.value === 'string') {
66
+ checkForIdInString(node, node.value);
67
+ }
68
+ },
69
+ // Check JSX text
70
+ JSXText(node) {
71
+ checkForIdInString(node, node.value);
72
+ },
73
+ // We don't need a separate handler for CallExpression since we already handle Literals
74
+ // The Literal handler will catch the string arguments in t("user.profile.id")
75
+ };
76
+ },
77
+ });
78
+ //# sourceMappingURL=enforce-id-capitalization.js.map
@@ -0,0 +1,3 @@
1
+ type MessageIds = 'enforceMicrodiff' | 'enforceMicrodiffImport';
2
+ export declare const enforceMicrodiff: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<MessageIds, [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
3
+ export {};
@@ -0,0 +1,379 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforceMicrodiff = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ exports.enforceMicrodiff = (0, createRule_1.createRule)({
7
+ name: 'enforce-microdiff',
8
+ meta: {
9
+ type: 'suggestion',
10
+ docs: {
11
+ description: 'Enforce using microdiff for object and array comparison operations',
12
+ recommended: 'error',
13
+ },
14
+ fixable: 'code',
15
+ schema: [],
16
+ messages: {
17
+ enforceMicrodiff: 'Use the microdiff library for object and array comparison operations',
18
+ enforceMicrodiffImport: 'Import diff from microdiff instead of {{importSource}}',
19
+ },
20
+ },
21
+ defaultOptions: [],
22
+ create(context) {
23
+ const sourceCode = context.getSourceCode();
24
+ const importedDiffLibraries = new Map();
25
+ const importedFunctions = new Map(); // Map of imported function names to their sources
26
+ let hasMicrodiffImport = false;
27
+ const reportedNodes = new Set();
28
+ // Add a specific set to track which import names are used
29
+ const usedImportNames = new Set();
30
+ // Check if a node is an object or array type
31
+ function isObjectOrArrayType(node) {
32
+ if (node.type === utils_1.AST_NODE_TYPES.ArrayExpression ||
33
+ node.type === utils_1.AST_NODE_TYPES.ObjectExpression) {
34
+ return true;
35
+ }
36
+ // For identifiers, we'll make a simple assumption based on naming conventions
37
+ if (node.type === utils_1.AST_NODE_TYPES.Identifier) {
38
+ const name = node.name.toLowerCase();
39
+ // Names that likely represent objects or arrays
40
+ if (name.includes('obj') ||
41
+ name.includes('config') ||
42
+ name.includes('options') ||
43
+ name.includes('data') ||
44
+ name.includes('state') ||
45
+ name.includes('props') ||
46
+ name.includes('items') ||
47
+ name.includes('array') ||
48
+ name.includes('list') ||
49
+ name.endsWith('s')) {
50
+ return true;
51
+ }
52
+ }
53
+ // For member expressions, assume they could be objects/arrays
54
+ if (node.type === utils_1.AST_NODE_TYPES.MemberExpression) {
55
+ return true;
56
+ }
57
+ return false;
58
+ }
59
+ return {
60
+ // Track imports of diffing libraries
61
+ ImportDeclaration(node) {
62
+ const importSource = node.source.value;
63
+ // Check for microdiff import
64
+ if (importSource === 'microdiff') {
65
+ hasMicrodiffImport = true;
66
+ return;
67
+ }
68
+ // Track other diffing libraries
69
+ if ([
70
+ 'deep-diff',
71
+ 'fast-diff',
72
+ 'diff',
73
+ 'deep-object-diff',
74
+ 'fast-deep-equal',
75
+ 'fast-deep-equal/es6',
76
+ ].includes(importSource)) {
77
+ // Track imported function names and their sources
78
+ node.specifiers.forEach((specifier) => {
79
+ if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
80
+ (specifier.imported.name === 'diff' ||
81
+ specifier.imported.name === 'diffArrays' ||
82
+ specifier.imported.name === 'detailedDiff')) {
83
+ // Track the local name (which could be different due to renaming)
84
+ const localName = specifier.local.name;
85
+ importedFunctions.set(localName, importSource);
86
+ }
87
+ else if (importSource === 'fast-deep-equal' ||
88
+ importSource === 'fast-deep-equal/es6') {
89
+ // Handle default imports for fast-deep-equal
90
+ if (specifier.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier) {
91
+ importedFunctions.set(specifier.local.name, importSource);
92
+ }
93
+ }
94
+ });
95
+ // Report all competing diffing libraries right away
96
+ context.report({
97
+ node,
98
+ messageId: 'enforceMicrodiffImport',
99
+ data: {
100
+ importSource,
101
+ },
102
+ fix(fixer) {
103
+ // If we already have a microdiff import, just remove this import
104
+ if (hasMicrodiffImport) {
105
+ return fixer.remove(node);
106
+ }
107
+ // Otherwise, replace with microdiff import
108
+ return fixer.replaceText(node, `import { diff } from 'microdiff';`);
109
+ },
110
+ });
111
+ // Check if importing a diff function or a known equality library
112
+ const hasDiffImport = node.specifiers.some((specifier) => {
113
+ if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier) {
114
+ return (specifier.imported.name === 'diff' ||
115
+ specifier.imported.name === 'diffArrays' ||
116
+ specifier.imported.name === 'detailedDiff');
117
+ }
118
+ return false;
119
+ });
120
+ if (hasDiffImport ||
121
+ importSource === 'fast-deep-equal' ||
122
+ importSource === 'fast-deep-equal/es6') {
123
+ importedDiffLibraries.set(importSource, node);
124
+ }
125
+ }
126
+ },
127
+ // Check for usage of other diffing libraries
128
+ CallExpression(node) {
129
+ // Skip if we've already reported this node
130
+ if (reportedNodes.has(node)) {
131
+ return;
132
+ }
133
+ const { callee } = node;
134
+ // Check for direct calls to imported diff functions
135
+ if (callee.type === utils_1.AST_NODE_TYPES.Identifier) {
136
+ const name = callee.name;
137
+ // Check if this is a function we specifically imported from a diff library
138
+ if (importedFunctions.has(name)) {
139
+ usedImportNames.add(name);
140
+ // Always report it if it's from a tracked library
141
+ reportedNodes.add(node);
142
+ context.report({
143
+ node,
144
+ messageId: 'enforceMicrodiff',
145
+ fix(fixer) {
146
+ return fixer.replaceText(callee, 'diff');
147
+ },
148
+ });
149
+ return;
150
+ }
151
+ // Check known diff function names
152
+ const isDiffFunction = [
153
+ 'deepDiff',
154
+ 'fastDiff',
155
+ 'diffArrays',
156
+ 'detailedDiff',
157
+ 'fastDeepEqual',
158
+ 'isEqual',
159
+ ].includes(name);
160
+ if (isDiffFunction) {
161
+ // Track this import name as used
162
+ usedImportNames.add(name);
163
+ // Check if we have at least 2 arguments that are objects or arrays
164
+ if (node.arguments.length >= 2 &&
165
+ isObjectOrArrayType(node.arguments[0]) &&
166
+ isObjectOrArrayType(node.arguments[1])) {
167
+ reportedNodes.add(node);
168
+ context.report({
169
+ node,
170
+ messageId: 'enforceMicrodiff',
171
+ fix(fixer) {
172
+ // When handling fast-diff and similar libraries, need to ensure the function name is replaced
173
+ return fixer.replaceText(callee, 'diff');
174
+ },
175
+ });
176
+ }
177
+ }
178
+ }
179
+ // Check for lodash difference functions
180
+ if (callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
181
+ callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
182
+ callee.object.name === '_') {
183
+ const property = callee.property;
184
+ if (property.type === utils_1.AST_NODE_TYPES.Identifier &&
185
+ ['difference', 'differenceBy', 'differenceWith'].includes(property.name)) {
186
+ reportedNodes.add(node);
187
+ context.report({
188
+ node,
189
+ messageId: 'enforceMicrodiff',
190
+ fix(fixer) {
191
+ // Replace with microdiff
192
+ return fixer.replaceText(node, `diff(${node.arguments
193
+ .map((arg) => sourceCode.getText(arg))
194
+ .join(', ')})`);
195
+ },
196
+ });
197
+ }
198
+ }
199
+ },
200
+ // Check for manual object comparison patterns
201
+ BinaryExpression(node) {
202
+ // Skip if we've already reported this node or its parent function
203
+ if (reportedNodes.has(node)) {
204
+ return;
205
+ }
206
+ // Find the parent function or method
207
+ let current = node;
208
+ while (current &&
209
+ current.type !== utils_1.AST_NODE_TYPES.FunctionDeclaration &&
210
+ current.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
211
+ current = current.parent;
212
+ }
213
+ // If we already reported the parent function, skip this node
214
+ if (current && reportedNodes.has(current)) {
215
+ return;
216
+ }
217
+ // Check for JSON.stringify comparison pattern
218
+ if ((node.operator === '===' || node.operator === '!==') &&
219
+ node.left.type === utils_1.AST_NODE_TYPES.CallExpression &&
220
+ node.right.type === utils_1.AST_NODE_TYPES.CallExpression) {
221
+ const isJsonStringify = (expr) => {
222
+ return (expr.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
223
+ expr.callee.object.type === utils_1.AST_NODE_TYPES.Identifier &&
224
+ expr.callee.object.name === 'JSON' &&
225
+ expr.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
226
+ expr.callee.property.name === 'stringify');
227
+ };
228
+ if (isJsonStringify(node.left) && isJsonStringify(node.right)) {
229
+ const leftArg = node.left.arguments[0];
230
+ const rightArg = node.right.arguments[0];
231
+ if (isObjectOrArrayType(leftArg) && isObjectOrArrayType(rightArg)) {
232
+ reportedNodes.add(node);
233
+ const isEqual = node.operator === '===';
234
+ context.report({
235
+ node,
236
+ messageId: 'enforceMicrodiff',
237
+ fix(fixer) {
238
+ // Find the containing function to add the import
239
+ let functionNode = node;
240
+ while (functionNode &&
241
+ functionNode.type !== utils_1.AST_NODE_TYPES.FunctionDeclaration &&
242
+ functionNode.type !==
243
+ utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
244
+ functionNode.type !== utils_1.AST_NODE_TYPES.Program) {
245
+ functionNode = functionNode.parent;
246
+ }
247
+ // If we found a program node and microdiff isn't imported,
248
+ // we'll need to add the import manually
249
+ if (functionNode &&
250
+ functionNode.type === utils_1.AST_NODE_TYPES.Program &&
251
+ !hasMicrodiffImport) {
252
+ // Need to add an import
253
+ const importFix = fixer.insertTextBeforeRange([0, 0], "import { diff } from 'microdiff';\n\n");
254
+ // Replace JSON.stringify comparison
255
+ const compareFix = fixer.replaceText(node, `diff(${sourceCode.getText(leftArg)}, ${sourceCode.getText(rightArg)})${isEqual ? '.length === 0' : '.length > 0'}`);
256
+ return [importFix, compareFix];
257
+ }
258
+ // Otherwise just replace the comparison
259
+ return fixer.replaceText(node, `diff(${sourceCode.getText(leftArg)}, ${sourceCode.getText(rightArg)})${isEqual ? '.length === 0' : '.length > 0'}`);
260
+ },
261
+ });
262
+ }
263
+ }
264
+ }
265
+ },
266
+ // Check for custom deep comparison functions
267
+ FunctionDeclaration(node) {
268
+ // Skip if we've already reported this node
269
+ if (reportedNodes.has(node)) {
270
+ return;
271
+ }
272
+ // Look for functions that might be implementing diff logic
273
+ if (node.id &&
274
+ [
275
+ 'detectChanges',
276
+ 'hasConfigChanged',
277
+ 'compareObjects',
278
+ 'compareArrays',
279
+ 'findChanges',
280
+ 'detectDifferences',
281
+ 'hasStateChanged',
282
+ 'stateHasUpdated',
283
+ 'arrayHasChanged',
284
+ 'settingsChanged',
285
+ ].includes(node.id.name)) {
286
+ // Check if function has two parameters that might be objects/arrays
287
+ if (node.params.length >= 2) {
288
+ const body = node.body;
289
+ const bodyText = sourceCode.getText(body);
290
+ // Check if the function body contains a JSON.stringify comparison
291
+ if (node.id.name === 'hasConfigChanged' &&
292
+ bodyText.includes('JSON.stringify') &&
293
+ bodyText.includes('!==')) {
294
+ reportedNodes.add(node);
295
+ const param1 = sourceCode.getText(node.params[0]);
296
+ const param2 = sourceCode.getText(node.params[1]);
297
+ context.report({
298
+ node,
299
+ messageId: 'enforceMicrodiff',
300
+ fix(fixer) {
301
+ // Create a new version of the function with microdiff
302
+ const newFunctionBody = `{
303
+ return diff(${param1}, ${param2}).length > 0;
304
+ }`;
305
+ if (!hasMicrodiffImport) {
306
+ // Create a new import statement
307
+ return fixer.replaceText(node, `import { diff } from 'microdiff';\n\nfunction ${node.id?.name}(${param1}, ${param2}) ${newFunctionBody}`);
308
+ }
309
+ else {
310
+ // Just replace the function body
311
+ return fixer.replaceText(body, newFunctionBody);
312
+ }
313
+ },
314
+ });
315
+ return;
316
+ }
317
+ // Look for patterns that suggest object/array comparison
318
+ const hasComparisonLogic = bodyText.includes('JSON.stringify') ||
319
+ bodyText.includes('Object.keys') ||
320
+ bodyText.includes('for (') ||
321
+ bodyText.includes('.some(') ||
322
+ bodyText.includes('.every(');
323
+ if (hasComparisonLogic) {
324
+ reportedNodes.add(node);
325
+ context.report({
326
+ node,
327
+ messageId: 'enforceMicrodiff',
328
+ });
329
+ }
330
+ }
331
+ }
332
+ },
333
+ // Check for custom deep comparison in arrow functions
334
+ ArrowFunctionExpression(node) {
335
+ // Skip if we've already reported this node
336
+ if (reportedNodes.has(node)) {
337
+ return;
338
+ }
339
+ // Only check arrow functions assigned to variables with comparison-like names
340
+ const parent = node.parent;
341
+ if (parent &&
342
+ parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
343
+ parent.id.type === utils_1.AST_NODE_TYPES.Identifier &&
344
+ [
345
+ 'detectChanges',
346
+ 'hasConfigChanged',
347
+ 'compareObjects',
348
+ 'compareArrays',
349
+ 'findChanges',
350
+ 'detectDifferences',
351
+ 'hasStateChanged',
352
+ 'stateHasUpdated',
353
+ 'arrayHasChanged',
354
+ 'settingsChanged',
355
+ ].includes(parent.id.name)) {
356
+ // Check if function has two parameters that might be objects/arrays
357
+ if (node.params.length >= 2) {
358
+ const body = node.body;
359
+ // Look for patterns that suggest object/array comparison
360
+ const bodyText = sourceCode.getText(body);
361
+ const hasComparisonLogic = bodyText.includes('JSON.stringify') ||
362
+ bodyText.includes('Object.keys') ||
363
+ bodyText.includes('for (') ||
364
+ bodyText.includes('.some(') ||
365
+ bodyText.includes('.every(');
366
+ if (hasComparisonLogic) {
367
+ reportedNodes.add(node);
368
+ context.report({
369
+ node,
370
+ messageId: 'enforceMicrodiff',
371
+ });
372
+ }
373
+ }
374
+ }
375
+ },
376
+ };
377
+ },
378
+ });
379
+ //# sourceMappingURL=enforce-microdiff.js.map
@@ -0,0 +1 @@
1
+ export declare const enforceMuiRoundedIcons: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"enforceRoundedVariant", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforceMuiRoundedIcons = void 0;
4
+ const createRule_1 = require("../utils/createRule");
5
+ const utils_1 = require("@typescript-eslint/utils");
6
+ exports.enforceMuiRoundedIcons = (0, createRule_1.createRule)({
7
+ name: 'enforce-mui-rounded-icons',
8
+ meta: {
9
+ type: 'suggestion',
10
+ docs: {
11
+ description: 'Enforce the use of -Rounded variant for MUI icons',
12
+ recommended: 'error',
13
+ },
14
+ fixable: 'code',
15
+ schema: [],
16
+ messages: {
17
+ enforceRoundedVariant: 'Use the -Rounded variant for MUI icons (e.g., LogoutRounded instead of Logout)',
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ return {
23
+ ImportDeclaration(node) {
24
+ // Only check imports from @mui/icons-material
25
+ if (node.source.type === utils_1.AST_NODE_TYPES.Literal &&
26
+ typeof node.source.value === 'string' &&
27
+ node.source.value.startsWith('@mui/icons-material/')) {
28
+ const iconPath = node.source.value;
29
+ // Skip if already using -Rounded variant
30
+ if (iconPath.endsWith('Rounded')) {
31
+ return;
32
+ }
33
+ // Extract the icon name from the path
34
+ const iconName = iconPath.split('/').pop();
35
+ // Skip if the icon name is not a string (shouldn't happen)
36
+ if (!iconName) {
37
+ return;
38
+ }
39
+ // Create the rounded variant name
40
+ const roundedVariant = `${iconName}Rounded`;
41
+ context.report({
42
+ node,
43
+ messageId: 'enforceRoundedVariant',
44
+ fix: (fixer) => {
45
+ // Replace the import path with the rounded variant
46
+ return fixer.replaceText(node.source, `'@mui/icons-material/${roundedVariant}'`);
47
+ },
48
+ });
49
+ }
50
+ },
51
+ };
52
+ },
53
+ });
54
+ //# sourceMappingURL=enforce-mui-rounded-icons.js.map
@@ -0,0 +1,4 @@
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+ export declare const enforceObjectLiteralAsConst: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"enforceAsConst", never[], {
3
+ ReturnStatement(node: TSESTree.ReturnStatement): void;
4
+ }>;