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