@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,284 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforceAssertSafeObjectKey = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ exports.enforceAssertSafeObjectKey = (0, createRule_1.createRule)({
7
+ name: 'enforce-assertSafe-object-key',
8
+ meta: {
9
+ type: 'problem',
10
+ docs: {
11
+ description: 'Enforce the use of assertSafe(id) when accessing object properties with computed keys that involve string interpolation or explicit string conversion.',
12
+ recommended: 'error',
13
+ },
14
+ fixable: 'code',
15
+ schema: [],
16
+ messages: {
17
+ useAssertSafe: 'Use assertSafe() for object key access to ensure safe property access. Replace `obj[String(id)]` or `obj[`${id}`]` with `obj[assertSafe(id)]`.',
18
+ },
19
+ },
20
+ defaultOptions: [],
21
+ create(context) {
22
+ let hasAssertSafeImport = false;
23
+ /**
24
+ * Helper function to add assertSafe import if needed
25
+ */
26
+ const addAssertSafeImport = (fixer) => {
27
+ const program = context.getSourceCode().ast;
28
+ const firstImport = program.body.find((node) => node.type === utils_1.AST_NODE_TYPES.ImportDeclaration);
29
+ const importStatement = "import { assertSafe } from 'utils/assertions';\n";
30
+ if (firstImport) {
31
+ return fixer.insertTextBefore(firstImport, importStatement);
32
+ }
33
+ else {
34
+ return fixer.insertTextBefore(program.body[0], importStatement);
35
+ }
36
+ };
37
+ /**
38
+ * Helper function to create fixes for a node
39
+ */
40
+ const createFixes = (fixer, node, argText) => {
41
+ const fixes = [];
42
+ // Add import if not present
43
+ if (!hasAssertSafeImport) {
44
+ fixes.push(addAssertSafeImport(fixer));
45
+ hasAssertSafeImport = true;
46
+ }
47
+ // Replace the node with assertSafe(argText)
48
+ fixes.push(fixer.replaceText(node, `assertSafe(${argText})`));
49
+ return fixes;
50
+ };
51
+ return {
52
+ ImportDeclaration(node) {
53
+ // Check if assertSafe is already imported
54
+ if (node.specifiers.some((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
55
+ specifier.imported.name === 'assertSafe')) {
56
+ hasAssertSafeImport = true;
57
+ }
58
+ },
59
+ // Handle computed property in object destructuring
60
+ Property(node) {
61
+ if (node.computed && node.key) {
62
+ const key = node.key;
63
+ // Check for String(id) pattern
64
+ if (key.type === utils_1.AST_NODE_TYPES.CallExpression &&
65
+ key.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
66
+ key.callee.name === 'String') {
67
+ context.report({
68
+ node: key,
69
+ messageId: 'useAssertSafe',
70
+ fix(fixer) {
71
+ const arg = key.arguments[0];
72
+ const argText = context.getSourceCode().getText(arg);
73
+ return createFixes(fixer, key, argText);
74
+ },
75
+ });
76
+ }
77
+ // Check for template literals like `${id}`
78
+ if (key.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
79
+ key.expressions.length === 1 &&
80
+ key.quasis.length === 2 &&
81
+ key.quasis[0].value.raw === '' &&
82
+ key.quasis[1].value.raw === '') {
83
+ context.report({
84
+ node: key,
85
+ messageId: 'useAssertSafe',
86
+ fix(fixer) {
87
+ const expr = key.expressions[0];
88
+ const exprText = context.getSourceCode().getText(expr);
89
+ return createFixes(fixer, key, exprText);
90
+ },
91
+ });
92
+ }
93
+ }
94
+ },
95
+ // Handle binary expressions like 'key' in obj
96
+ BinaryExpression(node) {
97
+ if (node.operator === 'in') {
98
+ const left = node.left;
99
+ // Check for String(id) pattern
100
+ if (left.type === utils_1.AST_NODE_TYPES.CallExpression &&
101
+ left.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
102
+ left.callee.name === 'String') {
103
+ context.report({
104
+ node: left,
105
+ messageId: 'useAssertSafe',
106
+ fix(fixer) {
107
+ const arg = left.arguments[0];
108
+ const argText = context.getSourceCode().getText(arg);
109
+ return createFixes(fixer, left, argText);
110
+ },
111
+ });
112
+ }
113
+ // Check for template literals like `${id}`
114
+ if (left.type === utils_1.AST_NODE_TYPES.TemplateLiteral &&
115
+ left.expressions.length === 1 &&
116
+ left.quasis.length === 2 &&
117
+ left.quasis[0].value.raw === '' &&
118
+ left.quasis[1].value.raw === '') {
119
+ context.report({
120
+ node: left,
121
+ messageId: 'useAssertSafe',
122
+ fix(fixer) {
123
+ const expr = left.expressions[0];
124
+ const exprText = context.getSourceCode().getText(expr);
125
+ return createFixes(fixer, left, exprText);
126
+ },
127
+ });
128
+ }
129
+ }
130
+ },
131
+ MemberExpression(node) {
132
+ if (node.computed) {
133
+ const property = node.property;
134
+ // Skip if already using assertSafe
135
+ if (property.type === utils_1.AST_NODE_TYPES.CallExpression &&
136
+ property.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
137
+ property.callee.name === 'assertSafe') {
138
+ // Already using assertSafe, this is valid
139
+ return;
140
+ }
141
+ // Try to determine if this is likely an array or dictionary
142
+ const objectNode = node.object;
143
+ let objectName = '';
144
+ let isLikelyArray = false;
145
+ if (objectNode.type === utils_1.AST_NODE_TYPES.Identifier) {
146
+ objectName = objectNode.name.toLowerCase();
147
+ isLikelyArray =
148
+ /^(array|arr|items|elements|list|collection|data)s?$/i.test(objectName);
149
+ }
150
+ // Check for string literals - allow them for dictionaries but not for regular objects
151
+ if (property.type === utils_1.AST_NODE_TYPES.Literal &&
152
+ typeof property.value === 'string') {
153
+ // String literals are fine, no need for assertSafe
154
+ return;
155
+ }
156
+ // Check for numeric literals - always allow for arrays
157
+ if (property.type === utils_1.AST_NODE_TYPES.Literal &&
158
+ typeof property.value === 'number') {
159
+ // Numeric literals are fine, no need for assertSafe
160
+ return;
161
+ }
162
+ // Check if we're using String(id) pattern
163
+ if (property.type === utils_1.AST_NODE_TYPES.CallExpression &&
164
+ property.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
165
+ property.callee.name === 'String') {
166
+ context.report({
167
+ node: property,
168
+ messageId: 'useAssertSafe',
169
+ fix(fixer) {
170
+ const arg = property.arguments[0];
171
+ const argText = context.getSourceCode().getText(arg);
172
+ return createFixes(fixer, property, argText);
173
+ },
174
+ });
175
+ return;
176
+ }
177
+ // Check for template literals
178
+ if (property.type === utils_1.AST_NODE_TYPES.TemplateLiteral) {
179
+ // If it's a template literal in an array, allow it
180
+ if (isLikelyArray) {
181
+ return;
182
+ }
183
+ // Only flag simple template literals that are just `${id}`
184
+ // Complex templates with additional text like `prefix_${id}_suffix` are allowed
185
+ const isSimpleVarInterpolation = property.expressions.length === 1 &&
186
+ property.quasis.length === 2 &&
187
+ property.quasis[0].value.raw === '' &&
188
+ property.quasis[1].value.raw === '';
189
+ if (!isSimpleVarInterpolation) {
190
+ // Complex template literals with additional text are fine
191
+ return;
192
+ }
193
+ context.report({
194
+ node: property,
195
+ messageId: 'useAssertSafe',
196
+ fix(fixer) {
197
+ // Extract the expression from the template literal
198
+ const expr = property.expressions[0];
199
+ const exprText = context.getSourceCode().getText(expr);
200
+ return createFixes(fixer, property, exprText);
201
+ },
202
+ });
203
+ return;
204
+ }
205
+ // Check for direct variable usage (identifiers)
206
+ if (property.type === utils_1.AST_NODE_TYPES.Identifier) {
207
+ // Skip numeric literals, they're safe
208
+ if (/^\d+$/.test(property.name)) {
209
+ return;
210
+ }
211
+ // If it looks like an array access, allow it
212
+ if (isLikelyArray) {
213
+ return;
214
+ }
215
+ context.report({
216
+ node: property,
217
+ messageId: 'useAssertSafe',
218
+ fix(fixer) {
219
+ // For direct variable use, just use the variable name
220
+ const propText = context.getSourceCode().getText(property);
221
+ return createFixes(fixer, property, propText);
222
+ },
223
+ });
224
+ return;
225
+ }
226
+ // Check for binary expressions (like index + 1)
227
+ if (property.type === utils_1.AST_NODE_TYPES.BinaryExpression) {
228
+ // Allow binary expressions in array access
229
+ if (isLikelyArray) {
230
+ return;
231
+ }
232
+ context.report({
233
+ node: property,
234
+ messageId: 'useAssertSafe',
235
+ fix(fixer) {
236
+ const propText = context.getSourceCode().getText(property);
237
+ return createFixes(fixer, property, propText);
238
+ },
239
+ });
240
+ return;
241
+ }
242
+ // Check for boolean expressions and other literals
243
+ if (property.type === utils_1.AST_NODE_TYPES.Literal ||
244
+ property.type === utils_1.AST_NODE_TYPES.LogicalExpression ||
245
+ property.type === utils_1.AST_NODE_TYPES.ConditionalExpression) {
246
+ // Allow these expressions in array access
247
+ if (isLikelyArray) {
248
+ return;
249
+ }
250
+ context.report({
251
+ node: property,
252
+ messageId: 'useAssertSafe',
253
+ fix(fixer) {
254
+ const propText = context.getSourceCode().getText(property);
255
+ return createFixes(fixer, property, propText);
256
+ },
257
+ });
258
+ return;
259
+ }
260
+ // Check for function calls (anything that isn't handled above)
261
+ if (property.type === utils_1.AST_NODE_TYPES.MemberExpression ||
262
+ (property.type === utils_1.AST_NODE_TYPES.CallExpression &&
263
+ !(property.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
264
+ property.callee.name === 'String'))) {
265
+ // Allow member expressions and function calls in array access
266
+ if (isLikelyArray) {
267
+ return;
268
+ }
269
+ context.report({
270
+ node: property,
271
+ messageId: 'useAssertSafe',
272
+ fix(fixer) {
273
+ const propText = context.getSourceCode().getText(property);
274
+ return createFixes(fixer, property, propText);
275
+ },
276
+ });
277
+ return;
278
+ }
279
+ }
280
+ },
281
+ };
282
+ },
283
+ });
284
+ //# sourceMappingURL=enforce-assertSafe-object-key.js.map
@@ -21,34 +21,212 @@ exports.enforceCentralizedMockFirestore = (0, createRule_1.createRule)({
21
21
  defaultOptions: [],
22
22
  create(context) {
23
23
  let hasCentralizedImport = false;
24
- let mockFirestoreNodes = [];
24
+ const mockFirestoreNodes = new Set();
25
+ const customMockFirestoreNames = new Set();
26
+ const customMockFirestoreCallExpressions = new Set();
27
+ const thisExpressions = [];
25
28
  return {
26
29
  ImportDeclaration(node) {
27
30
  if (node.source.value.endsWith(MOCK_FIRESTORE_PATH)) {
28
31
  hasCentralizedImport = true;
32
+ // Check for renamed imports
33
+ for (const specifier of node.specifiers) {
34
+ if (specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
35
+ specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
36
+ specifier.imported.name === 'mockFirestore' &&
37
+ specifier.local.name !== 'mockFirestore') {
38
+ customMockFirestoreNames.add(specifier.local.name);
39
+ }
40
+ }
29
41
  }
30
42
  },
31
43
  VariableDeclarator(node) {
32
44
  if (node.id.type === utils_1.AST_NODE_TYPES.Identifier &&
33
45
  node.id.name === 'mockFirestore') {
34
- mockFirestoreNodes.push(node);
46
+ mockFirestoreNodes.add(node);
47
+ }
48
+ else if (node.id.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
49
+ for (const prop of node.id.properties) {
50
+ if (prop.type === utils_1.AST_NODE_TYPES.Property &&
51
+ prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
52
+ prop.key.name === 'mockFirestore') {
53
+ mockFirestoreNodes.add(node);
54
+ // Track renamed destructured imports
55
+ if (prop.value.type === utils_1.AST_NODE_TYPES.Identifier &&
56
+ prop.value.name !== 'mockFirestore') {
57
+ customMockFirestoreNames.add(prop.value.name);
58
+ }
59
+ break;
60
+ }
61
+ }
35
62
  }
36
63
  },
37
- 'Program:exit'() {
38
- if (!hasCentralizedImport && mockFirestoreNodes.length > 0) {
39
- for (const node of mockFirestoreNodes) {
40
- context.report({
41
- node,
42
- messageId: 'useCentralizedMockFirestore',
43
- fix(fixer) {
44
- const importFix = fixer.insertTextBefore(context.getSourceCode().ast.body[0], `import { mockFirestore } from '${MOCK_FIRESTORE_PATH}';\n\n`);
45
- const declarationFix = fixer.remove(node.parent);
46
- return [importFix, declarationFix];
47
- },
48
- });
64
+ PropertyDefinition(node) {
65
+ if (node.key.type === utils_1.AST_NODE_TYPES.Identifier &&
66
+ node.key.name === 'mockFirestore') {
67
+ mockFirestoreNodes.add(node);
68
+ }
69
+ },
70
+ CallExpression(node) {
71
+ // Track calls to custom mockFirestore names
72
+ if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
73
+ customMockFirestoreNames.has(node.callee.name)) {
74
+ customMockFirestoreCallExpressions.add(node);
75
+ }
76
+ if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
77
+ node.callee.name === 'require' &&
78
+ node.arguments.length > 0 &&
79
+ node.arguments[0].type === utils_1.AST_NODE_TYPES.Literal &&
80
+ typeof node.arguments[0].value === 'string' &&
81
+ !node.arguments[0].value.endsWith(MOCK_FIRESTORE_PATH)) {
82
+ const parent = node.parent;
83
+ if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
84
+ parent.id.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
85
+ for (const prop of parent.id.properties) {
86
+ if (prop.type === utils_1.AST_NODE_TYPES.Property &&
87
+ prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
88
+ prop.key.name === 'mockFirestore') {
89
+ mockFirestoreNodes.add(parent);
90
+ // Track renamed destructured imports
91
+ if (prop.value.type === utils_1.AST_NODE_TYPES.Identifier &&
92
+ prop.value.name !== 'mockFirestore') {
93
+ customMockFirestoreNames.add(prop.value.name);
94
+ }
95
+ break;
96
+ }
97
+ }
98
+ }
99
+ }
100
+ },
101
+ // Handle dynamic imports
102
+ 'AwaitExpression > CallExpression[callee.type="ImportExpression"]'(node) {
103
+ const parent = node.parent;
104
+ if (parent?.type === utils_1.AST_NODE_TYPES.AwaitExpression &&
105
+ parent.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
106
+ parent.parent.id.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
107
+ for (const prop of parent.parent.id.properties) {
108
+ if (prop.type === utils_1.AST_NODE_TYPES.Property &&
109
+ prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
110
+ prop.key.name === 'mockFirestore') {
111
+ mockFirestoreNodes.add(parent.parent);
112
+ // Track renamed destructured imports
113
+ if (prop.value.type === utils_1.AST_NODE_TYPES.Identifier &&
114
+ prop.value.name !== 'mockFirestore') {
115
+ customMockFirestoreNames.add(prop.value.name);
116
+ }
117
+ break;
118
+ }
49
119
  }
50
120
  }
51
121
  },
122
+ // Handle complex object destructuring
123
+ 'ObjectPattern > Property > ObjectPattern > Property > ObjectPattern > Property[key.name="mockFirestore"]'(node) {
124
+ let current = node;
125
+ while (current.parent) {
126
+ if (current.parent.type === utils_1.AST_NODE_TYPES.VariableDeclarator) {
127
+ mockFirestoreNodes.add(current.parent);
128
+ break;
129
+ }
130
+ current = current.parent;
131
+ }
132
+ },
133
+ // Capture this.mockFirestore expressions
134
+ MemberExpression(node) {
135
+ if (node.object.type === utils_1.AST_NODE_TYPES.ThisExpression &&
136
+ node.property.type === utils_1.AST_NODE_TYPES.Identifier &&
137
+ node.property.name === 'mockFirestore') {
138
+ thisExpressions.push(node);
139
+ }
140
+ },
141
+ 'Program:exit'() {
142
+ if (mockFirestoreNodes.size > 0) {
143
+ const sourceCode = context.getSourceCode();
144
+ // Report only once for the entire file
145
+ context.report({
146
+ node: Array.from(mockFirestoreNodes)[0],
147
+ messageId: 'useCentralizedMockFirestore',
148
+ fix(fixer) {
149
+ // Instead of trying to modify the code incrementally, we'll generate the entire fixed code
150
+ const originalText = sourceCode.getText();
151
+ const lines = originalText.split('\n');
152
+ // Find the indentation of the code
153
+ const indentMatch = lines[0].match(/^(\s*)/);
154
+ const indent = indentMatch ? indentMatch[1] : '';
155
+ // Create the import statement
156
+ const importLine = `${indent}import { mockFirestore } from '${MOCK_FIRESTORE_PATH}';`;
157
+ // Find all the lines that need to be removed
158
+ const linesToRemove = new Set();
159
+ // Process all nodes that need to be removed
160
+ mockFirestoreNodes.forEach(node => {
161
+ const startLine = sourceCode.getLocFromIndex(node.range[0]).line - 1;
162
+ const endLine = sourceCode.getLocFromIndex(node.range[1]).line - 1;
163
+ if (node.parent?.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
164
+ // If it's the only declarator, remove the entire declaration
165
+ if (node.parent.declarations.length === 1) {
166
+ const declStartLine = sourceCode.getLocFromIndex(node.parent.range[0]).line - 1;
167
+ const declEndLine = sourceCode.getLocFromIndex(node.parent.range[1]).line - 1;
168
+ for (let i = declStartLine; i <= declEndLine; i++) {
169
+ linesToRemove.add(i);
170
+ }
171
+ }
172
+ else {
173
+ // Otherwise, just remove this declarator
174
+ for (let i = startLine; i <= endLine; i++) {
175
+ linesToRemove.add(i);
176
+ }
177
+ }
178
+ }
179
+ else if (node.type === utils_1.AST_NODE_TYPES.PropertyDefinition) {
180
+ // Remove class property
181
+ for (let i = startLine; i <= endLine; i++) {
182
+ linesToRemove.add(i);
183
+ }
184
+ }
185
+ });
186
+ // Replace custom mockFirestore references with the standard one
187
+ const replacements = [];
188
+ // Add replacements for custom mockFirestore names
189
+ customMockFirestoreCallExpressions.forEach(node => {
190
+ if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier) {
191
+ replacements.push([
192
+ node.callee.range[0],
193
+ node.callee.name,
194
+ 'mockFirestore'
195
+ ]);
196
+ }
197
+ });
198
+ // Add replacements for this.mockFirestore
199
+ thisExpressions.forEach(expr => {
200
+ replacements.push([
201
+ expr.range[0],
202
+ sourceCode.getText(expr),
203
+ 'mockFirestore'
204
+ ]);
205
+ });
206
+ // Sort replacements in reverse order to avoid range issues
207
+ replacements.sort((a, b) => b[0] - a[0]);
208
+ // Apply replacements to the original text
209
+ let fixedText = originalText;
210
+ for (const [pos, oldText, newText] of replacements) {
211
+ fixedText =
212
+ fixedText.substring(0, pos) +
213
+ newText +
214
+ fixedText.substring(pos + oldText.length);
215
+ }
216
+ // Filter out the lines to remove
217
+ const fixedLines = fixedText.split('\n').filter((_, i) => !linesToRemove.has(i));
218
+ // Add the import statement at the beginning
219
+ if (!hasCentralizedImport) {
220
+ fixedLines.unshift(importLine);
221
+ }
222
+ // Join the lines back together
223
+ const result = fixedLines.join('\n');
224
+ // Return the fixed text
225
+ return fixer.replaceText(sourceCode.ast, result);
226
+ },
227
+ });
228
+ }
229
+ },
52
230
  };
53
231
  },
54
232
  });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * This rule enforces the use of CSS media queries instead of JavaScript-based breakpoints
3
+ * in React components for better performance and separation of concerns.
4
+ */
5
+ export declare const enforceCssMediaQueries: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"enforceCssMediaQueries", [], import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enforceCssMediaQueries = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ /**
7
+ * This rule enforces the use of CSS media queries instead of JavaScript-based breakpoints
8
+ * in React components for better performance and separation of concerns.
9
+ */
10
+ exports.enforceCssMediaQueries = (0, createRule_1.createRule)({
11
+ name: 'enforce-css-media-queries',
12
+ meta: {
13
+ type: 'suggestion',
14
+ docs: {
15
+ description: 'Enforce CSS media queries over JS breakpoints',
16
+ recommended: 'error',
17
+ },
18
+ messages: {
19
+ enforceCssMediaQueries: 'Use CSS media queries instead of JavaScript breakpoints for responsive design. JavaScript breakpoint handling can cause unnecessary re-renders and impact performance.',
20
+ },
21
+ schema: [],
22
+ },
23
+ defaultOptions: [],
24
+ create(context) {
25
+ return {
26
+ // Check for Material-UI's useMediaQuery and react-responsive imports
27
+ ImportDeclaration(node) {
28
+ // Check for @mui/material useMediaQuery
29
+ if (node.source.value === '@mui/material' &&
30
+ node.specifiers.some((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
31
+ specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
32
+ specifier.imported.name === 'useMediaQuery')) {
33
+ context.report({
34
+ node,
35
+ messageId: 'enforceCssMediaQueries',
36
+ });
37
+ }
38
+ // Check for react-responsive imports
39
+ if (node.source.value === 'react-responsive' ||
40
+ node.source.value.includes('react-responsive/')) {
41
+ context.report({
42
+ node,
43
+ messageId: 'enforceCssMediaQueries',
44
+ });
45
+ }
46
+ // Check for useMobile import from hooks/useMobile
47
+ if (node.source.value.includes('hooks/useMobile') &&
48
+ node.specifiers.some((specifier) => specifier.type === utils_1.AST_NODE_TYPES.ImportSpecifier &&
49
+ specifier.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
50
+ specifier.imported.name === 'useMobile')) {
51
+ context.report({
52
+ node,
53
+ messageId: 'enforceCssMediaQueries',
54
+ });
55
+ }
56
+ },
57
+ // Check for specific import specifiers
58
+ ImportSpecifier(node) {
59
+ if (node.parent &&
60
+ node.parent.type === utils_1.AST_NODE_TYPES.ImportDeclaration &&
61
+ node.imported.type === utils_1.AST_NODE_TYPES.Identifier &&
62
+ (
63
+ // Check for useMediaQuery from @mui/material
64
+ (node.parent.source.value === '@mui/material' &&
65
+ node.imported.name === 'useMediaQuery') ||
66
+ // Check for useMobile from any source
67
+ node.imported.name === 'useMobile')) {
68
+ context.report({
69
+ node,
70
+ messageId: 'enforceCssMediaQueries',
71
+ });
72
+ }
73
+ },
74
+ // Check for useMediaQuery and useMobile calls
75
+ CallExpression(node) {
76
+ if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
77
+ (node.callee.name === 'useMediaQuery' || node.callee.name === 'useMobile')) {
78
+ context.report({
79
+ node,
80
+ messageId: 'enforceCssMediaQueries',
81
+ });
82
+ }
83
+ },
84
+ };
85
+ },
86
+ });
87
+ //# sourceMappingURL=enforce-css-media-queries.js.map
@@ -0,0 +1,9 @@
1
+ export declare const RULE_NAME = "enforce-dynamic-imports";
2
+ type Options = [
3
+ {
4
+ libraries: string[];
5
+ allowImportType?: boolean;
6
+ }
7
+ ];
8
+ declare const _default: import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleModule<"dynamicImportRequired", Options, import("@typescript-eslint/utils/dist/ts-eslint/Rule").RuleListener>;
9
+ export default _default;