@tanstack/eslint-plugin-query 5.94.5 → 5.95.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/build/legacy/_tsup-dts-rollup.d.cts +57 -3
- package/build/legacy/_tsup-dts-rollup.d.ts +57 -3
- package/build/legacy/{chunk-HD2KSEKJ.js → chunk-T44AUBX4.js} +289 -90
- package/build/legacy/chunk-T44AUBX4.js.map +1 -0
- package/build/legacy/index.cjs +288 -89
- package/build/legacy/index.cjs.map +1 -1
- package/build/legacy/index.js +1 -1
- package/build/legacy/rules.cjs +288 -89
- package/build/legacy/rules.cjs.map +1 -1
- package/build/legacy/rules.js +1 -1
- package/build/modern/_tsup-dts-rollup.d.cts +57 -3
- package/build/modern/_tsup-dts-rollup.d.ts +57 -3
- package/build/modern/{chunk-XO2SGL7P.js → chunk-XH4ABCYA.js} +288 -90
- package/build/modern/chunk-XH4ABCYA.js.map +1 -0
- package/build/modern/index.cjs +287 -89
- package/build/modern/index.cjs.map +1 -1
- package/build/modern/index.js +1 -1
- package/build/modern/rules.cjs +287 -89
- package/build/modern/rules.cjs.map +1 -1
- package/build/modern/rules.js +1 -1
- package/package.json +1 -1
- package/src/rules/exhaustive-deps/exhaustive-deps.rule.ts +136 -90
- package/src/rules/exhaustive-deps/exhaustive-deps.utils.ts +248 -22
- package/src/utils/ast-utils.ts +2 -25
- package/build/legacy/chunk-HD2KSEKJ.js.map +0 -1
- package/build/modern/chunk-XO2SGL7P.js.map +0 -1
package/build/modern/rules.cjs
CHANGED
|
@@ -130,21 +130,6 @@ var ASTUtils = {
|
|
|
130
130
|
}
|
|
131
131
|
return identifiers;
|
|
132
132
|
},
|
|
133
|
-
isAncestorIsCallee(identifier) {
|
|
134
|
-
let previousNode = identifier;
|
|
135
|
-
let currentNode = identifier.parent;
|
|
136
|
-
while (currentNode !== void 0) {
|
|
137
|
-
if (currentNode.type === import_utils.AST_NODE_TYPES.CallExpression && currentNode.callee === previousNode) {
|
|
138
|
-
return true;
|
|
139
|
-
}
|
|
140
|
-
if (currentNode.type !== import_utils.AST_NODE_TYPES.MemberExpression) {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
previousNode = currentNode;
|
|
144
|
-
currentNode = currentNode.parent;
|
|
145
|
-
}
|
|
146
|
-
return false;
|
|
147
|
-
},
|
|
148
133
|
traverseUpOnly(identifier, allowedNodeTypes) {
|
|
149
134
|
const parent = identifier.parent;
|
|
150
135
|
if (parent !== void 0 && allowedNodeTypes.includes(parent.type)) {
|
|
@@ -365,7 +350,7 @@ var ExhaustiveDepsUtils = {
|
|
|
365
350
|
const { sourceCode, reference, scopeManager, node, filename } = params;
|
|
366
351
|
const component = ASTUtils.getFunctionAncestor(sourceCode, node);
|
|
367
352
|
const queryFnScope = scopeManager.acquire(node);
|
|
368
|
-
if (queryFnScope === null) {
|
|
353
|
+
if (queryFnScope === null || reference.isValueReference === false) {
|
|
369
354
|
return false;
|
|
370
355
|
}
|
|
371
356
|
let currentScope = reference.resolved?.scope ?? null;
|
|
@@ -397,15 +382,57 @@ var ExhaustiveDepsUtils = {
|
|
|
397
382
|
}
|
|
398
383
|
return reference.identifier.name !== "undefined" && reference.identifier.parent.type !== import_utils3.AST_NODE_TYPES.NewExpression && !ExhaustiveDepsUtils.isInstanceOfKind(reference.identifier.parent);
|
|
399
384
|
},
|
|
400
|
-
|
|
401
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Given required refs and existing queryKey entries, compute missing dependency paths
|
|
387
|
+
* respecting allowlisted variables and types.
|
|
388
|
+
*/
|
|
389
|
+
computeFilteredMissingPaths(params) {
|
|
390
|
+
const {
|
|
391
|
+
requiredRefs,
|
|
392
|
+
allowlistedVariables,
|
|
393
|
+
existingRootIdentifiers,
|
|
394
|
+
existingFullPaths
|
|
395
|
+
} = params;
|
|
396
|
+
const missingPaths = /* @__PURE__ */ new Set();
|
|
397
|
+
for (const { root, path, allowlistedByType } of requiredRefs) {
|
|
398
|
+
if (existingRootIdentifiers.has(root)) continue;
|
|
399
|
+
if (allowlistedVariables.has(root)) continue;
|
|
400
|
+
if (existingFullPaths.has(path)) continue;
|
|
401
|
+
if (allowlistedByType) continue;
|
|
402
|
+
missingPaths.add(path);
|
|
403
|
+
}
|
|
404
|
+
for (const path of missingPaths) {
|
|
405
|
+
const root = path.split(".")[0];
|
|
406
|
+
if (root !== path && root !== void 0 && missingPaths.has(root)) {
|
|
407
|
+
missingPaths.delete(path);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return Array.from(missingPaths);
|
|
402
411
|
},
|
|
412
|
+
/**
|
|
413
|
+
* Extract existing queryKey deps as root identifiers and full member paths.
|
|
414
|
+
*/
|
|
403
415
|
collectQueryKeyDeps(params) {
|
|
404
416
|
const { sourceCode, scopeManager, queryKeyNode } = params;
|
|
405
|
-
const
|
|
417
|
+
const roots = /* @__PURE__ */ new Set();
|
|
418
|
+
const paths = /* @__PURE__ */ new Set();
|
|
406
419
|
const visitorKeys = sourceCode.visitorKeys;
|
|
407
|
-
function
|
|
408
|
-
|
|
420
|
+
function addRoot(name8) {
|
|
421
|
+
const cleaned = ExhaustiveDepsUtils.normalizeChain(name8);
|
|
422
|
+
roots.add(cleaned);
|
|
423
|
+
paths.add(cleaned);
|
|
424
|
+
}
|
|
425
|
+
function addFull(text) {
|
|
426
|
+
const cleaned = ExhaustiveDepsUtils.normalizeChain(text);
|
|
427
|
+
paths.add(cleaned);
|
|
428
|
+
}
|
|
429
|
+
function addRefPath(refPath) {
|
|
430
|
+
if (!refPath) return;
|
|
431
|
+
if (refPath.coversRootMembers) {
|
|
432
|
+
addRoot(refPath.root);
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
addFull(refPath.path);
|
|
409
436
|
}
|
|
410
437
|
function visitChildren(node) {
|
|
411
438
|
const keys = visitorKeys[node.type] ?? [];
|
|
@@ -427,9 +454,15 @@ var ExhaustiveDepsUtils = {
|
|
|
427
454
|
function visit(node) {
|
|
428
455
|
if (!node) return;
|
|
429
456
|
switch (node.type) {
|
|
430
|
-
case import_utils3.AST_NODE_TYPES.Identifier:
|
|
431
|
-
|
|
457
|
+
case import_utils3.AST_NODE_TYPES.Identifier: {
|
|
458
|
+
addRefPath(
|
|
459
|
+
ExhaustiveDepsUtils.computeRefPath({
|
|
460
|
+
identifier: node,
|
|
461
|
+
sourceCode
|
|
462
|
+
})
|
|
463
|
+
);
|
|
432
464
|
return;
|
|
465
|
+
}
|
|
433
466
|
case import_utils3.AST_NODE_TYPES.ArrowFunctionExpression:
|
|
434
467
|
case import_utils3.AST_NODE_TYPES.FunctionExpression:
|
|
435
468
|
for (const reference of ExhaustiveDepsUtils.collectExternalRefsInFunction(
|
|
@@ -438,9 +471,15 @@ var ExhaustiveDepsUtils = {
|
|
|
438
471
|
scopeManager
|
|
439
472
|
}
|
|
440
473
|
)) {
|
|
441
|
-
if (reference.identifier.type
|
|
442
|
-
|
|
474
|
+
if (reference.identifier.type !== import_utils3.AST_NODE_TYPES.Identifier) {
|
|
475
|
+
continue;
|
|
443
476
|
}
|
|
477
|
+
addRefPath(
|
|
478
|
+
ExhaustiveDepsUtils.computeRefPath({
|
|
479
|
+
identifier: reference.identifier,
|
|
480
|
+
sourceCode
|
|
481
|
+
})
|
|
482
|
+
);
|
|
444
483
|
}
|
|
445
484
|
return;
|
|
446
485
|
case import_utils3.AST_NODE_TYPES.Property:
|
|
@@ -448,26 +487,96 @@ var ExhaustiveDepsUtils = {
|
|
|
448
487
|
return;
|
|
449
488
|
case import_utils3.AST_NODE_TYPES.MemberExpression:
|
|
450
489
|
if (node.parent.type === import_utils3.AST_NODE_TYPES.CallExpression && node.parent.callee === node && node.object.type === import_utils3.AST_NODE_TYPES.Identifier) {
|
|
451
|
-
|
|
490
|
+
addRoot(node.object.name);
|
|
452
491
|
} else {
|
|
453
492
|
visit(node.object);
|
|
454
493
|
}
|
|
455
494
|
return;
|
|
456
495
|
case import_utils3.AST_NODE_TYPES.CallExpression:
|
|
457
496
|
node.arguments.forEach((argument) => visit(argument));
|
|
458
|
-
|
|
459
|
-
|
|
497
|
+
switch (node.callee.type) {
|
|
498
|
+
case import_utils3.AST_NODE_TYPES.Identifier:
|
|
499
|
+
case import_utils3.AST_NODE_TYPES.MemberExpression:
|
|
500
|
+
case import_utils3.AST_NODE_TYPES.ChainExpression:
|
|
501
|
+
case import_utils3.AST_NODE_TYPES.TSNonNullExpression:
|
|
502
|
+
visit(node.callee);
|
|
503
|
+
break;
|
|
460
504
|
}
|
|
461
505
|
return;
|
|
462
506
|
}
|
|
463
507
|
visitChildren(node);
|
|
464
508
|
}
|
|
465
509
|
visit(queryKeyNode);
|
|
466
|
-
return
|
|
510
|
+
return { roots, paths };
|
|
467
511
|
},
|
|
468
512
|
isNode(value) {
|
|
469
513
|
return typeof value === "object" && value !== null && "type" in value && typeof value.type === "string";
|
|
470
514
|
},
|
|
515
|
+
/**
|
|
516
|
+
* Checks whether the resolved variable is allowlisted by its type annotation
|
|
517
|
+
*/
|
|
518
|
+
variableIsAllowlistedByType(params) {
|
|
519
|
+
const { allowlistedTypes, variable } = params;
|
|
520
|
+
if (allowlistedTypes.size === 0) return false;
|
|
521
|
+
if (!variable) return false;
|
|
522
|
+
for (const id of variable.identifiers) {
|
|
523
|
+
if (id.typeAnnotation) {
|
|
524
|
+
const typeIdentifiers = /* @__PURE__ */ new Set();
|
|
525
|
+
ExhaustiveDepsUtils.collectTypeIdentifiers(
|
|
526
|
+
id.typeAnnotation.typeAnnotation,
|
|
527
|
+
typeIdentifiers
|
|
528
|
+
);
|
|
529
|
+
for (const typeIdentifier of typeIdentifiers) {
|
|
530
|
+
if (allowlistedTypes.has(typeIdentifier)) return true;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return false;
|
|
535
|
+
},
|
|
536
|
+
isInstanceOfKind(node) {
|
|
537
|
+
return node.type === import_utils3.AST_NODE_TYPES.BinaryExpression && node.operator === "instanceof";
|
|
538
|
+
},
|
|
539
|
+
/**
|
|
540
|
+
* Normalizes a chain by removing optional chaining operators
|
|
541
|
+
*
|
|
542
|
+
* Example: `a?.b.c!` -> `a.b.c`
|
|
543
|
+
*/
|
|
544
|
+
normalizeChain(text) {
|
|
545
|
+
return text.replace(/(?:\?(\.)|!)/g, "$1");
|
|
546
|
+
},
|
|
547
|
+
/**
|
|
548
|
+
* Computes the reference path for an identifier
|
|
549
|
+
*
|
|
550
|
+
* Example: `a.b.c!` -> `{ path: 'a.b.c', root: 'a' }`
|
|
551
|
+
*/
|
|
552
|
+
computeRefPath(params) {
|
|
553
|
+
const { identifier, sourceCode } = params;
|
|
554
|
+
const fullChainNode = ASTUtils.traverseUpOnly(identifier, [
|
|
555
|
+
import_utils3.AST_NODE_TYPES.MemberExpression,
|
|
556
|
+
import_utils3.AST_NODE_TYPES.TSNonNullExpression,
|
|
557
|
+
import_utils3.AST_NODE_TYPES.Identifier
|
|
558
|
+
]);
|
|
559
|
+
const fullText = ExhaustiveDepsUtils.normalizeChain(
|
|
560
|
+
sourceCode.getText(fullChainNode)
|
|
561
|
+
);
|
|
562
|
+
const parent = fullChainNode.parent;
|
|
563
|
+
let dependencyPath = fullText;
|
|
564
|
+
let coversRootMembers = fullText === identifier.name;
|
|
565
|
+
if (parent && parent.type === import_utils3.AST_NODE_TYPES.CallExpression && parent.callee === fullChainNode) {
|
|
566
|
+
const segments = fullText.split(".");
|
|
567
|
+
if (segments.length > 1) {
|
|
568
|
+
dependencyPath = segments.slice(0, -1).join(".");
|
|
569
|
+
}
|
|
570
|
+
coversRootMembers = false;
|
|
571
|
+
}
|
|
572
|
+
dependencyPath = dependencyPath.split(".")[0] === "" ? identifier.name : dependencyPath;
|
|
573
|
+
const root = dependencyPath.split(".")[0];
|
|
574
|
+
return {
|
|
575
|
+
path: dependencyPath,
|
|
576
|
+
root: root ?? identifier.name,
|
|
577
|
+
coversRootMembers: coversRootMembers && dependencyPath === root
|
|
578
|
+
};
|
|
579
|
+
},
|
|
471
580
|
collectExternalRefsInFunction(params) {
|
|
472
581
|
const { functionNode, scopeManager } = params;
|
|
473
582
|
const functionScope = scopeManager.acquire(functionNode);
|
|
@@ -499,6 +608,52 @@ var ExhaustiveDepsUtils = {
|
|
|
499
608
|
}
|
|
500
609
|
collect(functionScope);
|
|
501
610
|
return externalRefs;
|
|
611
|
+
},
|
|
612
|
+
/**
|
|
613
|
+
* Recursively collects type identifiers from a type annotation
|
|
614
|
+
*/
|
|
615
|
+
collectTypeIdentifiers(typeNode, out) {
|
|
616
|
+
switch (typeNode.type) {
|
|
617
|
+
case import_utils3.AST_NODE_TYPES.TSTypeReference: {
|
|
618
|
+
if (typeNode.typeName.type === import_utils3.AST_NODE_TYPES.Identifier) {
|
|
619
|
+
out.add(typeNode.typeName.name);
|
|
620
|
+
}
|
|
621
|
+
break;
|
|
622
|
+
}
|
|
623
|
+
case import_utils3.AST_NODE_TYPES.TSUnionType:
|
|
624
|
+
case import_utils3.AST_NODE_TYPES.TSIntersectionType: {
|
|
625
|
+
typeNode.types.forEach(
|
|
626
|
+
(t) => ExhaustiveDepsUtils.collectTypeIdentifiers(t, out)
|
|
627
|
+
);
|
|
628
|
+
break;
|
|
629
|
+
}
|
|
630
|
+
case import_utils3.AST_NODE_TYPES.TSArrayType: {
|
|
631
|
+
ExhaustiveDepsUtils.collectTypeIdentifiers(typeNode.elementType, out);
|
|
632
|
+
break;
|
|
633
|
+
}
|
|
634
|
+
case import_utils3.AST_NODE_TYPES.TSTupleType: {
|
|
635
|
+
typeNode.elementTypes.forEach(
|
|
636
|
+
(et) => ExhaustiveDepsUtils.collectTypeIdentifiers(et, out)
|
|
637
|
+
);
|
|
638
|
+
break;
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
},
|
|
642
|
+
/**
|
|
643
|
+
* Gets the function expression nodes from a queryFn property, handling conditional expressions.
|
|
644
|
+
* When neither branch is skipToken, returns both branches so all deps are scanned.
|
|
645
|
+
*/
|
|
646
|
+
getQueryFnNodes(queryFn) {
|
|
647
|
+
if (queryFn.value.type !== import_utils3.AST_NODE_TYPES.ConditionalExpression) {
|
|
648
|
+
return [queryFn.value];
|
|
649
|
+
}
|
|
650
|
+
if (queryFn.value.consequent.type === import_utils3.AST_NODE_TYPES.Identifier && queryFn.value.consequent.name === "skipToken") {
|
|
651
|
+
return [queryFn.value.alternate];
|
|
652
|
+
}
|
|
653
|
+
if (queryFn.value.alternate.type === import_utils3.AST_NODE_TYPES.Identifier && queryFn.value.alternate.name === "skipToken") {
|
|
654
|
+
return [queryFn.value.consequent];
|
|
655
|
+
}
|
|
656
|
+
return [queryFn.value.consequent, queryFn.value.alternate];
|
|
502
657
|
}
|
|
503
658
|
};
|
|
504
659
|
|
|
@@ -521,22 +676,34 @@ var rule = createRule({
|
|
|
521
676
|
},
|
|
522
677
|
hasSuggestions: true,
|
|
523
678
|
fixable: "code",
|
|
524
|
-
schema: [
|
|
679
|
+
schema: [
|
|
680
|
+
{
|
|
681
|
+
type: "object",
|
|
682
|
+
properties: {
|
|
683
|
+
allowlist: {
|
|
684
|
+
type: "object",
|
|
685
|
+
properties: {
|
|
686
|
+
variables: { type: "array", items: { type: "string" } },
|
|
687
|
+
types: { type: "array", items: { type: "string" } }
|
|
688
|
+
},
|
|
689
|
+
additionalProperties: false
|
|
690
|
+
}
|
|
691
|
+
},
|
|
692
|
+
additionalProperties: false
|
|
693
|
+
}
|
|
694
|
+
]
|
|
525
695
|
},
|
|
526
696
|
defaultOptions: [],
|
|
527
697
|
create: detectTanstackQueryImports((context) => {
|
|
528
698
|
return {
|
|
529
|
-
|
|
530
|
-
if (!ASTUtils.isObjectExpression(node.parent) || !ASTUtils.isIdentifierWithName(node.key, QUERY_KEY)) {
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
699
|
+
ObjectExpression: (node) => {
|
|
533
700
|
const scopeManager = context.sourceCode.scopeManager;
|
|
534
701
|
const queryKey = ASTUtils.findPropertyWithIdentifierKey(
|
|
535
|
-
node.
|
|
702
|
+
node.properties,
|
|
536
703
|
QUERY_KEY
|
|
537
704
|
);
|
|
538
705
|
const queryFn = ASTUtils.findPropertyWithIdentifierKey(
|
|
539
|
-
node.
|
|
706
|
+
node.properties,
|
|
540
707
|
QUERY_FN
|
|
541
708
|
);
|
|
542
709
|
if (scopeManager === null || queryKey === void 0 || queryFn === void 0 || !ASTUtils.isNodeOfOneOf(queryFn.value, [
|
|
@@ -550,73 +717,104 @@ var rule = createRule({
|
|
|
550
717
|
queryKey.value,
|
|
551
718
|
context
|
|
552
719
|
);
|
|
553
|
-
const
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
node: getQueryFnRelevantNode(queryFn)
|
|
557
|
-
});
|
|
558
|
-
const relevantRefs = externalRefs.filter(
|
|
559
|
-
(reference) => ExhaustiveDepsUtils.isRelevantReference({
|
|
560
|
-
sourceCode: context.sourceCode,
|
|
561
|
-
reference,
|
|
720
|
+
const queryFnNodes = ExhaustiveDepsUtils.getQueryFnNodes(queryFn);
|
|
721
|
+
const externalRefs = queryFnNodes.flatMap(
|
|
722
|
+
(fnNode) => ASTUtils.getExternalRefs({
|
|
562
723
|
scopeManager,
|
|
563
|
-
|
|
564
|
-
|
|
724
|
+
sourceCode: context.sourceCode,
|
|
725
|
+
node: fnNode
|
|
565
726
|
})
|
|
566
727
|
);
|
|
728
|
+
const relevantRefs = externalRefs.filter(
|
|
729
|
+
(reference) => queryFnNodes.some(
|
|
730
|
+
(fnNode) => ExhaustiveDepsUtils.isRelevantReference({
|
|
731
|
+
sourceCode: context.sourceCode,
|
|
732
|
+
reference,
|
|
733
|
+
scopeManager,
|
|
734
|
+
node: fnNode,
|
|
735
|
+
filename: context.filename
|
|
736
|
+
})
|
|
737
|
+
)
|
|
738
|
+
);
|
|
739
|
+
const ruleOptions = context.options.at(0);
|
|
740
|
+
const allowlistedVariables = new Set(
|
|
741
|
+
ruleOptions?.allowlist?.variables ?? []
|
|
742
|
+
);
|
|
743
|
+
const allowlistedTypes = new Set(ruleOptions?.allowlist?.types ?? []);
|
|
744
|
+
const requiredRefs = relevantRefs.flatMap((ref) => {
|
|
745
|
+
if (ref.identifier.type !== import_utils4.AST_NODE_TYPES.Identifier) return [];
|
|
746
|
+
const refPath = ExhaustiveDepsUtils.computeRefPath({
|
|
747
|
+
identifier: ref.identifier,
|
|
748
|
+
sourceCode: context.sourceCode
|
|
749
|
+
});
|
|
750
|
+
if (refPath === null) return [];
|
|
751
|
+
return [
|
|
752
|
+
{
|
|
753
|
+
...refPath,
|
|
754
|
+
allowlistedByType: ExhaustiveDepsUtils.variableIsAllowlistedByType({
|
|
755
|
+
allowlistedTypes,
|
|
756
|
+
variable: ref.resolved ?? null
|
|
757
|
+
})
|
|
758
|
+
}
|
|
759
|
+
];
|
|
760
|
+
});
|
|
761
|
+
if (requiredRefs.length === 0) return;
|
|
567
762
|
const queryKeyDeps = ExhaustiveDepsUtils.collectQueryKeyDeps({
|
|
568
763
|
sourceCode: context.sourceCode,
|
|
569
764
|
scopeManager,
|
|
570
765
|
queryKeyNode
|
|
571
766
|
});
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
messageId: "fixTo",
|
|
593
|
-
data: { result: existingWithMissing },
|
|
594
|
-
fix(fixer) {
|
|
595
|
-
return fixer.replaceText(queryKeyNode, existingWithMissing);
|
|
596
|
-
}
|
|
597
|
-
});
|
|
598
|
-
}
|
|
599
|
-
context.report({
|
|
600
|
-
node,
|
|
601
|
-
messageId: "missingDeps",
|
|
602
|
-
data: {
|
|
603
|
-
deps: uniqueMissingRefs.map((ref) => ref.text).join(", ")
|
|
604
|
-
},
|
|
605
|
-
suggest: suggestions
|
|
606
|
-
});
|
|
607
|
-
}
|
|
767
|
+
const missingPaths = ExhaustiveDepsUtils.computeFilteredMissingPaths({
|
|
768
|
+
requiredRefs,
|
|
769
|
+
allowlistedVariables,
|
|
770
|
+
existingRootIdentifiers: queryKeyDeps.roots,
|
|
771
|
+
existingFullPaths: queryKeyDeps.paths
|
|
772
|
+
});
|
|
773
|
+
if (missingPaths.length === 0) return;
|
|
774
|
+
const missingAsText = missingPaths.join(", ");
|
|
775
|
+
const suggestions = buildSuggestions({
|
|
776
|
+
queryKeyNode,
|
|
777
|
+
missingPaths,
|
|
778
|
+
missingAsText,
|
|
779
|
+
sourceCode: context.sourceCode
|
|
780
|
+
});
|
|
781
|
+
context.report({
|
|
782
|
+
node,
|
|
783
|
+
messageId: "missingDeps",
|
|
784
|
+
data: { deps: missingAsText },
|
|
785
|
+
suggest: suggestions
|
|
786
|
+
});
|
|
608
787
|
}
|
|
609
788
|
};
|
|
610
789
|
})
|
|
611
790
|
});
|
|
612
|
-
function
|
|
613
|
-
|
|
614
|
-
|
|
791
|
+
function buildSuggestions(params) {
|
|
792
|
+
const { queryKeyNode, missingPaths, missingAsText, sourceCode } = params;
|
|
793
|
+
if (queryKeyNode.type !== import_utils4.AST_NODE_TYPES.ArrayExpression) {
|
|
794
|
+
return [];
|
|
615
795
|
}
|
|
616
|
-
|
|
617
|
-
|
|
796
|
+
const closingBracket = sourceCode.getLastToken(queryKeyNode);
|
|
797
|
+
if (!closingBracket) return [];
|
|
798
|
+
const existingElements = queryKeyNode.elements.filter((el) => el !== null).map((el) => sourceCode.getText(el));
|
|
799
|
+
const resultText = `[${[...existingElements, ...missingPaths].join(", ")}]`;
|
|
800
|
+
if (queryKeyNode.elements.length === 0) {
|
|
801
|
+
return [
|
|
802
|
+
{
|
|
803
|
+
messageId: "fixTo",
|
|
804
|
+
data: { result: resultText },
|
|
805
|
+
fix: (fixer) => fixer.replaceText(queryKeyNode, resultText)
|
|
806
|
+
}
|
|
807
|
+
];
|
|
618
808
|
}
|
|
619
|
-
|
|
809
|
+
const tokenBefore = sourceCode.getTokenBefore(closingBracket);
|
|
810
|
+
const separator = tokenBefore?.value === "," ? " " : ", ";
|
|
811
|
+
return [
|
|
812
|
+
{
|
|
813
|
+
messageId: "fixTo",
|
|
814
|
+
data: { result: resultText },
|
|
815
|
+
fix: (fixer) => fixer.insertTextBefore(closingBracket, `${separator}${missingAsText}`)
|
|
816
|
+
}
|
|
817
|
+
];
|
|
620
818
|
}
|
|
621
819
|
function dereferenceVariablesAndTypeAssertions(queryKeyNode, context) {
|
|
622
820
|
const visitedNodes = /* @__PURE__ */ new Set();
|