@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.
- package/README.md +140 -99
- package/lib/index.js +111 -17
- package/lib/rules/array-methods-this-context.js +20 -2
- package/lib/rules/avoid-utils-directory.js +11 -2
- package/lib/rules/dynamic-https-errors.d.ts +3 -1
- package/lib/rules/dynamic-https-errors.js +48 -10
- package/lib/rules/enforce-assert-safe-object-key.d.ts +8 -0
- package/lib/rules/{enforce-assertSafe-object-key.js → enforce-assert-safe-object-key.js} +51 -95
- package/lib/rules/enforce-assert-throws.d.ts +3 -1
- package/lib/rules/enforce-assert-throws.js +493 -51
- package/lib/rules/enforce-boolean-naming-prefixes.d.ts +3 -1
- package/lib/rules/enforce-boolean-naming-prefixes.js +832 -39
- package/lib/rules/enforce-callable-types.js +4 -4
- package/lib/rules/enforce-callback-memo.js +143 -18
- package/lib/rules/enforce-centralized-mock-firestore.js +4 -1
- package/lib/rules/enforce-console-error.d.ts +3 -0
- package/lib/rules/enforce-console-error.js +250 -0
- package/lib/rules/enforce-css-media-queries.js +24 -46
- package/lib/rules/enforce-dynamic-firebase-imports.js +100 -19
- package/lib/rules/enforce-dynamic-imports.js +1 -1
- package/lib/rules/enforce-exported-function-types.js +3 -3
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.d.ts +2 -0
- package/lib/rules/enforce-fieldpath-syntax-in-docsetter.js +212 -0
- package/lib/rules/enforce-firestore-doc-ref-generic.js +335 -0
- package/lib/rules/enforce-firestore-facade.js +347 -157
- package/lib/rules/enforce-firestore-rules-get-access.d.ts +3 -0
- package/lib/rules/enforce-firestore-rules-get-access.js +109 -0
- package/lib/rules/enforce-global-constants.d.ts +4 -1
- package/lib/rules/enforce-global-constants.js +240 -12
- package/lib/rules/enforce-memoize-async.d.ts +2 -1
- package/lib/rules/enforce-memoize-async.js +96 -27
- package/lib/rules/enforce-memoize-getters.d.ts +3 -0
- package/lib/rules/enforce-memoize-getters.js +123 -0
- package/lib/rules/enforce-microdiff.js +2 -1
- package/lib/rules/enforce-positive-naming.js +5 -7
- package/lib/rules/enforce-props-argument-name.d.ts +2 -7
- package/lib/rules/enforce-props-argument-name.js +165 -151
- package/lib/rules/enforce-props-naming-consistency.d.ts +2 -0
- package/lib/rules/enforce-props-naming-consistency.js +145 -0
- package/lib/rules/enforce-querykey-ts.d.ts +9 -0
- package/lib/rules/enforce-querykey-ts.js +274 -0
- package/lib/rules/enforce-singular-type-names.js +9 -5
- package/lib/rules/enforce-typescript-markdown-code-blocks.d.ts +1 -0
- package/lib/rules/enforce-typescript-markdown-code-blocks.js +96 -0
- package/lib/rules/enforce-verb-noun-naming.js +85 -10
- package/lib/rules/extract-global-constants.js +57 -45
- package/lib/rules/fast-deep-equal-over-microdiff.d.ts +2 -1
- package/lib/rules/fast-deep-equal-over-microdiff.js +356 -78
- package/lib/rules/firestore-transaction-reads-before-writes.d.ts +1 -0
- package/lib/rules/firestore-transaction-reads-before-writes.js +231 -0
- package/lib/rules/generic-starts-with-t.js +8 -2
- package/lib/rules/memoize-root-level-hocs.d.ts +7 -0
- package/lib/rules/memoize-root-level-hocs.js +242 -0
- package/lib/rules/no-array-length-in-deps.d.ts +12 -0
- package/lib/rules/no-array-length-in-deps.js +308 -0
- package/lib/rules/no-async-array-filter.js +5 -2
- package/lib/rules/no-complex-cloud-params.js +15 -2
- package/lib/rules/no-compositing-layer-props.js +4 -2
- package/lib/rules/no-entire-object-hook-deps.d.ts +3 -1
- package/lib/rules/no-entire-object-hook-deps.js +317 -63
- package/lib/rules/no-excessive-parent-chain.d.ts +5 -0
- package/lib/rules/no-excessive-parent-chain.js +273 -0
- package/lib/rules/no-firestore-object-arrays.js +345 -30
- package/lib/rules/no-hungarian.js +13 -13
- package/lib/rules/no-margin-properties.d.ts +7 -1
- package/lib/rules/no-margin-properties.js +16 -3
- package/lib/rules/no-misleading-boolean-prefixes.d.ts +7 -0
- package/lib/rules/no-misleading-boolean-prefixes.js +327 -0
- package/lib/rules/no-overridable-method-calls-in-constructor.d.ts +1 -0
- package/lib/rules/no-overridable-method-calls-in-constructor.js +260 -0
- package/lib/rules/no-passthrough-getters.d.ts +4 -0
- package/lib/rules/no-passthrough-getters.js +170 -0
- package/lib/rules/no-redundant-usecallback-wrapper.d.ts +8 -0
- package/lib/rules/no-redundant-usecallback-wrapper.js +315 -0
- package/lib/rules/no-restricted-properties-fix.d.ts +12 -0
- package/lib/rules/no-restricted-properties-fix.js +172 -0
- package/lib/rules/no-separate-loading-state.d.ts +8 -0
- package/lib/rules/no-separate-loading-state.js +142 -0
- package/lib/rules/no-stale-state-across-await.d.ts +1 -0
- package/lib/rules/no-stale-state-across-await.js +185 -0
- package/lib/rules/no-static-constants-in-dynamic-files.d.ts +3 -0
- package/lib/rules/no-static-constants-in-dynamic-files.js +149 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.d.ts +1 -0
- package/lib/rules/no-try-catch-already-exists-in-transaction.js +342 -0
- package/lib/rules/no-type-assertion-returns.js +6 -0
- package/lib/rules/no-undefined-null-passthrough.js +166 -19
- package/lib/rules/no-unnecessary-verb-suffix.js +0 -2
- package/lib/rules/no-unsafe-firestore-spread.js +8 -5
- package/lib/rules/no-unused-props.d.ts +3 -0
- package/lib/rules/no-unused-props.js +213 -78
- package/lib/rules/optimize-object-boolean-conditions.d.ts +1 -0
- package/lib/rules/optimize-object-boolean-conditions.js +234 -0
- package/lib/rules/parallelize-async-operations.d.ts +8 -0
- package/lib/rules/parallelize-async-operations.js +375 -0
- package/lib/rules/prefer-batch-operations.js +199 -33
- package/lib/rules/prefer-block-comments-for-declarations.js +13 -3
- package/lib/rules/prefer-document-flattening.d.ts +4 -0
- package/lib/rules/prefer-document-flattening.js +239 -0
- package/lib/rules/prefer-field-paths-in-transforms.d.ts +7 -0
- package/lib/rules/prefer-field-paths-in-transforms.js +250 -0
- package/lib/rules/prefer-fragment-component.js +3 -3
- package/lib/rules/prefer-global-router-state-key.d.ts +7 -3
- package/lib/rules/prefer-global-router-state-key.js +230 -31
- package/lib/rules/prefer-next-dynamic.d.ts +9 -0
- package/lib/rules/prefer-next-dynamic.js +366 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.d.ts +1 -0
- package/lib/rules/prefer-nullish-coalescing-boolean-props.js +430 -0
- package/lib/rules/prefer-nullish-coalescing-override.d.ts +6 -0
- package/lib/rules/prefer-nullish-coalescing-override.js +182 -0
- package/lib/rules/prefer-params-over-parent-id.d.ts +5 -0
- package/lib/rules/prefer-params-over-parent-id.js +228 -0
- package/lib/rules/prefer-settings-object.js +23 -15
- package/lib/rules/prefer-type-alias-over-typeof-constant.d.ts +3 -0
- package/lib/rules/prefer-type-alias-over-typeof-constant.js +259 -0
- package/lib/rules/prefer-url-tostring-over-tojson.d.ts +2 -0
- package/lib/rules/prefer-url-tostring-over-tojson.js +166 -0
- package/lib/rules/prefer-use-deep-compare-memo.d.ts +4 -0
- package/lib/rules/prefer-use-deep-compare-memo.js +428 -0
- package/lib/rules/prefer-usecallback-over-usememo-for-functions.js +24 -1
- package/lib/rules/prefer-usememo-over-useeffect-usestate.js +24 -5
- package/lib/rules/react-usememo-should-be-component.d.ts +2 -1
- package/lib/rules/react-usememo-should-be-component.js +144 -78
- package/lib/rules/require-dynamic-firebase-imports.js +2 -1
- package/lib/rules/require-image-optimized.js +1 -1
- package/lib/rules/require-memo.js +11 -1
- package/lib/rules/require-usememo-object-literals.js +7 -1
- package/lib/rules/semantic-function-prefixes.js +38 -18
- package/lib/rules/sync-onwrite-name-func.js +5 -1
- package/lib/rules/test-file-location-enforcement.d.ts +1 -0
- package/lib/rules/test-file-location-enforcement.js +57 -0
- package/lib/rules/use-custom-link.js +6 -1
- package/lib/rules/use-custom-router.js +6 -1
- package/lib/rules/use-latest-callback.d.ts +3 -0
- package/lib/rules/use-latest-callback.js +270 -0
- package/lib/utils/ASTHelpers.js +10 -0
- package/lib/utils/createRule.js +1 -1
- package/lib/utils/graph/ClassGraphSorterReadability.js +1 -1
- package/lib/utils/ruleTester.d.ts +1 -0
- package/lib/utils/ruleTester.js +4 -1
- package/package.json +21 -14
- package/lib/rules/enforce-assertSafe-object-key.d.ts +0 -2
|
@@ -194,6 +194,308 @@ exports.enforceFirestoreDocRefGeneric = (0, createRule_1.createRule)({
|
|
|
194
194
|
}
|
|
195
195
|
return false;
|
|
196
196
|
}
|
|
197
|
+
const isTypedCollectionReferenceCache = new Map();
|
|
198
|
+
function isTypedCollectionReference(node) {
|
|
199
|
+
if (!node)
|
|
200
|
+
return false;
|
|
201
|
+
if (isTypedCollectionReferenceCache.has(node)) {
|
|
202
|
+
return isTypedCollectionReferenceCache.get(node);
|
|
203
|
+
}
|
|
204
|
+
let result = false;
|
|
205
|
+
if (node.type === utils_1.AST_NODE_TYPES.CallExpression &&
|
|
206
|
+
node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression &&
|
|
207
|
+
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
208
|
+
node.callee.property.name === 'collection' &&
|
|
209
|
+
node.typeParameters &&
|
|
210
|
+
node.typeParameters.params.length > 0) {
|
|
211
|
+
result = true;
|
|
212
|
+
}
|
|
213
|
+
else if (node.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
214
|
+
result = checkMemberExpressionForCollectionReference(node);
|
|
215
|
+
}
|
|
216
|
+
else if (node.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
217
|
+
result = checkIdentifierForCollectionReference(node);
|
|
218
|
+
}
|
|
219
|
+
else if (node.type === utils_1.AST_NODE_TYPES.CallExpression) {
|
|
220
|
+
result = checkCallExpressionForCollectionReference(node);
|
|
221
|
+
}
|
|
222
|
+
isTypedCollectionReferenceCache.set(node, result);
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
function checkMemberExpressionForCollectionReference(node) {
|
|
226
|
+
const obj = node.object;
|
|
227
|
+
const property = node.property;
|
|
228
|
+
// Handle this.property access
|
|
229
|
+
if (obj.type === utils_1.AST_NODE_TYPES.ThisExpression &&
|
|
230
|
+
property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
231
|
+
const classNode = findParentClass(node);
|
|
232
|
+
if (classNode) {
|
|
233
|
+
const classProp = classNode.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.PropertyDefinition &&
|
|
234
|
+
member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
235
|
+
member.key.name === property.name);
|
|
236
|
+
if (classProp?.typeAnnotation) {
|
|
237
|
+
return hasCollectionReferenceType(classProp.typeAnnotation.typeAnnotation);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Handle nested property access like this.collections.tasks
|
|
242
|
+
if (obj.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
243
|
+
const parentType = getTypeOfMemberExpression(obj);
|
|
244
|
+
if (parentType && property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
245
|
+
// Check if the parent object has a property with CollectionReference type
|
|
246
|
+
return checkObjectPropertyForCollectionReference(parentType, property.name);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// Handle identifier.property access
|
|
250
|
+
if (obj.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
251
|
+
property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
252
|
+
const objType = getTypeOfIdentifier(obj);
|
|
253
|
+
if (objType) {
|
|
254
|
+
return checkObjectPropertyForCollectionReference(objType, property.name);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// Handle getter methods like this.collection where collection is a getter
|
|
258
|
+
if (obj.type === utils_1.AST_NODE_TYPES.ThisExpression &&
|
|
259
|
+
property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
260
|
+
const classNode = findParentClass(node);
|
|
261
|
+
if (classNode) {
|
|
262
|
+
const getter = classNode.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
263
|
+
member.kind === 'get' &&
|
|
264
|
+
member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
265
|
+
member.key.name === property.name);
|
|
266
|
+
if (getter) {
|
|
267
|
+
// Check explicit return type
|
|
268
|
+
if (getter.value.returnType) {
|
|
269
|
+
return hasCollectionReferenceType(getter.value.returnType.typeAnnotation);
|
|
270
|
+
}
|
|
271
|
+
// Check return statement to infer type
|
|
272
|
+
if (getter.value.body &&
|
|
273
|
+
getter.value.body.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
274
|
+
const returnStmt = getter.value.body.body.find((stmt) => stmt.type === utils_1.AST_NODE_TYPES.ReturnStatement);
|
|
275
|
+
if (returnStmt?.argument?.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
276
|
+
return checkMemberExpressionForCollectionReference(returnStmt.argument);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
function checkIdentifierForCollectionReference(node) {
|
|
285
|
+
// Check function parameters
|
|
286
|
+
const functionParam = findFunctionParameter(node);
|
|
287
|
+
if (functionParam &&
|
|
288
|
+
'typeAnnotation' in functionParam &&
|
|
289
|
+
functionParam.typeAnnotation) {
|
|
290
|
+
return hasCollectionReferenceType(functionParam.typeAnnotation.typeAnnotation);
|
|
291
|
+
}
|
|
292
|
+
// Check variable declarations in current scope and parent scopes
|
|
293
|
+
if (findVariableDeclaration(node)) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
function checkCallExpressionForCollectionReference(node) {
|
|
299
|
+
// Check if this is a method call that returns CollectionReference
|
|
300
|
+
if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression) {
|
|
301
|
+
const obj = node.callee.object;
|
|
302
|
+
const property = node.callee.property;
|
|
303
|
+
if (property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
304
|
+
// Check if this is a getter or method that returns CollectionReference
|
|
305
|
+
if (obj.type === utils_1.AST_NODE_TYPES.ThisExpression) {
|
|
306
|
+
const classNode = findParentClass(node);
|
|
307
|
+
if (classNode) {
|
|
308
|
+
const method = classNode.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.MethodDefinition &&
|
|
309
|
+
member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
310
|
+
member.key.name === property.name &&
|
|
311
|
+
!!member.value.returnType);
|
|
312
|
+
if (method?.value.returnType) {
|
|
313
|
+
return hasCollectionReferenceType(method.value.returnType.typeAnnotation);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
function getTypeOfMemberExpression(node) {
|
|
322
|
+
if (node.object.type === utils_1.AST_NODE_TYPES.ThisExpression &&
|
|
323
|
+
node.property.type === utils_1.AST_NODE_TYPES.Identifier) {
|
|
324
|
+
const classNode = findParentClass(node);
|
|
325
|
+
if (classNode) {
|
|
326
|
+
const classProp = classNode.body.body.find((member) => member.type === utils_1.AST_NODE_TYPES.PropertyDefinition &&
|
|
327
|
+
member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
328
|
+
'name' in node.property &&
|
|
329
|
+
member.key.name === node.property.name);
|
|
330
|
+
return classProp?.typeAnnotation?.typeAnnotation || null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
function getTypeOfIdentifier(node) {
|
|
336
|
+
const functionParam = findFunctionParameter(node);
|
|
337
|
+
if (functionParam &&
|
|
338
|
+
'typeAnnotation' in functionParam &&
|
|
339
|
+
functionParam.typeAnnotation) {
|
|
340
|
+
return functionParam.typeAnnotation.typeAnnotation;
|
|
341
|
+
}
|
|
342
|
+
const varDecl = findVariableDeclarationNode(node);
|
|
343
|
+
if (varDecl?.typeAnnotation) {
|
|
344
|
+
return varDecl.typeAnnotation.typeAnnotation;
|
|
345
|
+
}
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
function checkObjectPropertyForCollectionReference(objectType, propertyName) {
|
|
349
|
+
if (objectType.type === utils_1.AST_NODE_TYPES.TSTypeLiteral) {
|
|
350
|
+
const property = objectType.members.find((member) => member.type === utils_1.AST_NODE_TYPES.TSPropertySignature &&
|
|
351
|
+
member.key.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
352
|
+
member.key.name === propertyName &&
|
|
353
|
+
!!member.typeAnnotation);
|
|
354
|
+
if (property?.typeAnnotation) {
|
|
355
|
+
return hasCollectionReferenceType(property.typeAnnotation.typeAnnotation);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Handle Record<string, CollectionReference<T>> types
|
|
359
|
+
if (objectType.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
360
|
+
objectType.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
361
|
+
objectType.typeName.name === 'Record' &&
|
|
362
|
+
objectType.typeParameters &&
|
|
363
|
+
objectType.typeParameters.params.length === 2) {
|
|
364
|
+
const valueType = objectType.typeParameters.params[1];
|
|
365
|
+
return hasCollectionReferenceType(valueType);
|
|
366
|
+
}
|
|
367
|
+
// Handle array types like CollectionReference<T>[]
|
|
368
|
+
if (objectType.type === utils_1.AST_NODE_TYPES.TSArrayType) {
|
|
369
|
+
return hasCollectionReferenceType(objectType.elementType);
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
function findFunctionParameter(node) {
|
|
374
|
+
const isFunctionNode = (n) => n.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
375
|
+
n.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
376
|
+
n.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression;
|
|
377
|
+
const findParamInFunction = (func) => {
|
|
378
|
+
const param = func.params.find((p) => p.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
379
|
+
p.name === node.name &&
|
|
380
|
+
'typeAnnotation' in p &&
|
|
381
|
+
p.typeAnnotation !== undefined);
|
|
382
|
+
return param || null;
|
|
383
|
+
};
|
|
384
|
+
const functionScopes = [];
|
|
385
|
+
let current = node;
|
|
386
|
+
while (current) {
|
|
387
|
+
if (isFunctionNode(current)) {
|
|
388
|
+
functionScopes.push(current);
|
|
389
|
+
}
|
|
390
|
+
current = current.parent;
|
|
391
|
+
}
|
|
392
|
+
for (const func of functionScopes) {
|
|
393
|
+
const param = findParamInFunction(func);
|
|
394
|
+
if (param) {
|
|
395
|
+
return param;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
function findVariableDeclaration(node) {
|
|
401
|
+
let current = node;
|
|
402
|
+
while (current) {
|
|
403
|
+
// Check in current scope
|
|
404
|
+
if (current.type === utils_1.AST_NODE_TYPES.Program ||
|
|
405
|
+
current.type === utils_1.AST_NODE_TYPES.BlockStatement ||
|
|
406
|
+
current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
407
|
+
current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
408
|
+
current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
409
|
+
const varDecl = findVariableInScope(current, node.name);
|
|
410
|
+
if (varDecl && varDecl.typeAnnotation) {
|
|
411
|
+
return hasCollectionReferenceType(varDecl.typeAnnotation.typeAnnotation);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
current = current.parent;
|
|
415
|
+
}
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
function findVariableDeclarationNode(node) {
|
|
419
|
+
let current = node;
|
|
420
|
+
while (current) {
|
|
421
|
+
if (current.type === utils_1.AST_NODE_TYPES.Program ||
|
|
422
|
+
current.type === utils_1.AST_NODE_TYPES.BlockStatement ||
|
|
423
|
+
current.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
424
|
+
current.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
|
|
425
|
+
current.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
426
|
+
const varDecl = findVariableInScope(current, node.name);
|
|
427
|
+
if (varDecl) {
|
|
428
|
+
return varDecl;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
current = current.parent;
|
|
432
|
+
}
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
function findVariableInScope(scope, varName) {
|
|
436
|
+
const body = getNodeBody(scope);
|
|
437
|
+
if (!body)
|
|
438
|
+
return null;
|
|
439
|
+
for (const stmt of body) {
|
|
440
|
+
if (stmt.type === utils_1.AST_NODE_TYPES.VariableDeclaration) {
|
|
441
|
+
for (const decl of stmt.declarations) {
|
|
442
|
+
if (decl.type === utils_1.AST_NODE_TYPES.VariableDeclarator &&
|
|
443
|
+
decl.id.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
444
|
+
decl.id.name === varName &&
|
|
445
|
+
decl.id.typeAnnotation) {
|
|
446
|
+
return decl.id;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return null;
|
|
452
|
+
}
|
|
453
|
+
function getNodeBody(node) {
|
|
454
|
+
if (node.type === utils_1.AST_NODE_TYPES.Program) {
|
|
455
|
+
return node.body;
|
|
456
|
+
}
|
|
457
|
+
if (node.type === utils_1.AST_NODE_TYPES.BlockStatement) {
|
|
458
|
+
return node.body;
|
|
459
|
+
}
|
|
460
|
+
if (node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
|
|
461
|
+
node.type === utils_1.AST_NODE_TYPES.FunctionExpression) {
|
|
462
|
+
return node.body?.body || null;
|
|
463
|
+
}
|
|
464
|
+
if (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
465
|
+
return node.body.type === utils_1.AST_NODE_TYPES.BlockStatement
|
|
466
|
+
? node.body.body
|
|
467
|
+
: null;
|
|
468
|
+
}
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
function hasCollectionReferenceType(typeNode) {
|
|
472
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSTypeReference &&
|
|
473
|
+
((typeNode.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
474
|
+
typeNode.typeName.name === 'CollectionReference') ||
|
|
475
|
+
(typeNode.typeName.type === utils_1.AST_NODE_TYPES.TSQualifiedName &&
|
|
476
|
+
typeNode.typeName.right.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
477
|
+
typeNode.typeName.right.name === 'CollectionReference')) &&
|
|
478
|
+
typeNode.typeParameters &&
|
|
479
|
+
typeNode.typeParameters.params.length > 0) {
|
|
480
|
+
return true;
|
|
481
|
+
}
|
|
482
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSUnionType) {
|
|
483
|
+
return typeNode.types.some(hasCollectionReferenceType);
|
|
484
|
+
}
|
|
485
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSIntersectionType) {
|
|
486
|
+
return typeNode.types.some(hasCollectionReferenceType);
|
|
487
|
+
}
|
|
488
|
+
// TSParenthesizedType may appear even though AST_NODE_TYPES omits it
|
|
489
|
+
if (typeNode.type === 'TSParenthesizedType') {
|
|
490
|
+
const inner = typeNode
|
|
491
|
+
.typeAnnotation;
|
|
492
|
+
return hasCollectionReferenceType(inner);
|
|
493
|
+
}
|
|
494
|
+
if (typeNode.type === utils_1.AST_NODE_TYPES.TSArrayType) {
|
|
495
|
+
return hasCollectionReferenceType(typeNode.elementType);
|
|
496
|
+
}
|
|
497
|
+
return false;
|
|
498
|
+
}
|
|
197
499
|
return {
|
|
198
500
|
TSTypeReference(node) {
|
|
199
501
|
if (node.typeName.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
@@ -231,6 +533,39 @@ exports.enforceFirestoreDocRefGeneric = (0, createRule_1.createRule)({
|
|
|
231
533
|
node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
232
534
|
node.callee.property.name === 'doc') {
|
|
233
535
|
const typeAnnotation = node.typeParameters;
|
|
536
|
+
const isOnTypedCollection = isTypedCollectionReference(node.callee.object);
|
|
537
|
+
// If this is a .doc() call on a typed CollectionReference,
|
|
538
|
+
// only check for invalid generics, not missing generics
|
|
539
|
+
if (isOnTypedCollection) {
|
|
540
|
+
if (typeAnnotation && hasInvalidType(typeAnnotation.params[0])) {
|
|
541
|
+
context.report({
|
|
542
|
+
node,
|
|
543
|
+
messageId: 'invalidGeneric',
|
|
544
|
+
data: { type: 'DocumentReference' },
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
return; // Skip the missing generic check for typed CollectionReference.doc() calls
|
|
548
|
+
}
|
|
549
|
+
// For standalone doc() calls or calls on untyped collections
|
|
550
|
+
if (!typeAnnotation) {
|
|
551
|
+
context.report({
|
|
552
|
+
node,
|
|
553
|
+
messageId: 'missingGeneric',
|
|
554
|
+
data: { type: 'DocumentReference' },
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
else if (hasInvalidType(typeAnnotation.params[0])) {
|
|
558
|
+
context.report({
|
|
559
|
+
node,
|
|
560
|
+
messageId: 'invalidGeneric',
|
|
561
|
+
data: { type: 'DocumentReference' },
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
// Check for standalone doc() function calls
|
|
566
|
+
else if (node.callee.type === utils_1.AST_NODE_TYPES.Identifier &&
|
|
567
|
+
node.callee.name === 'doc') {
|
|
568
|
+
const typeAnnotation = node.typeParameters;
|
|
234
569
|
if (!typeAnnotation) {
|
|
235
570
|
context.report({
|
|
236
571
|
node,
|