@blumintinc/eslint-plugin-blumint 1.12.6 → 1.13.0

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 (141) hide show
  1. package/README.md +140 -99
  2. package/lib/index.js +111 -17
  3. package/lib/rules/array-methods-this-context.js +20 -2
  4. package/lib/rules/avoid-utils-directory.js +11 -2
  5. package/lib/rules/dynamic-https-errors.d.ts +3 -1
  6. package/lib/rules/dynamic-https-errors.js +48 -10
  7. package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
  8. package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +51 -95
  9. package/lib/rules/enforce-assert-throws.d.ts +3 -1
  10. package/lib/rules/enforce-assert-throws.js +493 -51
  11. package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
  12. package/lib/rules/enforce-boolean-naming-prefixes.js +832 -39
  13. package/lib/rules/enforce-callable-types.js +4 -4
  14. package/lib/rules/enforce-callback-memo.js +143 -18
  15. package/lib/rules/enforce-centralized-mock-firestore.js +4 -1
  16. package/lib/rules/enforce-console-error.d.ts +3 -0
  17. package/lib/rules/enforce-console-error.js +250 -0
  18. package/lib/rules/enforce-css-media-queries.js +24 -46
  19. package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
  20. package/lib/rules/enforce-dynamic-imports.js +1 -1
  21. package/lib/rules/enforce-exported-function-types.js +3 -3
  22. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
  23. package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +212 -0
  24. package/lib/rules/enforce-firestore-doc-ref-generic.js +335 -0
  25. package/lib/rules/enforce-firestore-facade.js +347 -157
  26. package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
  27. package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
  28. package/lib/rules/enforce-global-constants.d.ts +4 -1
  29. package/lib/rules/enforce-global-constants.js +240 -12
  30. package/lib/rules/enforce-memoize-async.d.ts +2 -1
  31. package/lib/rules/enforce-memoize-async.js +96 -27
  32. package/lib/rules/enforce-memoize-getters.d.ts +3 -0
  33. package/lib/rules/enforce-memoize-getters.js +123 -0
  34. package/lib/rules/enforce-microdiff.js +2 -1
  35. package/lib/rules/enforce-positive-naming.js +5 -7
  36. package/lib/rules/enforce-props-argument-name.d.ts +2 -7
  37. package/lib/rules/enforce-props-argument-name.js +165 -151
  38. package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
  39. package/lib/rules/enforce-props-naming-consistency.js +145 -0
  40. package/lib/rules/enforce-querykey-ts.d.ts +9 -0
  41. package/lib/rules/enforce-querykey-ts.js +274 -0
  42. package/lib/rules/enforce-singular-type-names.js +9 -5
  43. package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
  44. package/lib/rules/enforce-typescript-markdown-code-blocks.js +96 -0
  45. package/lib/rules/enforce-verb-noun-naming.js +85 -10
  46. package/lib/rules/extract-global-constants.js +57 -45
  47. package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
  48. package/lib/rules/fast-deep-equal-over-microdiff.js +356 -78
  49. package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
  50. package/lib/rules/firestore-transaction-reads-before-writes.js +231 -0
  51. package/lib/rules/generic-starts-with-t.js +8 -2
  52. package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
  53. package/lib/rules/memoize-root-level-hocs.js +242 -0
  54. package/lib/rules/no-array-length-in-deps.d.ts +12 -0
  55. package/lib/rules/no-array-length-in-deps.js +308 -0
  56. package/lib/rules/no-async-array-filter.js +5 -2
  57. package/lib/rules/no-complex-cloud-params.js +15 -2
  58. package/lib/rules/no-compositing-layer-props.js +4 -2
  59. package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
  60. package/lib/rules/no-entire-object-hook-deps.js +317 -63
  61. package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
  62. package/lib/rules/no-excessive-parent-chain.js +273 -0
  63. package/lib/rules/no-firestore-object-arrays.js +345 -30
  64. package/lib/rules/no-hungarian.js +13 -13
  65. package/lib/rules/no-margin-properties.d.ts +7 -1
  66. package/lib/rules/no-margin-properties.js +16 -3
  67. package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
  68. package/lib/rules/no-misleading-boolean-prefixes.js +327 -0
  69. package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
  70. package/lib/rules/no-overridable-method-calls-in-constructor.js +260 -0
  71. package/lib/rules/no-passthrough-getters.d.ts +4 -0
  72. package/lib/rules/no-passthrough-getters.js +170 -0
  73. package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
  74. package/lib/rules/no-redundant-usecallback-wrapper.js +315 -0
  75. package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
  76. package/lib/rules/no-restricted-properties-fix.js +172 -0
  77. package/lib/rules/no-separate-loading-state.d.ts +8 -0
  78. package/lib/rules/no-separate-loading-state.js +142 -0
  79. package/lib/rules/no-stale-state-across-await.d.ts +1 -0
  80. package/lib/rules/no-stale-state-across-await.js +185 -0
  81. package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
  82. package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
  83. package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
  84. package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
  85. package/lib/rules/no-type-assertion-returns.js +6 -0
  86. package/lib/rules/no-undefined-null-passthrough.js +166 -19
  87. package/lib/rules/no-unnecessary-verb-suffix.js +0 -2
  88. package/lib/rules/no-unsafe-firestore-spread.js +8 -5
  89. package/lib/rules/no-unused-props.d.ts +3 -0
  90. package/lib/rules/no-unused-props.js +213 -78
  91. package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
  92. package/lib/rules/optimize-object-boolean-conditions.js +234 -0
  93. package/lib/rules/parallelize-async-operations.d.ts +8 -0
  94. package/lib/rules/parallelize-async-operations.js +375 -0
  95. package/lib/rules/prefer-batch-operations.js +199 -33
  96. package/lib/rules/prefer-block-comments-for-declarations.js +13 -3
  97. package/lib/rules/prefer-document-flattening.d.ts +4 -0
  98. package/lib/rules/prefer-document-flattening.js +239 -0
  99. package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
  100. package/lib/rules/prefer-field-paths-in-transforms.js +250 -0
  101. package/lib/rules/prefer-fragment-component.js +3 -3
  102. package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
  103. package/lib/rules/prefer-global-router-state-key.js +230 -31
  104. package/lib/rules/prefer-next-dynamic.d.ts +9 -0
  105. package/lib/rules/prefer-next-dynamic.js +366 -0
  106. package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
  107. package/lib/rules/prefer-nullish-coalescing-boolean-props.js +430 -0
  108. package/lib/rules/prefer-nullish-coalescing-override.d.ts +6 -0
  109. package/lib/rules/prefer-nullish-coalescing-override.js +182 -0
  110. package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
  111. package/lib/rules/prefer-params-over-parent-id.js +228 -0
  112. package/lib/rules/prefer-settings-object.js +23 -15
  113. package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
  114. package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
  115. package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
  116. package/lib/rules/prefer-url-tostring-over-tojson.js +166 -0
  117. package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
  118. package/lib/rules/prefer-use-deep-compare-memo.js +428 -0
  119. package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +24 -1
  120. package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
  121. package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
  122. package/lib/rules/react-usememo-should-be-component.js +144 -78
  123. package/lib/rules/require-dynamic-firebase-imports.js +2 -1
  124. package/lib/rules/require-image-optimized.js +1 -1
  125. package/lib/rules/require-memo.js +11 -1
  126. package/lib/rules/require-usememo-object-literals.js +7 -1
  127. package/lib/rules/semantic-function-prefixes.js +38 -18
  128. package/lib/rules/sync-onwrite-name-func.js +5 -1
  129. package/lib/rules/test-file-location-enforcement.d.ts +1 -0
  130. package/lib/rules/test-file-location-enforcement.js +57 -0
  131. package/lib/rules/use-custom-link.js +6 -1
  132. package/lib/rules/use-custom-router.js +6 -1
  133. package/lib/rules/use-latest-callback.d.ts +3 -0
  134. package/lib/rules/use-latest-callback.js +270 -0
  135. package/lib/utils/ASTHelpers.js +10 -0
  136. package/lib/utils/createRule.js +1 -1
  137. package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
  138. package/lib/utils/ruleTester.d.ts +1 -0
  139. package/lib/utils/ruleTester.js +4 -1
  140. package/package.json +21 -14
  141. package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.noExcessiveParentChain = void 0;
4
+ const utils_1 = require("@typescript-eslint/utils");
5
+ const createRule_1 = require("../utils/createRule");
6
+ // Maximum number of consecutive .parent calls allowed before warning
7
+ const DEFAULT_MAX_PARENT_CHAIN_LENGTH = 2;
8
+ // Handler types that this rule applies to
9
+ const HANDLER_TYPES = new Set([
10
+ 'DocumentChangeHandler',
11
+ 'DocumentChangeHandlerTransaction',
12
+ 'RealtimeDbChangeHandler',
13
+ 'RealtimeDbChangeHandlerTransaction',
14
+ ]);
15
+ exports.noExcessiveParentChain = (0, createRule_1.createRule)({
16
+ name: 'no-excessive-parent-chain',
17
+ meta: {
18
+ type: 'suggestion',
19
+ hasSuggestions: true,
20
+ docs: {
21
+ description: 'Discourage excessive use of the ref.parent property chain in Firestore and RealtimeDB change handlers',
22
+ recommended: 'error',
23
+ },
24
+ schema: [
25
+ {
26
+ type: 'object',
27
+ properties: {
28
+ max: {
29
+ type: 'integer',
30
+ minimum: 1,
31
+ },
32
+ },
33
+ additionalProperties: false,
34
+ },
35
+ ],
36
+ messages: {
37
+ excessiveParentChain: 'Avoid using long chains of .parent calls ({{count}} found). Use the params object from the event parameter instead, which provides type-safe access to path segments.',
38
+ },
39
+ },
40
+ defaultOptions: [{}],
41
+ create(context) {
42
+ const maxParentChainLength = context.options[0]?.max ?? DEFAULT_MAX_PARENT_CHAIN_LENGTH;
43
+ // Track variables that contain event data
44
+ const eventDataVariables = new Map();
45
+ const eventIdentifiers = new Set();
46
+ const HANDLER_PARAM_SOURCE = '__handler_param__';
47
+ const recordEventIdentifier = (name) => {
48
+ eventIdentifiers.add(name);
49
+ };
50
+ const getRootIdentifier = (node) => {
51
+ let current = node;
52
+ while (current.type === utils_1.AST_NODE_TYPES.MemberExpression) {
53
+ current = current.object;
54
+ }
55
+ return current.type === utils_1.AST_NODE_TYPES.Identifier ? current.name : null;
56
+ };
57
+ const hasRefProperty = (node) => {
58
+ let current = node;
59
+ while (current) {
60
+ if (current.property.type === utils_1.AST_NODE_TYPES.Identifier &&
61
+ current.property.name === 'ref') {
62
+ return true;
63
+ }
64
+ current =
65
+ current.object.type === utils_1.AST_NODE_TYPES.MemberExpression
66
+ ? current.object
67
+ : null;
68
+ }
69
+ return false;
70
+ };
71
+ const registerHandlerParams = (node) => {
72
+ for (const param of node.params) {
73
+ if (param.type === utils_1.AST_NODE_TYPES.Identifier) {
74
+ recordEventIdentifier(param.name);
75
+ }
76
+ if (param.type === utils_1.AST_NODE_TYPES.ObjectPattern) {
77
+ for (const prop of param.properties) {
78
+ if (prop.type === utils_1.AST_NODE_TYPES.Property &&
79
+ prop.key.type === utils_1.AST_NODE_TYPES.Identifier &&
80
+ prop.value.type === utils_1.AST_NODE_TYPES.Identifier &&
81
+ prop.key.name === 'data') {
82
+ eventDataVariables.set(prop.value.name, HANDLER_PARAM_SOURCE);
83
+ recordEventIdentifier(HANDLER_PARAM_SOURCE);
84
+ }
85
+ }
86
+ }
87
+ }
88
+ };
89
+ // Check if a function is one of our handler types
90
+ function isHandlerFunction(node) {
91
+ if (node.type !== utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
92
+ node.type !== utils_1.AST_NODE_TYPES.FunctionExpression) {
93
+ return false;
94
+ }
95
+ // Check if the function is assigned to a variable with a type annotation
96
+ const parent = node.parent;
97
+ if (parent?.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
98
+ parent.id.type === utils_1.AST_NODE_TYPES.Identifier) {
99
+ // Check for type annotation
100
+ if (parent.id.typeAnnotation?.typeAnnotation) {
101
+ const typeNode = parent.id.typeAnnotation.typeAnnotation;
102
+ // Check if it's a reference to one of our handler types
103
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
104
+ typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
105
+ HANDLER_TYPES.has(typeNode.typeName.name)) {
106
+ return true;
107
+ }
108
+ // Check for generic types that might be handlers
109
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
110
+ typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier) {
111
+ // Try to resolve the type name to see if it's one of our handler types
112
+ const scope = context.getScope();
113
+ const typeName = typeNode.typeName.name;
114
+ const variable = scope.variables.find((v) => v.name === typeName);
115
+ if (variable && variable.defs.length > 0) {
116
+ const def = variable.defs[0];
117
+ if (def.node.type === utils_1.AST_NODE_TYPES.TSTypeAliasDeclaration &&
118
+ def.node.typeAnnotation.type ===
119
+ utils_1.AST_NODE_TYPES.TSTypeReference &&
120
+ def.node.typeAnnotation.typeName.type ===
121
+ utils_1.AST_NODE_TYPES.Identifier &&
122
+ HANDLER_TYPES.has(def.node.typeAnnotation.typeName.name)) {
123
+ return true;
124
+ }
125
+ }
126
+ }
127
+ }
128
+ }
129
+ // Check if the function is exported with a type annotation
130
+ if (parent?.type === utils_1.AST_NODE_TYPES.ExportNamedDeclaration &&
131
+ parent.declaration?.type === utils_1.AST_NODE_TYPES.VariableDeclaration &&
132
+ parent.declaration.declarations[0]?.id.type ===
133
+ utils_1.AST_NODE_TYPES.Identifier &&
134
+ parent.declaration.declarations[0].id.typeAnnotation?.typeAnnotation) {
135
+ const typeNode = parent.declaration.declarations[0].id.typeAnnotation.typeAnnotation;
136
+ if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
137
+ typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
138
+ HANDLER_TYPES.has(typeNode.typeName.name)) {
139
+ return true;
140
+ }
141
+ }
142
+ return false;
143
+ }
144
+ // Count consecutive .parent calls in a member expression chain
145
+ function countParentChain(node) {
146
+ let count = 1; // Start with 1 for the current .parent
147
+ let current = node.object;
148
+ // Traverse the chain of member expressions
149
+ while (current.type === utils_1.AST_NODE_TYPES.MemberExpression &&
150
+ current.property.type === utils_1.AST_NODE_TYPES.Identifier &&
151
+ current.property.name === 'parent') {
152
+ count++;
153
+ current = current.object;
154
+ }
155
+ return count;
156
+ }
157
+ // This function has been removed as it's no longer needed
158
+ return {
159
+ // Register handler parameters to capture destructured event data
160
+ 'ArrowFunctionExpression, FunctionExpression'(node) {
161
+ if (isHandlerFunction(node)) {
162
+ registerHandlerParams(node);
163
+ }
164
+ },
165
+ // Track variable assignments that contain event data
166
+ VariableDeclarator(node) {
167
+ if (node.id.type === utils_1.AST_NODE_TYPES.Identifier && node.init) {
168
+ // Track direct event.data assignments
169
+ if (node.init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
170
+ node.init.property.type === utils_1.AST_NODE_TYPES.Identifier &&
171
+ node.init.property.name === 'data' &&
172
+ node.init.object.type === utils_1.AST_NODE_TYPES.Identifier) {
173
+ // Store the variable name and the source object (event)
174
+ eventDataVariables.set(node.id.name, node.init.object.name);
175
+ recordEventIdentifier(node.init.object.name);
176
+ }
177
+ // Track assignments from other tracked variables
178
+ if (node.init.type === utils_1.AST_NODE_TYPES.Identifier &&
179
+ eventDataVariables.has(node.init.name)) {
180
+ // Store the variable name with the same source as the original variable
181
+ eventDataVariables.set(node.id.name, eventDataVariables.get(node.init.name) || '');
182
+ const source = eventDataVariables.get(node.init.name);
183
+ if (source) {
184
+ recordEventIdentifier(source);
185
+ }
186
+ }
187
+ // Track assignments from event data properties
188
+ if (node.init.type === utils_1.AST_NODE_TYPES.MemberExpression &&
189
+ node.init.object.type === utils_1.AST_NODE_TYPES.Identifier &&
190
+ eventDataVariables.has(node.init.object.name)) {
191
+ // Store the variable name with the same source as the original variable
192
+ eventDataVariables.set(node.id.name, eventDataVariables.get(node.init.object.name) || '');
193
+ const source = eventDataVariables.get(node.init.object.name);
194
+ if (source) {
195
+ recordEventIdentifier(source);
196
+ }
197
+ }
198
+ }
199
+ // Also track destructuring assignments
200
+ if (node.id.type === utils_1.AST_NODE_TYPES.ObjectPattern &&
201
+ node.init?.type === utils_1.AST_NODE_TYPES.Identifier) {
202
+ const eventSource = eventDataVariables.get(node.init.name) ||
203
+ (eventIdentifiers.has(node.init.name) ? node.init.name : null);
204
+ for (const property of node.id.properties) {
205
+ if (property.type === utils_1.AST_NODE_TYPES.Property &&
206
+ property.key.type === utils_1.AST_NODE_TYPES.Identifier &&
207
+ property.value.type === utils_1.AST_NODE_TYPES.Identifier &&
208
+ eventSource) {
209
+ const targetName = property.value.name;
210
+ eventDataVariables.set(targetName, eventSource);
211
+ recordEventIdentifier(eventSource);
212
+ }
213
+ }
214
+ }
215
+ },
216
+ // Check for excessive parent chains in member expressions
217
+ MemberExpression(node) {
218
+ // Only check for .parent chains
219
+ if (node.property.type !== utils_1.AST_NODE_TYPES.Identifier ||
220
+ node.property.name !== 'parent') {
221
+ return;
222
+ }
223
+ // Count the number of consecutive .parent calls
224
+ const parentCount = countParentChain(node);
225
+ if (parentCount <= maxParentChainLength) {
226
+ return;
227
+ }
228
+ // Check if we're in a handler function
229
+ let current = node;
230
+ let inHandler = false;
231
+ while (current) {
232
+ if (current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression ||
233
+ current.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
234
+ if (isHandlerFunction(current)) {
235
+ inHandler = true;
236
+ break;
237
+ }
238
+ }
239
+ current = current.parent;
240
+ }
241
+ if (!inHandler) {
242
+ return;
243
+ }
244
+ // Only report when the chain originates from tracked event data and contains a ref segment
245
+ const rootIdentifier = getRootIdentifier(node);
246
+ if (!rootIdentifier) {
247
+ return;
248
+ }
249
+ if (!hasRefProperty(node) ||
250
+ (!eventDataVariables.has(rootIdentifier) &&
251
+ !eventIdentifiers.has(rootIdentifier))) {
252
+ return;
253
+ }
254
+ context.report({
255
+ node,
256
+ messageId: 'excessiveParentChain',
257
+ data: {
258
+ count: parentCount,
259
+ },
260
+ suggest: [
261
+ {
262
+ messageId: 'excessiveParentChain',
263
+ fix(fixer) {
264
+ return fixer.replaceText(node, 'event.params');
265
+ },
266
+ },
267
+ ],
268
+ });
269
+ },
270
+ };
271
+ },
272
+ });
273
+ //# sourceMappingURL=no-excessive-parent-chain.js.map