@tanstack/eslint-plugin-query 5.50.1 → 5.51.7

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.
@@ -44,7 +44,8 @@ const rule = createRule({
44
44
  );
45
45
  if (scopeManager === null || queryKey === void 0 || queryFn === void 0 || !astUtils.ASTUtils.isNodeOfOneOf(queryFn.value, [
46
46
  utils.AST_NODE_TYPES.ArrowFunctionExpression,
47
- utils.AST_NODE_TYPES.FunctionExpression
47
+ utils.AST_NODE_TYPES.FunctionExpression,
48
+ utils.AST_NODE_TYPES.ConditionalExpression
48
49
  ])) {
49
50
  return;
50
51
  }
@@ -65,14 +66,14 @@ const rule = createRule({
65
66
  const externalRefs = astUtils.ASTUtils.getExternalRefs({
66
67
  scopeManager,
67
68
  sourceCode: context.sourceCode,
68
- node: queryFn.value
69
+ node: getQueryFnRelevantNode(queryFn)
69
70
  });
70
71
  const relevantRefs = externalRefs.filter(
71
72
  (reference) => exhaustiveDeps_utils.ExhaustiveDepsUtils.isRelevantReference({
72
73
  sourceCode: context.sourceCode,
73
74
  reference,
74
75
  scopeManager,
75
- node: queryFn.value
76
+ node: getQueryFnRelevantNode(queryFn)
76
77
  })
77
78
  );
78
79
  const existingKeys = astUtils.ASTUtils.getNestedIdentifiers(queryKeyValue).map(
@@ -116,6 +117,15 @@ const rule = createRule({
116
117
  };
117
118
  })
118
119
  });
120
+ function getQueryFnRelevantNode(queryFn) {
121
+ if (queryFn.value.type !== utils.AST_NODE_TYPES.ConditionalExpression) {
122
+ return queryFn.value;
123
+ }
124
+ if (queryFn.value.consequent.type === utils.AST_NODE_TYPES.Identifier && queryFn.value.consequent.name === "skipToken") {
125
+ return queryFn.value.alternate;
126
+ }
127
+ return queryFn.value.consequent;
128
+ }
119
129
  exports.name = name;
120
130
  exports.rule = rule;
121
131
  //# sourceMappingURL=exhaustive-deps.rule.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"exhaustive-deps.rule.cjs","sources":["../../../../src/rules/exhaustive-deps/exhaustive-deps.rule.ts"],"sourcesContent":["import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'\nimport { ASTUtils } from '../../utils/ast-utils'\nimport { getDocsUrl } from '../../utils/get-docs-url'\nimport { uniqueBy } from '../../utils/unique-by'\nimport { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'\nimport { ExhaustiveDepsUtils } from './exhaustive-deps.utils'\nimport type { TSESLint } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from '../../types'\n\nconst QUERY_KEY = 'queryKey'\nconst QUERY_FN = 'queryFn'\n\nexport const name = 'exhaustive-deps'\n\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Exhaustive deps rule for useQuery',\n recommended: 'error',\n },\n messages: {\n missingDeps: `The following dependencies are missing in your queryKey: {{deps}}`,\n fixTo: 'Fix to {{result}}',\n },\n hasSuggestions: true,\n fixable: 'code',\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackQueryImports((context) => {\n return {\n Property: (node) => {\n if (\n !ASTUtils.isObjectExpression(node.parent) ||\n !ASTUtils.isIdentifierWithName(node.key, QUERY_KEY)\n ) {\n return\n }\n\n const scopeManager = context.sourceCode.scopeManager\n const queryKey = ASTUtils.findPropertyWithIdentifierKey(\n node.parent.properties,\n QUERY_KEY,\n )\n const queryFn = ASTUtils.findPropertyWithIdentifierKey(\n node.parent.properties,\n QUERY_FN,\n )\n\n if (\n scopeManager === null ||\n queryKey === undefined ||\n queryFn === undefined ||\n !ASTUtils.isNodeOfOneOf(queryFn.value, [\n AST_NODE_TYPES.ArrowFunctionExpression,\n AST_NODE_TYPES.FunctionExpression,\n ])\n ) {\n return\n }\n\n let queryKeyNode = queryKey.value\n\n if (\n queryKeyNode.type === AST_NODE_TYPES.TSAsExpression &&\n queryKeyNode.expression.type === AST_NODE_TYPES.ArrayExpression\n ) {\n queryKeyNode = queryKeyNode.expression\n }\n\n if (queryKeyNode.type === AST_NODE_TYPES.Identifier) {\n const expression = ASTUtils.getReferencedExpressionByIdentifier({\n context,\n node: queryKeyNode,\n })\n\n if (expression?.type === AST_NODE_TYPES.ArrayExpression) {\n queryKeyNode = expression\n }\n }\n\n const queryKeyValue = queryKeyNode\n const externalRefs = ASTUtils.getExternalRefs({\n scopeManager,\n sourceCode: context.sourceCode,\n node: queryFn.value,\n })\n\n const relevantRefs = externalRefs.filter((reference) =>\n ExhaustiveDepsUtils.isRelevantReference({\n sourceCode: context.sourceCode,\n reference,\n scopeManager,\n node: queryFn.value,\n }),\n )\n\n const existingKeys = ASTUtils.getNestedIdentifiers(queryKeyValue).map(\n (identifier) =>\n ASTUtils.mapKeyNodeToText(identifier, context.sourceCode),\n )\n\n const missingRefs = relevantRefs\n .map((ref) => ({\n ref: ref,\n text: ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),\n }))\n .filter(({ ref, text }) => {\n return (\n !ref.isTypeReference &&\n !ASTUtils.isAncestorIsCallee(ref.identifier) &&\n !existingKeys.some((existingKey) => existingKey === text) &&\n !existingKeys.includes(text.split('.')[0] ?? '')\n )\n })\n .map(({ ref, text }) => ({\n identifier: ref.identifier,\n text: text,\n }))\n\n const uniqueMissingRefs = uniqueBy(missingRefs, (x) => x.text)\n\n if (uniqueMissingRefs.length > 0) {\n const missingAsText = uniqueMissingRefs\n .map((ref) =>\n ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),\n )\n .join(', ')\n\n const existingWithMissing = context.sourceCode\n .getText(queryKeyValue)\n .replace(/\\]$/, `, ${missingAsText}]`)\n\n const suggestions: TSESLint.ReportSuggestionArray<string> = []\n\n if (queryKeyNode.type === AST_NODE_TYPES.ArrayExpression) {\n suggestions.push({\n messageId: 'fixTo',\n data: { result: existingWithMissing },\n fix(fixer) {\n return fixer.replaceText(queryKeyValue, existingWithMissing)\n },\n })\n }\n\n context.report({\n node: node,\n messageId: 'missingDeps',\n data: {\n deps: uniqueMissingRefs.map((ref) => ref.text).join(', '),\n },\n suggest: suggestions,\n })\n }\n },\n }\n }),\n})\n"],"names":["ESLintUtils","getDocsUrl","detectTanstackQueryImports","ASTUtils","AST_NODE_TYPES","ExhaustiveDepsUtils","uniqueBy"],"mappings":"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"}
1
+ {"version":3,"file":"exhaustive-deps.rule.cjs","sources":["../../../../src/rules/exhaustive-deps/exhaustive-deps.rule.ts"],"sourcesContent":["import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'\nimport { ASTUtils } from '../../utils/ast-utils'\nimport { getDocsUrl } from '../../utils/get-docs-url'\nimport { uniqueBy } from '../../utils/unique-by'\nimport { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'\nimport { ExhaustiveDepsUtils } from './exhaustive-deps.utils'\nimport type { TSESLint, TSESTree } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from '../../types'\n\nconst QUERY_KEY = 'queryKey'\nconst QUERY_FN = 'queryFn'\n\nexport const name = 'exhaustive-deps'\n\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Exhaustive deps rule for useQuery',\n recommended: 'error',\n },\n messages: {\n missingDeps: `The following dependencies are missing in your queryKey: {{deps}}`,\n fixTo: 'Fix to {{result}}',\n },\n hasSuggestions: true,\n fixable: 'code',\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackQueryImports((context) => {\n return {\n Property: (node) => {\n if (\n !ASTUtils.isObjectExpression(node.parent) ||\n !ASTUtils.isIdentifierWithName(node.key, QUERY_KEY)\n ) {\n return\n }\n\n const scopeManager = context.sourceCode.scopeManager\n const queryKey = ASTUtils.findPropertyWithIdentifierKey(\n node.parent.properties,\n QUERY_KEY,\n )\n const queryFn = ASTUtils.findPropertyWithIdentifierKey(\n node.parent.properties,\n QUERY_FN,\n )\n\n if (\n scopeManager === null ||\n queryKey === undefined ||\n queryFn === undefined ||\n !ASTUtils.isNodeOfOneOf(queryFn.value, [\n AST_NODE_TYPES.ArrowFunctionExpression,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ConditionalExpression,\n ])\n ) {\n return\n }\n\n let queryKeyNode = queryKey.value\n\n if (\n queryKeyNode.type === AST_NODE_TYPES.TSAsExpression &&\n queryKeyNode.expression.type === AST_NODE_TYPES.ArrayExpression\n ) {\n queryKeyNode = queryKeyNode.expression\n }\n\n if (queryKeyNode.type === AST_NODE_TYPES.Identifier) {\n const expression = ASTUtils.getReferencedExpressionByIdentifier({\n context,\n node: queryKeyNode,\n })\n\n if (expression?.type === AST_NODE_TYPES.ArrayExpression) {\n queryKeyNode = expression\n }\n }\n\n const queryKeyValue = queryKeyNode\n const externalRefs = ASTUtils.getExternalRefs({\n scopeManager,\n sourceCode: context.sourceCode,\n node: getQueryFnRelevantNode(queryFn),\n })\n\n const relevantRefs = externalRefs.filter((reference) =>\n ExhaustiveDepsUtils.isRelevantReference({\n sourceCode: context.sourceCode,\n reference,\n scopeManager,\n node: getQueryFnRelevantNode(queryFn),\n }),\n )\n\n const existingKeys = ASTUtils.getNestedIdentifiers(queryKeyValue).map(\n (identifier) =>\n ASTUtils.mapKeyNodeToText(identifier, context.sourceCode),\n )\n\n const missingRefs = relevantRefs\n .map((ref) => ({\n ref: ref,\n text: ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),\n }))\n .filter(({ ref, text }) => {\n return (\n !ref.isTypeReference &&\n !ASTUtils.isAncestorIsCallee(ref.identifier) &&\n !existingKeys.some((existingKey) => existingKey === text) &&\n !existingKeys.includes(text.split('.')[0] ?? '')\n )\n })\n .map(({ ref, text }) => ({\n identifier: ref.identifier,\n text: text,\n }))\n\n const uniqueMissingRefs = uniqueBy(missingRefs, (x) => x.text)\n\n if (uniqueMissingRefs.length > 0) {\n const missingAsText = uniqueMissingRefs\n .map((ref) =>\n ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),\n )\n .join(', ')\n\n const existingWithMissing = context.sourceCode\n .getText(queryKeyValue)\n .replace(/\\]$/, `, ${missingAsText}]`)\n\n const suggestions: TSESLint.ReportSuggestionArray<string> = []\n\n if (queryKeyNode.type === AST_NODE_TYPES.ArrayExpression) {\n suggestions.push({\n messageId: 'fixTo',\n data: { result: existingWithMissing },\n fix(fixer) {\n return fixer.replaceText(queryKeyValue, existingWithMissing)\n },\n })\n }\n\n context.report({\n node: node,\n messageId: 'missingDeps',\n data: {\n deps: uniqueMissingRefs.map((ref) => ref.text).join(', '),\n },\n suggest: suggestions,\n })\n }\n },\n }\n }),\n})\n\nfunction getQueryFnRelevantNode(queryFn: TSESTree.Property) {\n if (queryFn.value.type !== AST_NODE_TYPES.ConditionalExpression) {\n return queryFn.value\n }\n\n if (\n queryFn.value.consequent.type === AST_NODE_TYPES.Identifier &&\n queryFn.value.consequent.name === 'skipToken'\n ) {\n return queryFn.value.alternate\n }\n\n return queryFn.value.consequent\n}\n"],"names":["ESLintUtils","getDocsUrl","detectTanstackQueryImports","ASTUtils","AST_NODE_TYPES","ExhaustiveDepsUtils","uniqueBy"],"mappings":"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"}
@@ -42,7 +42,8 @@ const rule = createRule({
42
42
  );
43
43
  if (scopeManager === null || queryKey === void 0 || queryFn === void 0 || !ASTUtils.isNodeOfOneOf(queryFn.value, [
44
44
  AST_NODE_TYPES.ArrowFunctionExpression,
45
- AST_NODE_TYPES.FunctionExpression
45
+ AST_NODE_TYPES.FunctionExpression,
46
+ AST_NODE_TYPES.ConditionalExpression
46
47
  ])) {
47
48
  return;
48
49
  }
@@ -63,14 +64,14 @@ const rule = createRule({
63
64
  const externalRefs = ASTUtils.getExternalRefs({
64
65
  scopeManager,
65
66
  sourceCode: context.sourceCode,
66
- node: queryFn.value
67
+ node: getQueryFnRelevantNode(queryFn)
67
68
  });
68
69
  const relevantRefs = externalRefs.filter(
69
70
  (reference) => ExhaustiveDepsUtils.isRelevantReference({
70
71
  sourceCode: context.sourceCode,
71
72
  reference,
72
73
  scopeManager,
73
- node: queryFn.value
74
+ node: getQueryFnRelevantNode(queryFn)
74
75
  })
75
76
  );
76
77
  const existingKeys = ASTUtils.getNestedIdentifiers(queryKeyValue).map(
@@ -114,6 +115,15 @@ const rule = createRule({
114
115
  };
115
116
  })
116
117
  });
118
+ function getQueryFnRelevantNode(queryFn) {
119
+ if (queryFn.value.type !== AST_NODE_TYPES.ConditionalExpression) {
120
+ return queryFn.value;
121
+ }
122
+ if (queryFn.value.consequent.type === AST_NODE_TYPES.Identifier && queryFn.value.consequent.name === "skipToken") {
123
+ return queryFn.value.alternate;
124
+ }
125
+ return queryFn.value.consequent;
126
+ }
117
127
  export {
118
128
  name,
119
129
  rule
@@ -1 +1 @@
1
- {"version":3,"file":"exhaustive-deps.rule.js","sources":["../../../../src/rules/exhaustive-deps/exhaustive-deps.rule.ts"],"sourcesContent":["import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'\nimport { ASTUtils } from '../../utils/ast-utils'\nimport { getDocsUrl } from '../../utils/get-docs-url'\nimport { uniqueBy } from '../../utils/unique-by'\nimport { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'\nimport { ExhaustiveDepsUtils } from './exhaustive-deps.utils'\nimport type { TSESLint } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from '../../types'\n\nconst QUERY_KEY = 'queryKey'\nconst QUERY_FN = 'queryFn'\n\nexport const name = 'exhaustive-deps'\n\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Exhaustive deps rule for useQuery',\n recommended: 'error',\n },\n messages: {\n missingDeps: `The following dependencies are missing in your queryKey: {{deps}}`,\n fixTo: 'Fix to {{result}}',\n },\n hasSuggestions: true,\n fixable: 'code',\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackQueryImports((context) => {\n return {\n Property: (node) => {\n if (\n !ASTUtils.isObjectExpression(node.parent) ||\n !ASTUtils.isIdentifierWithName(node.key, QUERY_KEY)\n ) {\n return\n }\n\n const scopeManager = context.sourceCode.scopeManager\n const queryKey = ASTUtils.findPropertyWithIdentifierKey(\n node.parent.properties,\n QUERY_KEY,\n )\n const queryFn = ASTUtils.findPropertyWithIdentifierKey(\n node.parent.properties,\n QUERY_FN,\n )\n\n if (\n scopeManager === null ||\n queryKey === undefined ||\n queryFn === undefined ||\n !ASTUtils.isNodeOfOneOf(queryFn.value, [\n AST_NODE_TYPES.ArrowFunctionExpression,\n AST_NODE_TYPES.FunctionExpression,\n ])\n ) {\n return\n }\n\n let queryKeyNode = queryKey.value\n\n if (\n queryKeyNode.type === AST_NODE_TYPES.TSAsExpression &&\n queryKeyNode.expression.type === AST_NODE_TYPES.ArrayExpression\n ) {\n queryKeyNode = queryKeyNode.expression\n }\n\n if (queryKeyNode.type === AST_NODE_TYPES.Identifier) {\n const expression = ASTUtils.getReferencedExpressionByIdentifier({\n context,\n node: queryKeyNode,\n })\n\n if (expression?.type === AST_NODE_TYPES.ArrayExpression) {\n queryKeyNode = expression\n }\n }\n\n const queryKeyValue = queryKeyNode\n const externalRefs = ASTUtils.getExternalRefs({\n scopeManager,\n sourceCode: context.sourceCode,\n node: queryFn.value,\n })\n\n const relevantRefs = externalRefs.filter((reference) =>\n ExhaustiveDepsUtils.isRelevantReference({\n sourceCode: context.sourceCode,\n reference,\n scopeManager,\n node: queryFn.value,\n }),\n )\n\n const existingKeys = ASTUtils.getNestedIdentifiers(queryKeyValue).map(\n (identifier) =>\n ASTUtils.mapKeyNodeToText(identifier, context.sourceCode),\n )\n\n const missingRefs = relevantRefs\n .map((ref) => ({\n ref: ref,\n text: ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),\n }))\n .filter(({ ref, text }) => {\n return (\n !ref.isTypeReference &&\n !ASTUtils.isAncestorIsCallee(ref.identifier) &&\n !existingKeys.some((existingKey) => existingKey === text) &&\n !existingKeys.includes(text.split('.')[0] ?? '')\n )\n })\n .map(({ ref, text }) => ({\n identifier: ref.identifier,\n text: text,\n }))\n\n const uniqueMissingRefs = uniqueBy(missingRefs, (x) => x.text)\n\n if (uniqueMissingRefs.length > 0) {\n const missingAsText = uniqueMissingRefs\n .map((ref) =>\n ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),\n )\n .join(', ')\n\n const existingWithMissing = context.sourceCode\n .getText(queryKeyValue)\n .replace(/\\]$/, `, ${missingAsText}]`)\n\n const suggestions: TSESLint.ReportSuggestionArray<string> = []\n\n if (queryKeyNode.type === AST_NODE_TYPES.ArrayExpression) {\n suggestions.push({\n messageId: 'fixTo',\n data: { result: existingWithMissing },\n fix(fixer) {\n return fixer.replaceText(queryKeyValue, existingWithMissing)\n },\n })\n }\n\n context.report({\n node: node,\n messageId: 'missingDeps',\n data: {\n deps: uniqueMissingRefs.map((ref) => ref.text).join(', '),\n },\n suggest: suggestions,\n })\n }\n },\n }\n }),\n})\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"exhaustive-deps.rule.js","sources":["../../../../src/rules/exhaustive-deps/exhaustive-deps.rule.ts"],"sourcesContent":["import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'\nimport { ASTUtils } from '../../utils/ast-utils'\nimport { getDocsUrl } from '../../utils/get-docs-url'\nimport { uniqueBy } from '../../utils/unique-by'\nimport { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'\nimport { ExhaustiveDepsUtils } from './exhaustive-deps.utils'\nimport type { TSESLint, TSESTree } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from '../../types'\n\nconst QUERY_KEY = 'queryKey'\nconst QUERY_FN = 'queryFn'\n\nexport const name = 'exhaustive-deps'\n\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Exhaustive deps rule for useQuery',\n recommended: 'error',\n },\n messages: {\n missingDeps: `The following dependencies are missing in your queryKey: {{deps}}`,\n fixTo: 'Fix to {{result}}',\n },\n hasSuggestions: true,\n fixable: 'code',\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackQueryImports((context) => {\n return {\n Property: (node) => {\n if (\n !ASTUtils.isObjectExpression(node.parent) ||\n !ASTUtils.isIdentifierWithName(node.key, QUERY_KEY)\n ) {\n return\n }\n\n const scopeManager = context.sourceCode.scopeManager\n const queryKey = ASTUtils.findPropertyWithIdentifierKey(\n node.parent.properties,\n QUERY_KEY,\n )\n const queryFn = ASTUtils.findPropertyWithIdentifierKey(\n node.parent.properties,\n QUERY_FN,\n )\n\n if (\n scopeManager === null ||\n queryKey === undefined ||\n queryFn === undefined ||\n !ASTUtils.isNodeOfOneOf(queryFn.value, [\n AST_NODE_TYPES.ArrowFunctionExpression,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ConditionalExpression,\n ])\n ) {\n return\n }\n\n let queryKeyNode = queryKey.value\n\n if (\n queryKeyNode.type === AST_NODE_TYPES.TSAsExpression &&\n queryKeyNode.expression.type === AST_NODE_TYPES.ArrayExpression\n ) {\n queryKeyNode = queryKeyNode.expression\n }\n\n if (queryKeyNode.type === AST_NODE_TYPES.Identifier) {\n const expression = ASTUtils.getReferencedExpressionByIdentifier({\n context,\n node: queryKeyNode,\n })\n\n if (expression?.type === AST_NODE_TYPES.ArrayExpression) {\n queryKeyNode = expression\n }\n }\n\n const queryKeyValue = queryKeyNode\n const externalRefs = ASTUtils.getExternalRefs({\n scopeManager,\n sourceCode: context.sourceCode,\n node: getQueryFnRelevantNode(queryFn),\n })\n\n const relevantRefs = externalRefs.filter((reference) =>\n ExhaustiveDepsUtils.isRelevantReference({\n sourceCode: context.sourceCode,\n reference,\n scopeManager,\n node: getQueryFnRelevantNode(queryFn),\n }),\n )\n\n const existingKeys = ASTUtils.getNestedIdentifiers(queryKeyValue).map(\n (identifier) =>\n ASTUtils.mapKeyNodeToText(identifier, context.sourceCode),\n )\n\n const missingRefs = relevantRefs\n .map((ref) => ({\n ref: ref,\n text: ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),\n }))\n .filter(({ ref, text }) => {\n return (\n !ref.isTypeReference &&\n !ASTUtils.isAncestorIsCallee(ref.identifier) &&\n !existingKeys.some((existingKey) => existingKey === text) &&\n !existingKeys.includes(text.split('.')[0] ?? '')\n )\n })\n .map(({ ref, text }) => ({\n identifier: ref.identifier,\n text: text,\n }))\n\n const uniqueMissingRefs = uniqueBy(missingRefs, (x) => x.text)\n\n if (uniqueMissingRefs.length > 0) {\n const missingAsText = uniqueMissingRefs\n .map((ref) =>\n ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),\n )\n .join(', ')\n\n const existingWithMissing = context.sourceCode\n .getText(queryKeyValue)\n .replace(/\\]$/, `, ${missingAsText}]`)\n\n const suggestions: TSESLint.ReportSuggestionArray<string> = []\n\n if (queryKeyNode.type === AST_NODE_TYPES.ArrayExpression) {\n suggestions.push({\n messageId: 'fixTo',\n data: { result: existingWithMissing },\n fix(fixer) {\n return fixer.replaceText(queryKeyValue, existingWithMissing)\n },\n })\n }\n\n context.report({\n node: node,\n messageId: 'missingDeps',\n data: {\n deps: uniqueMissingRefs.map((ref) => ref.text).join(', '),\n },\n suggest: suggestions,\n })\n }\n },\n }\n }),\n})\n\nfunction getQueryFnRelevantNode(queryFn: TSESTree.Property) {\n if (queryFn.value.type !== AST_NODE_TYPES.ConditionalExpression) {\n return queryFn.value\n }\n\n if (\n queryFn.value.consequent.type === AST_NODE_TYPES.Identifier &&\n queryFn.value.consequent.name === 'skipToken'\n ) {\n return queryFn.value.alternate\n }\n\n return queryFn.value.consequent\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/eslint-plugin-query",
3
- "version": "5.50.1",
3
+ "version": "5.51.7",
4
4
  "description": "ESLint plugin for TanStack Query",
5
5
  "author": "Eliya Cohen",
6
6
  "license": "MIT",
@@ -402,6 +402,20 @@ ruleTester.run('exhaustive-deps', rule, {
402
402
  }
403
403
  `,
404
404
  },
405
+ {
406
+ name: 'queryFn as a ternary expression with dep and a skipToken',
407
+ code: normalizeIndent`
408
+ import { useQuery, skipToken } from "@tanstack/react-query";
409
+ const fetch = true
410
+
411
+ function Component({ id }) {
412
+ useQuery({
413
+ queryKey: [id],
414
+ queryFn: fetch ? () => Promise.resolve(id) : skipToken
415
+ })
416
+ }
417
+ `,
418
+ },
405
419
  ],
406
420
  invalid: [
407
421
  {
@@ -733,5 +747,25 @@ ruleTester.run('exhaustive-deps', rule, {
733
747
  },
734
748
  ],
735
749
  },
750
+ {
751
+ name: 'should fail if queryFn is a ternary expression with missing dep and a skipToken',
752
+ code: normalizeIndent`
753
+ import { useQuery, skipToken } from "@tanstack/react-query";
754
+ const fetch = true
755
+
756
+ function Component({ id }) {
757
+ useQuery({
758
+ queryKey: [],
759
+ queryFn: fetch ? () => Promise.resolve(id) : skipToken
760
+ })
761
+ }
762
+ `,
763
+ errors: [
764
+ {
765
+ messageId: 'missingDeps',
766
+ data: { deps: 'id' },
767
+ },
768
+ ],
769
+ },
736
770
  ],
737
771
  })
@@ -4,7 +4,7 @@ import { getDocsUrl } from '../../utils/get-docs-url'
4
4
  import { uniqueBy } from '../../utils/unique-by'
5
5
  import { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'
6
6
  import { ExhaustiveDepsUtils } from './exhaustive-deps.utils'
7
- import type { TSESLint } from '@typescript-eslint/utils'
7
+ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
8
8
  import type { ExtraRuleDocs } from '../../types'
9
9
 
10
10
  const QUERY_KEY = 'queryKey'
@@ -59,6 +59,7 @@ export const rule = createRule({
59
59
  !ASTUtils.isNodeOfOneOf(queryFn.value, [
60
60
  AST_NODE_TYPES.ArrowFunctionExpression,
61
61
  AST_NODE_TYPES.FunctionExpression,
62
+ AST_NODE_TYPES.ConditionalExpression,
62
63
  ])
63
64
  ) {
64
65
  return
@@ -88,7 +89,7 @@ export const rule = createRule({
88
89
  const externalRefs = ASTUtils.getExternalRefs({
89
90
  scopeManager,
90
91
  sourceCode: context.sourceCode,
91
- node: queryFn.value,
92
+ node: getQueryFnRelevantNode(queryFn),
92
93
  })
93
94
 
94
95
  const relevantRefs = externalRefs.filter((reference) =>
@@ -96,7 +97,7 @@ export const rule = createRule({
96
97
  sourceCode: context.sourceCode,
97
98
  reference,
98
99
  scopeManager,
99
- node: queryFn.value,
100
+ node: getQueryFnRelevantNode(queryFn),
100
101
  }),
101
102
  )
102
103
 
@@ -161,3 +162,18 @@ export const rule = createRule({
161
162
  }
162
163
  }),
163
164
  })
165
+
166
+ function getQueryFnRelevantNode(queryFn: TSESTree.Property) {
167
+ if (queryFn.value.type !== AST_NODE_TYPES.ConditionalExpression) {
168
+ return queryFn.value
169
+ }
170
+
171
+ if (
172
+ queryFn.value.consequent.type === AST_NODE_TYPES.Identifier &&
173
+ queryFn.value.consequent.name === 'skipToken'
174
+ ) {
175
+ return queryFn.value.alternate
176
+ }
177
+
178
+ return queryFn.value.consequent
179
+ }