@tanstack/eslint-plugin-query 5.32.1 → 5.43.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 (54) hide show
  1. package/dist/cjs/configs.cjs +0 -1
  2. package/dist/cjs/configs.cjs.map +1 -1
  3. package/dist/cjs/configs.d.cts +1 -2
  4. package/dist/cjs/rules/exhaustive-deps/exhaustive-deps.rule.cjs +11 -9
  5. package/dist/cjs/rules/exhaustive-deps/exhaustive-deps.rule.cjs.map +1 -1
  6. package/dist/cjs/rules/exhaustive-deps/exhaustive-deps.rule.d.cts +3 -1
  7. package/dist/cjs/rules/exhaustive-deps/exhaustive-deps.utils.cjs +6 -3
  8. package/dist/cjs/rules/exhaustive-deps/exhaustive-deps.utils.cjs.map +1 -1
  9. package/dist/cjs/rules/exhaustive-deps/exhaustive-deps.utils.d.cts +5 -2
  10. package/dist/cjs/rules/no-rest-destructuring/no-rest-destructuring.rule.cjs.map +1 -1
  11. package/dist/cjs/rules/no-rest-destructuring/no-rest-destructuring.rule.d.cts +3 -1
  12. package/dist/cjs/rules/no-rest-destructuring/no-rest-destructuring.utils.d.cts +2 -1
  13. package/dist/cjs/rules/stable-query-client/stable-query-client.rule.cjs +4 -1
  14. package/dist/cjs/rules/stable-query-client/stable-query-client.rule.cjs.map +1 -1
  15. package/dist/cjs/rules/stable-query-client/stable-query-client.rule.d.cts +3 -1
  16. package/dist/cjs/rules.cjs.map +1 -1
  17. package/dist/cjs/rules.d.cts +4 -2
  18. package/dist/cjs/types.d.cts +3 -0
  19. package/dist/cjs/utils/ast-utils.cjs +2 -2
  20. package/dist/cjs/utils/ast-utils.cjs.map +1 -1
  21. package/dist/cjs/utils/ast-utils.d.cts +3 -3
  22. package/dist/cjs/utils/detect-react-query-imports.d.cts +2 -2
  23. package/dist/esm/configs.d.ts +1 -2
  24. package/dist/esm/configs.js +0 -1
  25. package/dist/esm/configs.js.map +1 -1
  26. package/dist/esm/rules/exhaustive-deps/exhaustive-deps.rule.d.ts +3 -1
  27. package/dist/esm/rules/exhaustive-deps/exhaustive-deps.rule.js +11 -9
  28. package/dist/esm/rules/exhaustive-deps/exhaustive-deps.rule.js.map +1 -1
  29. package/dist/esm/rules/exhaustive-deps/exhaustive-deps.utils.d.ts +5 -2
  30. package/dist/esm/rules/exhaustive-deps/exhaustive-deps.utils.js +6 -3
  31. package/dist/esm/rules/exhaustive-deps/exhaustive-deps.utils.js.map +1 -1
  32. package/dist/esm/rules/no-rest-destructuring/no-rest-destructuring.rule.d.ts +3 -1
  33. package/dist/esm/rules/no-rest-destructuring/no-rest-destructuring.rule.js.map +1 -1
  34. package/dist/esm/rules/no-rest-destructuring/no-rest-destructuring.utils.d.ts +2 -1
  35. package/dist/esm/rules/stable-query-client/stable-query-client.rule.d.ts +3 -1
  36. package/dist/esm/rules/stable-query-client/stable-query-client.rule.js +4 -1
  37. package/dist/esm/rules/stable-query-client/stable-query-client.rule.js.map +1 -1
  38. package/dist/esm/rules.d.ts +4 -2
  39. package/dist/esm/rules.js.map +1 -1
  40. package/dist/esm/types.d.ts +3 -0
  41. package/dist/esm/utils/ast-utils.d.ts +3 -3
  42. package/dist/esm/utils/ast-utils.js +2 -2
  43. package/dist/esm/utils/ast-utils.js.map +1 -1
  44. package/dist/esm/utils/detect-react-query-imports.d.ts +2 -2
  45. package/package.json +6 -15
  46. package/src/__tests__/exhaustive-deps.test.ts +15 -27
  47. package/src/configs.ts +3 -2
  48. package/src/rules/exhaustive-deps/exhaustive-deps.rule.ts +14 -10
  49. package/src/rules/exhaustive-deps/exhaustive-deps.utils.ts +12 -4
  50. package/src/rules/no-rest-destructuring/no-rest-destructuring.rule.ts +3 -2
  51. package/src/rules/stable-query-client/stable-query-client.rule.ts +7 -3
  52. package/src/rules.ts +2 -0
  53. package/src/types.ts +3 -0
  54. package/src/utils/ast-utils.ts +3 -2
@@ -1 +1 @@
1
- {"version":3,"file":"exhaustive-deps.utils.js","sources":["../../../../src/rules/exhaustive-deps/exhaustive-deps.utils.ts"],"sourcesContent":["import { AST_NODE_TYPES } from '@typescript-eslint/utils'\nimport { ASTUtils } from '../../utils/ast-utils'\nimport type { TSESLint } from '@typescript-eslint/utils'\n\nexport const ExhaustiveDepsUtils = {\n isRelevantReference(params: {\n context: Readonly<TSESLint.RuleContext<string, ReadonlyArray<unknown>>>\n reference: TSESLint.Scope.Reference\n scopeManager: TSESLint.Scope.ScopeManager\n }) {\n const { reference, scopeManager, context } = params\n const component = ASTUtils.getFunctionAncestor(context)\n\n if (\n component !== undefined &&\n !ASTUtils.isDeclaredInNode({\n scopeManager,\n reference,\n functionNode: component,\n })\n ) {\n return false\n }\n\n return (\n reference.identifier.name !== 'undefined' &&\n reference.identifier.parent.type !== AST_NODE_TYPES.NewExpression &&\n !ExhaustiveDepsUtils.isQueryClientReference(reference)\n )\n },\n isQueryClientReference(reference: TSESLint.Scope.Reference) {\n const declarator = reference.resolved?.defs[0]?.node\n\n return (\n declarator?.type === AST_NODE_TYPES.VariableDeclarator &&\n declarator.init?.type === AST_NODE_TYPES.CallExpression &&\n declarator.init.callee.type === AST_NODE_TYPES.Identifier &&\n declarator.init.callee.name === 'useQueryClient'\n )\n },\n}\n"],"names":[],"mappings":";;AAIO,MAAM,sBAAsB;AAAA,EACjC,oBAAoB,QAIjB;AACD,UAAM,EAAE,WAAW,cAAc,QAAA,IAAY;AACvC,UAAA,YAAY,SAAS,oBAAoB,OAAO;AAEtD,QACE,cAAc,UACd,CAAC,SAAS,iBAAiB;AAAA,MACzB;AAAA,MACA;AAAA,MACA,cAAc;AAAA,IAAA,CACf,GACD;AACO,aAAA;AAAA,IACT;AAEA,WACE,UAAU,WAAW,SAAS,eAC9B,UAAU,WAAW,OAAO,SAAS,eAAe,iBACpD,CAAC,oBAAoB,uBAAuB,SAAS;AAAA,EAEzD;AAAA,EACA,uBAAuB,WAAqC;;AAC1D,UAAM,cAAa,qBAAU,aAAV,mBAAoB,KAAK,OAAzB,mBAA6B;AAEhD,YACE,yCAAY,UAAS,eAAe,wBACpC,gBAAW,SAAX,mBAAiB,UAAS,eAAe,kBACzC,WAAW,KAAK,OAAO,SAAS,eAAe,cAC/C,WAAW,KAAK,OAAO,SAAS;AAAA,EAEpC;AACF;"}
1
+ {"version":3,"file":"exhaustive-deps.utils.js","sources":["../../../../src/rules/exhaustive-deps/exhaustive-deps.utils.ts"],"sourcesContent":["import { AST_NODE_TYPES } from '@typescript-eslint/utils'\nimport { ASTUtils } from '../../utils/ast-utils'\nimport type { TSESLint, TSESTree } from '@typescript-eslint/utils'\n\nexport const ExhaustiveDepsUtils = {\n isRelevantReference(params: {\n sourceCode: Readonly<TSESLint.SourceCode>\n reference: TSESLint.Scope.Reference\n scopeManager: TSESLint.Scope.ScopeManager\n node: TSESTree.Node\n }) {\n const { sourceCode, reference, scopeManager, node } = params\n const component = ASTUtils.getFunctionAncestor(sourceCode, node)\n\n if (\n component !== undefined &&\n !ASTUtils.isDeclaredInNode({\n scopeManager,\n reference,\n functionNode: component,\n })\n ) {\n return false\n }\n\n return (\n reference.identifier.name !== 'undefined' &&\n reference.identifier.parent.type !== AST_NODE_TYPES.NewExpression &&\n !ExhaustiveDepsUtils.isInstanceOfKind(reference.identifier.parent) &&\n !ExhaustiveDepsUtils.isQueryClientReference(reference)\n )\n },\n isInstanceOfKind(node: TSESTree.Node) {\n return (\n node.type === AST_NODE_TYPES.BinaryExpression &&\n node.operator === 'instanceof'\n )\n },\n isQueryClientReference(reference: TSESLint.Scope.Reference) {\n const declarator = reference.resolved?.defs[0]?.node\n\n return (\n declarator?.type === AST_NODE_TYPES.VariableDeclarator &&\n declarator.init?.type === AST_NODE_TYPES.CallExpression &&\n declarator.init.callee.type === AST_NODE_TYPES.Identifier &&\n declarator.init.callee.name === 'useQueryClient'\n )\n },\n}\n"],"names":[],"mappings":";;AAIO,MAAM,sBAAsB;AAAA,EACjC,oBAAoB,QAKjB;AACD,UAAM,EAAE,YAAY,WAAW,cAAc,SAAS;AACtD,UAAM,YAAY,SAAS,oBAAoB,YAAY,IAAI;AAE/D,QACE,cAAc,UACd,CAAC,SAAS,iBAAiB;AAAA,MACzB;AAAA,MACA;AAAA,MACA,cAAc;AAAA,IAAA,CACf,GACD;AACO,aAAA;AAAA,IACT;AAGE,WAAA,UAAU,WAAW,SAAS,eAC9B,UAAU,WAAW,OAAO,SAAS,eAAe,iBACpD,CAAC,oBAAoB,iBAAiB,UAAU,WAAW,MAAM,KACjE,CAAC,oBAAoB,uBAAuB,SAAS;AAAA,EAEzD;AAAA,EACA,iBAAiB,MAAqB;AACpC,WACE,KAAK,SAAS,eAAe,oBAC7B,KAAK,aAAa;AAAA,EAEtB;AAAA,EACA,uBAAuB,WAAqC;;AAC1D,UAAM,cAAa,qBAAU,aAAV,mBAAoB,KAAK,OAAzB,mBAA6B;AAEhD,YACE,yCAAY,UAAS,eAAe,wBACpC,gBAAW,SAAX,mBAAiB,UAAS,eAAe,kBACzC,WAAW,KAAK,OAAO,SAAS,eAAe,cAC/C,WAAW,KAAK,OAAO,SAAS;AAAA,EAEpC;AACF;"}
@@ -1,3 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { ExtraRuleDocs } from '../../types';
3
+
2
4
  export declare const name = "no-rest-destructuring";
3
- export declare const rule: ESLintUtils.RuleModule<string, readonly unknown[], ESLintUtils.RuleListener>;
5
+ export declare const rule: ESLintUtils.RuleModule<string, readonly unknown[], ExtraRuleDocs, ESLintUtils.RuleListener>;
@@ -1 +1 @@
1
- {"version":3,"file":"no-rest-destructuring.rule.js","sources":["../../../../src/rules/no-rest-destructuring/no-rest-destructuring.rule.ts"],"sourcesContent":["import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'\nimport { getDocsUrl } from '../../utils/get-docs-url'\nimport { ASTUtils } from '../../utils/ast-utils'\nimport { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'\nimport { NoRestDestructuringUtils } from './no-rest-destructuring.utils'\n\nexport const name = 'no-rest-destructuring'\n\nconst queryHooks = ['useQuery', 'useQueries', 'useInfiniteQuery']\n\nconst createRule = ESLintUtils.RuleCreator(getDocsUrl)\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallows rest destructuring in queries',\n recommended: 'warn' as any,\n },\n messages: {\n objectRestDestructure: `Object rest destructuring on a query will observe all changes to the query, leading to excessive re-renders.`,\n },\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackQueryImports((context, _, helpers) => {\n return {\n CallExpression: (node) => {\n if (\n !ASTUtils.isIdentifierWithOneOfNames(node.callee, queryHooks) ||\n !helpers.isTanstackQueryImport(node.callee) ||\n node.parent.type !== AST_NODE_TYPES.VariableDeclarator\n ) {\n return\n }\n\n const returnValue = node.parent.id\n if (node.callee.name !== 'useQueries') {\n if (NoRestDestructuringUtils.isObjectRestDestructuring(returnValue)) {\n context.report({\n node: node.parent,\n messageId: 'objectRestDestructure',\n })\n }\n return\n }\n\n if (returnValue.type !== AST_NODE_TYPES.ArrayPattern) {\n return\n }\n returnValue.elements.forEach((queryResult) => {\n if (queryResult === null) {\n return\n }\n if (NoRestDestructuringUtils.isObjectRestDestructuring(queryResult)) {\n context.report({\n node: queryResult,\n messageId: 'objectRestDestructure',\n })\n }\n })\n },\n }\n }),\n})\n"],"names":[],"mappings":";;;;;AAMO,MAAM,OAAO;AAEpB,MAAM,aAAa,CAAC,YAAY,cAAc,kBAAkB;AAEhE,MAAM,aAAa,YAAY,YAAY,UAAU;AAE9C,MAAM,OAAO,WAAW;AAAA,EAC7B;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,uBAAuB;AAAA,IACzB;AAAA,IACA,QAAQ,CAAC;AAAA,EACX;AAAA,EACA,gBAAgB,CAAC;AAAA,EAEjB,QAAQ,2BAA2B,CAAC,SAAS,GAAG,YAAY;AACnD,WAAA;AAAA,MACL,gBAAgB,CAAC,SAAS;AACxB,YACE,CAAC,SAAS,2BAA2B,KAAK,QAAQ,UAAU,KAC5D,CAAC,QAAQ,sBAAsB,KAAK,MAAM,KAC1C,KAAK,OAAO,SAAS,eAAe,oBACpC;AACA;AAAA,QACF;AAEM,cAAA,cAAc,KAAK,OAAO;AAC5B,YAAA,KAAK,OAAO,SAAS,cAAc;AACjC,cAAA,yBAAyB,0BAA0B,WAAW,GAAG;AACnE,oBAAQ,OAAO;AAAA,cACb,MAAM,KAAK;AAAA,cACX,WAAW;AAAA,YAAA,CACZ;AAAA,UACH;AACA;AAAA,QACF;AAEI,YAAA,YAAY,SAAS,eAAe,cAAc;AACpD;AAAA,QACF;AACY,oBAAA,SAAS,QAAQ,CAAC,gBAAgB;AAC5C,cAAI,gBAAgB,MAAM;AACxB;AAAA,UACF;AACI,cAAA,yBAAyB,0BAA0B,WAAW,GAAG;AACnE,oBAAQ,OAAO;AAAA,cACb,MAAM;AAAA,cACN,WAAW;AAAA,YAAA,CACZ;AAAA,UACH;AAAA,QAAA,CACD;AAAA,MACH;AAAA,IAAA;AAAA,EACF,CACD;AACH,CAAC;"}
1
+ {"version":3,"file":"no-rest-destructuring.rule.js","sources":["../../../../src/rules/no-rest-destructuring/no-rest-destructuring.rule.ts"],"sourcesContent":["import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils'\nimport { getDocsUrl } from '../../utils/get-docs-url'\nimport { ASTUtils } from '../../utils/ast-utils'\nimport { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'\nimport { NoRestDestructuringUtils } from './no-rest-destructuring.utils'\nimport type { ExtraRuleDocs } from '../../types'\n\nexport const name = 'no-rest-destructuring'\n\nconst queryHooks = ['useQuery', 'useQueries', 'useInfiniteQuery']\n\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Disallows rest destructuring in queries',\n recommended: 'warn',\n },\n messages: {\n objectRestDestructure: `Object rest destructuring on a query will observe all changes to the query, leading to excessive re-renders.`,\n },\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackQueryImports((context, _, helpers) => {\n return {\n CallExpression: (node) => {\n if (\n !ASTUtils.isIdentifierWithOneOfNames(node.callee, queryHooks) ||\n !helpers.isTanstackQueryImport(node.callee) ||\n node.parent.type !== AST_NODE_TYPES.VariableDeclarator\n ) {\n return\n }\n\n const returnValue = node.parent.id\n if (node.callee.name !== 'useQueries') {\n if (NoRestDestructuringUtils.isObjectRestDestructuring(returnValue)) {\n context.report({\n node: node.parent,\n messageId: 'objectRestDestructure',\n })\n }\n return\n }\n\n if (returnValue.type !== AST_NODE_TYPES.ArrayPattern) {\n return\n }\n returnValue.elements.forEach((queryResult) => {\n if (queryResult === null) {\n return\n }\n if (NoRestDestructuringUtils.isObjectRestDestructuring(queryResult)) {\n context.report({\n node: queryResult,\n messageId: 'objectRestDestructure',\n })\n }\n })\n },\n }\n }),\n})\n"],"names":[],"mappings":";;;;;AAOO,MAAM,OAAO;AAEpB,MAAM,aAAa,CAAC,YAAY,cAAc,kBAAkB;AAEhE,MAAM,aAAa,YAAY,YAA2B,UAAU;AAE7D,MAAM,OAAO,WAAW;AAAA,EAC7B;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,uBAAuB;AAAA,IACzB;AAAA,IACA,QAAQ,CAAC;AAAA,EACX;AAAA,EACA,gBAAgB,CAAC;AAAA,EAEjB,QAAQ,2BAA2B,CAAC,SAAS,GAAG,YAAY;AACnD,WAAA;AAAA,MACL,gBAAgB,CAAC,SAAS;AACxB,YACE,CAAC,SAAS,2BAA2B,KAAK,QAAQ,UAAU,KAC5D,CAAC,QAAQ,sBAAsB,KAAK,MAAM,KAC1C,KAAK,OAAO,SAAS,eAAe,oBACpC;AACA;AAAA,QACF;AAEM,cAAA,cAAc,KAAK,OAAO;AAC5B,YAAA,KAAK,OAAO,SAAS,cAAc;AACjC,cAAA,yBAAyB,0BAA0B,WAAW,GAAG;AACnE,oBAAQ,OAAO;AAAA,cACb,MAAM,KAAK;AAAA,cACX,WAAW;AAAA,YAAA,CACZ;AAAA,UACH;AACA;AAAA,QACF;AAEI,YAAA,YAAY,SAAS,eAAe,cAAc;AACpD;AAAA,QACF;AACY,oBAAA,SAAS,QAAQ,CAAC,gBAAgB;AAC5C,cAAI,gBAAgB,MAAM;AACxB;AAAA,UACF;AACI,cAAA,yBAAyB,0BAA0B,WAAW,GAAG;AACnE,oBAAQ,OAAO;AAAA,cACb,MAAM;AAAA,cACN,WAAW;AAAA,YAAA,CACZ;AAAA,UACH;AAAA,QAAA,CACD;AAAA,MACH;AAAA,IAAA;AAAA,EACF,CACD;AACH,CAAC;"}
@@ -1,4 +1,5 @@
1
- import type { TSESTree } from '@typescript-eslint/utils';
1
+ import { TSESTree } from '@typescript-eslint/utils';
2
+
2
3
  export declare const NoRestDestructuringUtils: {
3
4
  isObjectRestDestructuring(node: TSESTree.Node): boolean;
4
5
  };
@@ -1,3 +1,5 @@
1
1
  import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { ExtraRuleDocs } from '../../types';
3
+
2
4
  export declare const name = "stable-query-client";
3
- export declare const rule: ESLintUtils.RuleModule<string, readonly unknown[], ESLintUtils.RuleListener>;
5
+ export declare const rule: ESLintUtils.RuleModule<string, readonly unknown[], ExtraRuleDocs, ESLintUtils.RuleListener>;
@@ -33,7 +33,10 @@ const rule = createRule({
33
33
  )) {
34
34
  return;
35
35
  }
36
- const fnAncestor = ASTUtils.getFunctionAncestor(context);
36
+ const fnAncestor = ASTUtils.getFunctionAncestor(
37
+ context.sourceCode,
38
+ node
39
+ );
37
40
  const isReactServerComponent = (fnAncestor == null ? void 0 : fnAncestor.async) === true;
38
41
  if (!ASTUtils.isValidReactComponentOrHookName(fnAncestor == null ? void 0 : fnAncestor.id) || isReactServerComponent) {
39
42
  return;
@@ -1 +1 @@
1
- {"version":3,"file":"stable-query-client.rule.js","sources":["../../../../src/rules/stable-query-client/stable-query-client.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 { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'\nimport type { TSESLint } from '@typescript-eslint/utils'\n\nexport const name = 'stable-query-client'\n\nconst createRule = ESLintUtils.RuleCreator(getDocsUrl)\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Makes sure that QueryClient is stable',\n recommended: 'error' as any,\n },\n messages: {\n unstable: [\n 'QueryClient is not stable. It should be either extracted from the component or wrapped in React.useState.',\n 'See https://tkdodo.eu/blog/react-query-fa-qs#2-the-queryclient-is-not-stable',\n ].join('\\n'),\n fixTo: 'Fix to {{result}}',\n },\n hasSuggestions: true,\n fixable: 'code',\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackQueryImports((context, _, helpers) => {\n return {\n NewExpression: (node) => {\n if (\n node.callee.type !== AST_NODE_TYPES.Identifier ||\n node.callee.name !== 'QueryClient' ||\n node.parent.type !== AST_NODE_TYPES.VariableDeclarator ||\n !helpers.isSpecificTanstackQueryImport(\n node.callee,\n '@tanstack/react-query',\n )\n ) {\n return\n }\n\n const fnAncestor = ASTUtils.getFunctionAncestor(context)\n const isReactServerComponent = fnAncestor?.async === true\n\n if (\n !ASTUtils.isValidReactComponentOrHookName(fnAncestor?.id) ||\n isReactServerComponent\n ) {\n return\n }\n\n context.report({\n node: node.parent,\n messageId: 'unstable',\n fix: (() => {\n const { parent } = node\n\n if (parent.id.type !== AST_NODE_TYPES.Identifier) {\n return\n }\n\n const nodeText = context.getSourceCode().getText(node)\n const variableName = parent.id.name\n\n return (fixer: TSESLint.RuleFixer) => {\n return fixer.replaceTextRange(\n [parent.range[0], parent.range[1]],\n `[${variableName}] = React.useState(() => ${nodeText})`,\n )\n }\n })(),\n })\n },\n }\n }),\n})\n"],"names":[],"mappings":";;;;AAMO,MAAM,OAAO;AAEpB,MAAM,aAAa,YAAY,YAAY,UAAU;AAE9C,MAAM,OAAO,WAAW;AAAA,EAC7B;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,UAAU;AAAA,QACR;AAAA,QACA;AAAA,MAAA,EACA,KAAK,IAAI;AAAA,MACX,OAAO;AAAA,IACT;AAAA,IACA,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,QAAQ,CAAC;AAAA,EACX;AAAA,EACA,gBAAgB,CAAC;AAAA,EAEjB,QAAQ,2BAA2B,CAAC,SAAS,GAAG,YAAY;AACnD,WAAA;AAAA,MACL,eAAe,CAAC,SAAS;AACvB,YACE,KAAK,OAAO,SAAS,eAAe,cACpC,KAAK,OAAO,SAAS,iBACrB,KAAK,OAAO,SAAS,eAAe,sBACpC,CAAC,QAAQ;AAAA,UACP,KAAK;AAAA,UACL;AAAA,QAAA,GAEF;AACA;AAAA,QACF;AAEM,cAAA,aAAa,SAAS,oBAAoB,OAAO;AACjD,cAAA,0BAAyB,yCAAY,WAAU;AAErD,YACE,CAAC,SAAS,gCAAgC,yCAAY,EAAE,KACxD,wBACA;AACA;AAAA,QACF;AAEA,gBAAQ,OAAO;AAAA,UACb,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,UACX,MAAM,MAAM;AACJ,kBAAA,EAAE,OAAW,IAAA;AAEnB,gBAAI,OAAO,GAAG,SAAS,eAAe,YAAY;AAChD;AAAA,YACF;AAEA,kBAAM,WAAW,QAAQ,cAAc,EAAE,QAAQ,IAAI;AAC/C,kBAAA,eAAe,OAAO,GAAG;AAE/B,mBAAO,CAAC,UAA8B;AACpC,qBAAO,MAAM;AAAA,gBACX,CAAC,OAAO,MAAM,CAAC,GAAG,OAAO,MAAM,CAAC,CAAC;AAAA,gBACjC,IAAI,YAAY,4BAA4B,QAAQ;AAAA,cAAA;AAAA,YACtD;AAAA,UACF,GACC;AAAA,QAAA,CACJ;AAAA,MACH;AAAA,IAAA;AAAA,EACF,CACD;AACH,CAAC;"}
1
+ {"version":3,"file":"stable-query-client.rule.js","sources":["../../../../src/rules/stable-query-client/stable-query-client.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 { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'\nimport type { TSESLint } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from '../../types'\n\nexport const name = 'stable-query-client'\n\nconst createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)\n\nexport const rule = createRule({\n name,\n meta: {\n type: 'problem',\n docs: {\n description: 'Makes sure that QueryClient is stable',\n recommended: 'error',\n },\n messages: {\n unstable: [\n 'QueryClient is not stable. It should be either extracted from the component or wrapped in React.useState.',\n 'See https://tkdodo.eu/blog/react-query-fa-qs#2-the-queryclient-is-not-stable',\n ].join('\\n'),\n fixTo: 'Fix to {{result}}',\n },\n hasSuggestions: true,\n fixable: 'code',\n schema: [],\n },\n defaultOptions: [],\n\n create: detectTanstackQueryImports((context, _, helpers) => {\n return {\n NewExpression: (node) => {\n if (\n node.callee.type !== AST_NODE_TYPES.Identifier ||\n node.callee.name !== 'QueryClient' ||\n node.parent.type !== AST_NODE_TYPES.VariableDeclarator ||\n !helpers.isSpecificTanstackQueryImport(\n node.callee,\n '@tanstack/react-query',\n )\n ) {\n return\n }\n\n const fnAncestor = ASTUtils.getFunctionAncestor(\n context.sourceCode,\n node,\n )\n const isReactServerComponent = fnAncestor?.async === true\n\n if (\n !ASTUtils.isValidReactComponentOrHookName(fnAncestor?.id) ||\n isReactServerComponent\n ) {\n return\n }\n\n context.report({\n node: node.parent,\n messageId: 'unstable',\n fix: (() => {\n const { parent } = node\n\n if (parent.id.type !== AST_NODE_TYPES.Identifier) {\n return\n }\n\n const nodeText = context.getSourceCode().getText(node)\n const variableName = parent.id.name\n\n return (fixer: TSESLint.RuleFixer) => {\n return fixer.replaceTextRange(\n [parent.range[0], parent.range[1]],\n `[${variableName}] = React.useState(() => ${nodeText})`,\n )\n }\n })(),\n })\n },\n }\n }),\n})\n"],"names":[],"mappings":";;;;AAOO,MAAM,OAAO;AAEpB,MAAM,aAAa,YAAY,YAA2B,UAAU;AAE7D,MAAM,OAAO,WAAW;AAAA,EAC7B;AAAA,EACA,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aAAa;AAAA,MACb,aAAa;AAAA,IACf;AAAA,IACA,UAAU;AAAA,MACR,UAAU;AAAA,QACR;AAAA,QACA;AAAA,MAAA,EACA,KAAK,IAAI;AAAA,MACX,OAAO;AAAA,IACT;AAAA,IACA,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,QAAQ,CAAC;AAAA,EACX;AAAA,EACA,gBAAgB,CAAC;AAAA,EAEjB,QAAQ,2BAA2B,CAAC,SAAS,GAAG,YAAY;AACnD,WAAA;AAAA,MACL,eAAe,CAAC,SAAS;AACvB,YACE,KAAK,OAAO,SAAS,eAAe,cACpC,KAAK,OAAO,SAAS,iBACrB,KAAK,OAAO,SAAS,eAAe,sBACpC,CAAC,QAAQ;AAAA,UACP,KAAK;AAAA,UACL;AAAA,QAAA,GAEF;AACA;AAAA,QACF;AAEA,cAAM,aAAa,SAAS;AAAA,UAC1B,QAAQ;AAAA,UACR;AAAA,QAAA;AAEI,cAAA,0BAAyB,yCAAY,WAAU;AAErD,YACE,CAAC,SAAS,gCAAgC,yCAAY,EAAE,KACxD,wBACA;AACA;AAAA,QACF;AAEA,gBAAQ,OAAO;AAAA,UACb,MAAM,KAAK;AAAA,UACX,WAAW;AAAA,UACX,MAAM,MAAM;AACJ,kBAAA,EAAE,OAAW,IAAA;AAEnB,gBAAI,OAAO,GAAG,SAAS,eAAe,YAAY;AAChD;AAAA,YACF;AAEA,kBAAM,WAAW,QAAQ,cAAc,EAAE,QAAQ,IAAI;AAC/C,kBAAA,eAAe,OAAO,GAAG;AAE/B,mBAAO,CAAC,UAA8B;AACpC,qBAAO,MAAM;AAAA,gBACX,CAAC,OAAO,MAAM,CAAC,GAAG,OAAO,MAAM,CAAC,CAAC;AAAA,gBACjC,IAAI,YAAY,4BAA4B,QAAQ;AAAA,cAAA;AAAA,YACtD;AAAA,UACF,GACC;AAAA,QAAA,CACJ;AAAA,MACH;AAAA,IAAA;AAAA,EACF,CACD;AACH,CAAC;"}
@@ -1,2 +1,4 @@
1
- import type { ESLintUtils } from '@typescript-eslint/utils';
2
- export declare const rules: Record<string, ESLintUtils.RuleModule<string, ReadonlyArray<unknown>, ESLintUtils.RuleListener>>;
1
+ import { ESLintUtils } from '@typescript-eslint/utils';
2
+ import { ExtraRuleDocs } from './types.js';
3
+
4
+ export declare const rules: Record<string, ESLintUtils.RuleModule<string, ReadonlyArray<unknown>, ExtraRuleDocs, ESLintUtils.RuleListener>>;
@@ -1 +1 @@
1
- {"version":3,"file":"rules.js","sources":["../../src/rules.ts"],"sourcesContent":["import * as exhaustiveDeps from './rules/exhaustive-deps/exhaustive-deps.rule'\nimport * as stableQueryClient from './rules/stable-query-client/stable-query-client.rule'\nimport * as noRestDestructuring from './rules/no-rest-destructuring/no-rest-destructuring.rule'\nimport type { ESLintUtils } from '@typescript-eslint/utils'\n\nexport const rules: Record<\n string,\n ESLintUtils.RuleModule<\n string,\n ReadonlyArray<unknown>,\n ESLintUtils.RuleListener\n >\n> = {\n [exhaustiveDeps.name]: exhaustiveDeps.rule,\n [stableQueryClient.name]: stableQueryClient.rule,\n [noRestDestructuring.name]: noRestDestructuring.rule,\n}\n"],"names":["exhaustiveDeps.name","exhaustiveDeps.rule","stableQueryClient.name","stableQueryClient.rule","noRestDestructuring.name","noRestDestructuring.rule"],"mappings":";;;AAKO,MAAM,QAOT;AAAA,EACF,CAACA,IAAmB,GAAGC;AAAAA,EACvB,CAACC,MAAsB,GAAGC;AAAAA,EAC1B,CAACC,MAAwB,GAAGC;AAC9B;"}
1
+ {"version":3,"file":"rules.js","sources":["../../src/rules.ts"],"sourcesContent":["import * as exhaustiveDeps from './rules/exhaustive-deps/exhaustive-deps.rule'\nimport * as stableQueryClient from './rules/stable-query-client/stable-query-client.rule'\nimport * as noRestDestructuring from './rules/no-rest-destructuring/no-rest-destructuring.rule'\nimport type { ESLintUtils } from '@typescript-eslint/utils'\nimport type { ExtraRuleDocs } from './types'\n\nexport const rules: Record<\n string,\n ESLintUtils.RuleModule<\n string,\n ReadonlyArray<unknown>,\n ExtraRuleDocs,\n ESLintUtils.RuleListener\n >\n> = {\n [exhaustiveDeps.name]: exhaustiveDeps.rule,\n [stableQueryClient.name]: stableQueryClient.rule,\n [noRestDestructuring.name]: noRestDestructuring.rule,\n}\n"],"names":["exhaustiveDeps.name","exhaustiveDeps.rule","stableQueryClient.name","stableQueryClient.rule","noRestDestructuring.name","noRestDestructuring.rule"],"mappings":";;;AAMO,MAAM,QAQT;AAAA,EACF,CAACA,IAAmB,GAAGC;AAAAA,EACvB,CAACC,MAAsB,GAAGC;AAAAA,EAC1B,CAACC,MAAwB,GAAGC;AAC9B;"}
@@ -0,0 +1,3 @@
1
+ export type ExtraRuleDocs = {
2
+ recommended: 'strict' | 'error' | 'warn';
3
+ };
@@ -1,5 +1,5 @@
1
- import { AST_NODE_TYPES } from '@typescript-eslint/utils';
2
- import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
1
+ import { AST_NODE_TYPES, TSESLint, TSESTree } from '@typescript-eslint/utils';
2
+
3
3
  export declare const ASTUtils: {
4
4
  isNodeOfOneOf<T extends AST_NODE_TYPES>(node: TSESTree.Node, types: readonly T[]): node is TSESTree.Node & {
5
5
  type: T;
@@ -28,7 +28,7 @@ export declare const ASTUtils: {
28
28
  }): Array<TSESLint.Scope.Reference>;
29
29
  mapKeyNodeToText(node: TSESTree.Node, sourceCode: Readonly<TSESLint.SourceCode>): string;
30
30
  isValidReactComponentOrHookName(identifier: TSESTree.Identifier | null | undefined): boolean;
31
- getFunctionAncestor(context: Readonly<TSESLint.RuleContext<string, ReadonlyArray<unknown>>>): TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclarationWithOptionalName | TSESTree.FunctionExpression | undefined;
31
+ getFunctionAncestor(sourceCode: Readonly<TSESLint.SourceCode>, node: TSESTree.Node): TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclarationWithOptionalName | TSESTree.FunctionExpression | undefined;
32
32
  getReferencedExpressionByIdentifier(params: {
33
33
  node: TSESTree.Node;
34
34
  context: Readonly<TSESLint.RuleContext<string, ReadonlyArray<unknown>>>;
@@ -146,9 +146,9 @@ const ASTUtils = {
146
146
  isValidReactComponentOrHookName(identifier) {
147
147
  return identifier !== null && identifier !== void 0 && /^(use|[A-Z])/.test(identifier.name);
148
148
  },
149
- getFunctionAncestor(context) {
149
+ getFunctionAncestor(sourceCode, node) {
150
150
  var _a;
151
- for (const ancestor of context.getAncestors()) {
151
+ for (const ancestor of sourceCode.getAncestors(node)) {
152
152
  if (ancestor.type === AST_NODE_TYPES.FunctionDeclaration) {
153
153
  return ancestor;
154
154
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ast-utils.js","sources":["../../../src/utils/ast-utils.ts"],"sourcesContent":["import { AST_NODE_TYPES } from '@typescript-eslint/utils'\nimport { uniqueBy } from './unique-by'\nimport type { TSESLint, TSESTree } from '@typescript-eslint/utils'\n\nexport const ASTUtils = {\n isNodeOfOneOf<T extends AST_NODE_TYPES>(\n node: TSESTree.Node,\n types: ReadonlyArray<T>,\n ): node is TSESTree.Node & { type: T } {\n return types.includes(node.type as T)\n },\n isIdentifier(node: TSESTree.Node): node is TSESTree.Identifier {\n return node.type === AST_NODE_TYPES.Identifier\n },\n isIdentifierWithName(\n node: TSESTree.Node,\n name: string,\n ): node is TSESTree.Identifier {\n return ASTUtils.isIdentifier(node) && node.name === name\n },\n isIdentifierWithOneOfNames<T extends Array<string>>(\n node: TSESTree.Node,\n name: T,\n ): node is TSESTree.Identifier & { name: T[number] } {\n return ASTUtils.isIdentifier(node) && name.includes(node.name)\n },\n isProperty(node: TSESTree.Node): node is TSESTree.Property {\n return node.type === AST_NODE_TYPES.Property\n },\n isObjectExpression(node: TSESTree.Node): node is TSESTree.ObjectExpression {\n return node.type === AST_NODE_TYPES.ObjectExpression\n },\n isPropertyWithIdentifierKey(\n node: TSESTree.Node,\n key: string,\n ): node is TSESTree.Property {\n return (\n ASTUtils.isProperty(node) && ASTUtils.isIdentifierWithName(node.key, key)\n )\n },\n findPropertyWithIdentifierKey(\n properties: Array<TSESTree.ObjectLiteralElement>,\n key: string,\n ): TSESTree.Property | undefined {\n return properties.find((x) =>\n ASTUtils.isPropertyWithIdentifierKey(x, key),\n ) as TSESTree.Property | undefined\n },\n getNestedIdentifiers(node: TSESTree.Node): Array<TSESTree.Identifier> {\n const identifiers: Array<TSESTree.Identifier> = []\n\n if (ASTUtils.isIdentifier(node)) {\n identifiers.push(node)\n }\n\n if ('arguments' in node) {\n node.arguments.forEach((x) => {\n identifiers.push(...ASTUtils.getNestedIdentifiers(x))\n })\n }\n\n if ('elements' in node) {\n node.elements.forEach((x) => {\n if (x !== null) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(x))\n }\n })\n }\n\n if ('properties' in node) {\n node.properties.forEach((x) => {\n identifiers.push(...ASTUtils.getNestedIdentifiers(x))\n })\n }\n\n if ('expressions' in node) {\n node.expressions.forEach((x) => {\n identifiers.push(...ASTUtils.getNestedIdentifiers(x))\n })\n }\n\n if ('left' in node) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.left))\n }\n\n if ('right' in node) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.right))\n }\n\n if (node.type === AST_NODE_TYPES.Property) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.value))\n }\n\n if (node.type === AST_NODE_TYPES.SpreadElement) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.argument))\n }\n\n if (node.type === AST_NODE_TYPES.MemberExpression) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.object))\n }\n\n if (node.type === AST_NODE_TYPES.UnaryExpression) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.argument))\n }\n\n if (node.type === AST_NODE_TYPES.ChainExpression) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.expression))\n }\n\n if (node.type === AST_NODE_TYPES.TSNonNullExpression) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.expression))\n }\n\n return identifiers\n },\n isAncestorIsCallee(identifier: TSESTree.Node) {\n let previousNode = identifier\n let currentNode = identifier.parent\n\n while (currentNode !== undefined) {\n if (\n currentNode.type === AST_NODE_TYPES.CallExpression &&\n currentNode.callee === previousNode\n ) {\n return true\n }\n\n if (currentNode.type !== AST_NODE_TYPES.MemberExpression) {\n return false\n }\n\n previousNode = currentNode\n currentNode = currentNode.parent\n }\n\n return false\n },\n traverseUpOnly(\n identifier: TSESTree.Node,\n allowedNodeTypes: Array<AST_NODE_TYPES>,\n ): TSESTree.Node {\n const parent = identifier.parent\n\n if (parent !== undefined && allowedNodeTypes.includes(parent.type)) {\n return ASTUtils.traverseUpOnly(parent, allowedNodeTypes)\n }\n\n return identifier\n },\n isDeclaredInNode(params: {\n functionNode: TSESTree.Node\n reference: TSESLint.Scope.Reference\n scopeManager: TSESLint.Scope.ScopeManager\n }) {\n const { functionNode, reference, scopeManager } = params\n const scope = scopeManager.acquire(functionNode)\n\n if (scope === null) {\n return false\n }\n\n return scope.set.has(reference.identifier.name)\n },\n getExternalRefs(params: {\n scopeManager: TSESLint.Scope.ScopeManager\n sourceCode: Readonly<TSESLint.SourceCode>\n node: TSESTree.Node\n }): Array<TSESLint.Scope.Reference> {\n const { scopeManager, sourceCode, node } = params\n const scope = scopeManager.acquire(node)\n\n if (scope === null) {\n return []\n }\n\n const references = scope.references\n .filter((x) => x.isRead() && !scope.set.has(x.identifier.name))\n .map((x) => {\n const referenceNode = ASTUtils.traverseUpOnly(x.identifier, [\n AST_NODE_TYPES.MemberExpression,\n AST_NODE_TYPES.Identifier,\n ])\n\n return {\n variable: x,\n node: referenceNode,\n text: sourceCode.getText(referenceNode),\n }\n })\n\n const localRefIds = new Set(\n [...scope.set.values()].map((x) => sourceCode.getText(x.identifiers[0])),\n )\n\n const externalRefs = references.filter(\n (x) => x.variable.resolved === null || !localRefIds.has(x.text),\n )\n\n return uniqueBy(externalRefs, (x) => x.text).map((x) => x.variable)\n },\n mapKeyNodeToText(\n node: TSESTree.Node,\n sourceCode: Readonly<TSESLint.SourceCode>,\n ) {\n return sourceCode.getText(\n ASTUtils.traverseUpOnly(node, [\n AST_NODE_TYPES.MemberExpression,\n AST_NODE_TYPES.Identifier,\n ]),\n )\n },\n isValidReactComponentOrHookName(\n identifier: TSESTree.Identifier | null | undefined,\n ) {\n return (\n identifier !== null &&\n identifier !== undefined &&\n /^(use|[A-Z])/.test(identifier.name)\n )\n },\n getFunctionAncestor(\n context: Readonly<TSESLint.RuleContext<string, ReadonlyArray<unknown>>>,\n ) {\n for (const ancestor of context.getAncestors()) {\n if (ancestor.type === AST_NODE_TYPES.FunctionDeclaration) {\n return ancestor\n }\n\n if (\n ancestor.parent?.type === AST_NODE_TYPES.VariableDeclarator &&\n ancestor.parent.id.type === AST_NODE_TYPES.Identifier &&\n ASTUtils.isNodeOfOneOf(ancestor, [\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n ])\n ) {\n return ancestor\n }\n }\n\n return undefined\n },\n getReferencedExpressionByIdentifier(params: {\n node: TSESTree.Node\n context: Readonly<TSESLint.RuleContext<string, ReadonlyArray<unknown>>>\n }) {\n const { node, context } = params\n\n const resolvedNode = context\n .getScope()\n .references.find((ref) => ref.identifier === node)?.resolved?.defs[0]\n ?.node\n\n if (resolvedNode?.type !== AST_NODE_TYPES.VariableDeclarator) {\n return null\n }\n\n return resolvedNode.init\n },\n getClosestVariableDeclarator(node: TSESTree.Node) {\n let currentNode: TSESTree.Node | undefined = node\n\n while (currentNode.type !== AST_NODE_TYPES.Program) {\n if (currentNode.type === AST_NODE_TYPES.VariableDeclarator) {\n return currentNode\n }\n\n currentNode = currentNode.parent\n }\n\n return undefined\n },\n getNestedReturnStatements(\n node: TSESTree.Node,\n ): Array<TSESTree.ReturnStatement> {\n const returnStatements: Array<TSESTree.ReturnStatement> = []\n\n if (node.type === AST_NODE_TYPES.ReturnStatement) {\n returnStatements.push(node)\n }\n\n if ('body' in node && node.body !== undefined && node.body !== null) {\n Array.isArray(node.body)\n ? node.body.forEach((x) => {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(x))\n })\n : returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.body),\n )\n }\n\n if ('consequent' in node) {\n Array.isArray(node.consequent)\n ? node.consequent.forEach((x) => {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(x))\n })\n : returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.consequent),\n )\n }\n\n if ('alternate' in node && node.alternate !== null) {\n Array.isArray(node.alternate)\n ? node.alternate.forEach((x) => {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(x))\n })\n : returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.alternate),\n )\n }\n\n if ('cases' in node) {\n node.cases.forEach((x) => {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(x))\n })\n }\n\n if ('block' in node) {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(node.block))\n }\n\n if ('handler' in node && node.handler !== null) {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(node.handler))\n }\n\n if ('finalizer' in node && node.finalizer !== null) {\n returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.finalizer),\n )\n }\n\n if (\n 'expression' in node &&\n node.expression !== true &&\n node.expression !== false\n ) {\n returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.expression),\n )\n }\n\n if ('test' in node && node.test !== null) {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(node.test))\n }\n\n return returnStatements\n },\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ast-utils.js","sources":["../../../src/utils/ast-utils.ts"],"sourcesContent":["import { AST_NODE_TYPES } from '@typescript-eslint/utils'\nimport { uniqueBy } from './unique-by'\nimport type { TSESLint, TSESTree } from '@typescript-eslint/utils'\n\nexport const ASTUtils = {\n isNodeOfOneOf<T extends AST_NODE_TYPES>(\n node: TSESTree.Node,\n types: ReadonlyArray<T>,\n ): node is TSESTree.Node & { type: T } {\n return types.includes(node.type as T)\n },\n isIdentifier(node: TSESTree.Node): node is TSESTree.Identifier {\n return node.type === AST_NODE_TYPES.Identifier\n },\n isIdentifierWithName(\n node: TSESTree.Node,\n name: string,\n ): node is TSESTree.Identifier {\n return ASTUtils.isIdentifier(node) && node.name === name\n },\n isIdentifierWithOneOfNames<T extends Array<string>>(\n node: TSESTree.Node,\n name: T,\n ): node is TSESTree.Identifier & { name: T[number] } {\n return ASTUtils.isIdentifier(node) && name.includes(node.name)\n },\n isProperty(node: TSESTree.Node): node is TSESTree.Property {\n return node.type === AST_NODE_TYPES.Property\n },\n isObjectExpression(node: TSESTree.Node): node is TSESTree.ObjectExpression {\n return node.type === AST_NODE_TYPES.ObjectExpression\n },\n isPropertyWithIdentifierKey(\n node: TSESTree.Node,\n key: string,\n ): node is TSESTree.Property {\n return (\n ASTUtils.isProperty(node) && ASTUtils.isIdentifierWithName(node.key, key)\n )\n },\n findPropertyWithIdentifierKey(\n properties: Array<TSESTree.ObjectLiteralElement>,\n key: string,\n ): TSESTree.Property | undefined {\n return properties.find((x) =>\n ASTUtils.isPropertyWithIdentifierKey(x, key),\n ) as TSESTree.Property | undefined\n },\n getNestedIdentifiers(node: TSESTree.Node): Array<TSESTree.Identifier> {\n const identifiers: Array<TSESTree.Identifier> = []\n\n if (ASTUtils.isIdentifier(node)) {\n identifiers.push(node)\n }\n\n if ('arguments' in node) {\n node.arguments.forEach((x) => {\n identifiers.push(...ASTUtils.getNestedIdentifiers(x))\n })\n }\n\n if ('elements' in node) {\n node.elements.forEach((x) => {\n if (x !== null) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(x))\n }\n })\n }\n\n if ('properties' in node) {\n node.properties.forEach((x) => {\n identifiers.push(...ASTUtils.getNestedIdentifiers(x))\n })\n }\n\n if ('expressions' in node) {\n node.expressions.forEach((x) => {\n identifiers.push(...ASTUtils.getNestedIdentifiers(x))\n })\n }\n\n if ('left' in node) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.left))\n }\n\n if ('right' in node) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.right))\n }\n\n if (node.type === AST_NODE_TYPES.Property) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.value))\n }\n\n if (node.type === AST_NODE_TYPES.SpreadElement) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.argument))\n }\n\n if (node.type === AST_NODE_TYPES.MemberExpression) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.object))\n }\n\n if (node.type === AST_NODE_TYPES.UnaryExpression) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.argument))\n }\n\n if (node.type === AST_NODE_TYPES.ChainExpression) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.expression))\n }\n\n if (node.type === AST_NODE_TYPES.TSNonNullExpression) {\n identifiers.push(...ASTUtils.getNestedIdentifiers(node.expression))\n }\n\n return identifiers\n },\n isAncestorIsCallee(identifier: TSESTree.Node) {\n let previousNode = identifier\n let currentNode = identifier.parent\n\n while (currentNode !== undefined) {\n if (\n currentNode.type === AST_NODE_TYPES.CallExpression &&\n currentNode.callee === previousNode\n ) {\n return true\n }\n\n if (currentNode.type !== AST_NODE_TYPES.MemberExpression) {\n return false\n }\n\n previousNode = currentNode\n currentNode = currentNode.parent\n }\n\n return false\n },\n traverseUpOnly(\n identifier: TSESTree.Node,\n allowedNodeTypes: Array<AST_NODE_TYPES>,\n ): TSESTree.Node {\n const parent = identifier.parent\n\n if (parent !== undefined && allowedNodeTypes.includes(parent.type)) {\n return ASTUtils.traverseUpOnly(parent, allowedNodeTypes)\n }\n\n return identifier\n },\n isDeclaredInNode(params: {\n functionNode: TSESTree.Node\n reference: TSESLint.Scope.Reference\n scopeManager: TSESLint.Scope.ScopeManager\n }) {\n const { functionNode, reference, scopeManager } = params\n const scope = scopeManager.acquire(functionNode)\n\n if (scope === null) {\n return false\n }\n\n return scope.set.has(reference.identifier.name)\n },\n getExternalRefs(params: {\n scopeManager: TSESLint.Scope.ScopeManager\n sourceCode: Readonly<TSESLint.SourceCode>\n node: TSESTree.Node\n }): Array<TSESLint.Scope.Reference> {\n const { scopeManager, sourceCode, node } = params\n const scope = scopeManager.acquire(node)\n\n if (scope === null) {\n return []\n }\n\n const references = scope.references\n .filter((x) => x.isRead() && !scope.set.has(x.identifier.name))\n .map((x) => {\n const referenceNode = ASTUtils.traverseUpOnly(x.identifier, [\n AST_NODE_TYPES.MemberExpression,\n AST_NODE_TYPES.Identifier,\n ])\n\n return {\n variable: x,\n node: referenceNode,\n text: sourceCode.getText(referenceNode),\n }\n })\n\n const localRefIds = new Set(\n [...scope.set.values()].map((x) => sourceCode.getText(x.identifiers[0])),\n )\n\n const externalRefs = references.filter(\n (x) => x.variable.resolved === null || !localRefIds.has(x.text),\n )\n\n return uniqueBy(externalRefs, (x) => x.text).map((x) => x.variable)\n },\n mapKeyNodeToText(\n node: TSESTree.Node,\n sourceCode: Readonly<TSESLint.SourceCode>,\n ) {\n return sourceCode.getText(\n ASTUtils.traverseUpOnly(node, [\n AST_NODE_TYPES.MemberExpression,\n AST_NODE_TYPES.Identifier,\n ]),\n )\n },\n isValidReactComponentOrHookName(\n identifier: TSESTree.Identifier | null | undefined,\n ) {\n return (\n identifier !== null &&\n identifier !== undefined &&\n /^(use|[A-Z])/.test(identifier.name)\n )\n },\n getFunctionAncestor(\n sourceCode: Readonly<TSESLint.SourceCode>,\n node: TSESTree.Node,\n ) {\n for (const ancestor of sourceCode.getAncestors(node)) {\n if (ancestor.type === AST_NODE_TYPES.FunctionDeclaration) {\n return ancestor\n }\n\n if (\n ancestor.parent?.type === AST_NODE_TYPES.VariableDeclarator &&\n ancestor.parent.id.type === AST_NODE_TYPES.Identifier &&\n ASTUtils.isNodeOfOneOf(ancestor, [\n AST_NODE_TYPES.FunctionDeclaration,\n AST_NODE_TYPES.FunctionExpression,\n AST_NODE_TYPES.ArrowFunctionExpression,\n ])\n ) {\n return ancestor\n }\n }\n\n return undefined\n },\n getReferencedExpressionByIdentifier(params: {\n node: TSESTree.Node\n context: Readonly<TSESLint.RuleContext<string, ReadonlyArray<unknown>>>\n }) {\n const { node, context } = params\n\n const resolvedNode = context\n .getScope()\n .references.find((ref) => ref.identifier === node)?.resolved?.defs[0]\n ?.node\n\n if (resolvedNode?.type !== AST_NODE_TYPES.VariableDeclarator) {\n return null\n }\n\n return resolvedNode.init\n },\n getClosestVariableDeclarator(node: TSESTree.Node) {\n let currentNode: TSESTree.Node | undefined = node\n\n while (currentNode.type !== AST_NODE_TYPES.Program) {\n if (currentNode.type === AST_NODE_TYPES.VariableDeclarator) {\n return currentNode\n }\n\n currentNode = currentNode.parent\n }\n\n return undefined\n },\n getNestedReturnStatements(\n node: TSESTree.Node,\n ): Array<TSESTree.ReturnStatement> {\n const returnStatements: Array<TSESTree.ReturnStatement> = []\n\n if (node.type === AST_NODE_TYPES.ReturnStatement) {\n returnStatements.push(node)\n }\n\n if ('body' in node && node.body !== undefined && node.body !== null) {\n Array.isArray(node.body)\n ? node.body.forEach((x) => {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(x))\n })\n : returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.body),\n )\n }\n\n if ('consequent' in node) {\n Array.isArray(node.consequent)\n ? node.consequent.forEach((x) => {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(x))\n })\n : returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.consequent),\n )\n }\n\n if ('alternate' in node && node.alternate !== null) {\n Array.isArray(node.alternate)\n ? node.alternate.forEach((x) => {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(x))\n })\n : returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.alternate),\n )\n }\n\n if ('cases' in node) {\n node.cases.forEach((x) => {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(x))\n })\n }\n\n if ('block' in node) {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(node.block))\n }\n\n if ('handler' in node && node.handler !== null) {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(node.handler))\n }\n\n if ('finalizer' in node && node.finalizer !== null) {\n returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.finalizer),\n )\n }\n\n if (\n 'expression' in node &&\n node.expression !== true &&\n node.expression !== false\n ) {\n returnStatements.push(\n ...ASTUtils.getNestedReturnStatements(node.expression),\n )\n }\n\n if ('test' in node && node.test !== null) {\n returnStatements.push(...ASTUtils.getNestedReturnStatements(node.test))\n }\n\n return returnStatements\n },\n}\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { TSESTree } from '@typescript-eslint/utils';
2
- import type { ESLintUtils } from '@typescript-eslint/utils';
1
+ import { TSESTree, ESLintUtils } from '@typescript-eslint/utils';
2
+
3
3
  type Create = Parameters<ReturnType<typeof ESLintUtils.RuleCreator>>[0]['create'];
4
4
  type Context = Parameters<Create>[0];
5
5
  type Options = Parameters<Create>[1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/eslint-plugin-query",
3
- "version": "5.32.1",
3
+ "version": "5.43.0",
4
4
  "description": "ESLint plugin for TanStack Query",
5
5
  "author": "Eliya Cohen",
6
6
  "license": "MIT",
@@ -36,23 +36,14 @@
36
36
  "src"
37
37
  ],
38
38
  "dependencies": {
39
- "@typescript-eslint/utils": "^6.20.0"
39
+ "@typescript-eslint/utils": "8.0.0-alpha.28"
40
40
  },
41
41
  "devDependencies": {
42
- "@typescript-eslint/rule-tester": "^6.20.0",
43
- "eslint": "^8.56.0"
42
+ "@typescript-eslint/rule-tester": "8.0.0-alpha.28",
43
+ "eslint": "^9.4.0"
44
44
  },
45
45
  "peerDependencies": {
46
- "eslint": "^8.0.0"
46
+ "eslint": "^8.0.0 | ^9.0.0"
47
47
  },
48
- "scripts": {
49
- "clean": "rimraf ./dist && rimraf ./coverage",
50
- "test:eslint": "eslint --ext .ts,.tsx ./src",
51
- "test:types": "tsc",
52
- "test:lib": "vitest",
53
- "test:lib:dev": "pnpm run test:lib --watch",
54
- "test:build": "publint --strict",
55
- "test:attw": "attw --pack",
56
- "build": "vite build"
57
- }
48
+ "scripts": {}
58
49
  }
@@ -387,6 +387,21 @@ ruleTester.run('exhaustive-deps', rule, {
387
387
  }
388
388
  `,
389
389
  },
390
+ {
391
+ name: 'instanceof value should not be in query key',
392
+ code: `
393
+ class SomeClass {}
394
+
395
+ function Component({ value }) {
396
+ useQuery({
397
+ queryKey: ['foo'],
398
+ queryFn: () => {
399
+ return value instanceof SomeClass;
400
+ }
401
+ });
402
+ }
403
+ `,
404
+ },
390
405
  ],
391
406
  invalid: [
392
407
  {
@@ -664,33 +679,6 @@ ruleTester.run('exhaustive-deps', rule, {
664
679
  },
665
680
  ],
666
681
  },
667
- {
668
- name: 'should fail when a queryKey is a reference of an array expression with a missing dep',
669
- code: normalizeIndent`
670
- const x = 5;
671
- const queryKey = ['foo']
672
- useQuery({ queryKey, queryFn: () => x })
673
- `,
674
- errors: [
675
- {
676
- messageId: 'missingDeps',
677
- data: { deps: 'x' },
678
- suggestions: [
679
- {
680
- messageId: 'fixTo',
681
- data: {
682
- result: "['foo', x]",
683
- },
684
- output: normalizeIndent`
685
- const x = 5;
686
- const queryKey = ['foo', x]
687
- useQuery({ queryKey, queryFn: () => x })
688
- `,
689
- },
690
- ],
691
- },
692
- ],
693
- },
694
682
  {
695
683
  name: 'should fail when queryKey is a queryKeyFactory while having missing dep',
696
684
  code: normalizeIndent`
package/src/configs.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { rules } from './rules'
2
2
  import type { ESLintUtils } from '@typescript-eslint/utils'
3
+ import type { ExtraRuleDocs } from './types'
3
4
 
4
5
  function generateRecommendedConfig(
5
6
  allRules: Record<
@@ -7,12 +8,12 @@ function generateRecommendedConfig(
7
8
  ESLintUtils.RuleModule<
8
9
  string,
9
10
  ReadonlyArray<unknown>,
11
+ ExtraRuleDocs,
10
12
  ESLintUtils.RuleListener
11
13
  >
12
14
  >,
13
15
  ) {
14
16
  return Object.entries(allRules).reduce(
15
- // @ts-expect-error
16
17
  (memo, [name, rule]) => {
17
18
  const { recommended } = rule.meta.docs || {}
18
19
 
@@ -21,7 +22,7 @@ function generateRecommendedConfig(
21
22
  ...(recommended ? { [`@tanstack/query/${name}`]: recommended } : {}),
22
23
  }
23
24
  },
24
- {} as Record<string, 'strict' | 'error' | 'warn'>,
25
+ {} as Record<string, ExtraRuleDocs['recommended']>,
25
26
  )
26
27
  }
27
28
 
@@ -5,13 +5,14 @@ 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
7
  import type { TSESLint } from '@typescript-eslint/utils'
8
+ import type { ExtraRuleDocs } from '../../types'
8
9
 
9
10
  const QUERY_KEY = 'queryKey'
10
11
  const QUERY_FN = 'queryFn'
11
12
 
12
13
  export const name = 'exhaustive-deps'
13
14
 
14
- const createRule = ESLintUtils.RuleCreator(getDocsUrl)
15
+ const createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)
15
16
 
16
17
  export const rule = createRule({
17
18
  name,
@@ -19,7 +20,7 @@ export const rule = createRule({
19
20
  type: 'problem',
20
21
  docs: {
21
22
  description: 'Exhaustive deps rule for useQuery',
22
- recommended: 'error' as any,
23
+ recommended: 'error',
23
24
  },
24
25
  messages: {
25
26
  missingDeps: `The following dependencies are missing in your queryKey: {{deps}}`,
@@ -41,7 +42,7 @@ export const rule = createRule({
41
42
  return
42
43
  }
43
44
 
44
- const scopeManager = context.getSourceCode().scopeManager
45
+ const scopeManager = context.sourceCode.scopeManager
45
46
  const queryKey = ASTUtils.findPropertyWithIdentifierKey(
46
47
  node.parent.properties,
47
48
  QUERY_KEY,
@@ -83,30 +84,31 @@ export const rule = createRule({
83
84
  }
84
85
  }
85
86
 
86
- const sourceCode = context.getSourceCode()
87
87
  const queryKeyValue = queryKeyNode
88
88
  const externalRefs = ASTUtils.getExternalRefs({
89
89
  scopeManager,
90
- sourceCode,
90
+ sourceCode: context.sourceCode,
91
91
  node: queryFn.value,
92
92
  })
93
93
 
94
94
  const relevantRefs = externalRefs.filter((reference) =>
95
95
  ExhaustiveDepsUtils.isRelevantReference({
96
- context,
96
+ sourceCode: context.sourceCode,
97
97
  reference,
98
98
  scopeManager,
99
+ node: queryFn.value,
99
100
  }),
100
101
  )
101
102
 
102
103
  const existingKeys = ASTUtils.getNestedIdentifiers(queryKeyValue).map(
103
- (identifier) => ASTUtils.mapKeyNodeToText(identifier, sourceCode),
104
+ (identifier) =>
105
+ ASTUtils.mapKeyNodeToText(identifier, context.sourceCode),
104
106
  )
105
107
 
106
108
  const missingRefs = relevantRefs
107
109
  .map((ref) => ({
108
110
  ref: ref,
109
- text: ASTUtils.mapKeyNodeToText(ref.identifier, sourceCode),
111
+ text: ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),
110
112
  }))
111
113
  .filter(({ ref, text }) => {
112
114
  return (
@@ -125,10 +127,12 @@ export const rule = createRule({
125
127
 
126
128
  if (uniqueMissingRefs.length > 0) {
127
129
  const missingAsText = uniqueMissingRefs
128
- .map((ref) => ASTUtils.mapKeyNodeToText(ref.identifier, sourceCode))
130
+ .map((ref) =>
131
+ ASTUtils.mapKeyNodeToText(ref.identifier, context.sourceCode),
132
+ )
129
133
  .join(', ')
130
134
 
131
- const existingWithMissing = sourceCode
135
+ const existingWithMissing = context.sourceCode
132
136
  .getText(queryKeyValue)
133
137
  .replace(/\]$/, `, ${missingAsText}]`)
134
138
 
@@ -1,15 +1,16 @@
1
1
  import { AST_NODE_TYPES } from '@typescript-eslint/utils'
2
2
  import { ASTUtils } from '../../utils/ast-utils'
3
- import type { TSESLint } from '@typescript-eslint/utils'
3
+ import type { TSESLint, TSESTree } from '@typescript-eslint/utils'
4
4
 
5
5
  export const ExhaustiveDepsUtils = {
6
6
  isRelevantReference(params: {
7
- context: Readonly<TSESLint.RuleContext<string, ReadonlyArray<unknown>>>
7
+ sourceCode: Readonly<TSESLint.SourceCode>
8
8
  reference: TSESLint.Scope.Reference
9
9
  scopeManager: TSESLint.Scope.ScopeManager
10
+ node: TSESTree.Node
10
11
  }) {
11
- const { reference, scopeManager, context } = params
12
- const component = ASTUtils.getFunctionAncestor(context)
12
+ const { sourceCode, reference, scopeManager, node } = params
13
+ const component = ASTUtils.getFunctionAncestor(sourceCode, node)
13
14
 
14
15
  if (
15
16
  component !== undefined &&
@@ -25,9 +26,16 @@ export const ExhaustiveDepsUtils = {
25
26
  return (
26
27
  reference.identifier.name !== 'undefined' &&
27
28
  reference.identifier.parent.type !== AST_NODE_TYPES.NewExpression &&
29
+ !ExhaustiveDepsUtils.isInstanceOfKind(reference.identifier.parent) &&
28
30
  !ExhaustiveDepsUtils.isQueryClientReference(reference)
29
31
  )
30
32
  },
33
+ isInstanceOfKind(node: TSESTree.Node) {
34
+ return (
35
+ node.type === AST_NODE_TYPES.BinaryExpression &&
36
+ node.operator === 'instanceof'
37
+ )
38
+ },
31
39
  isQueryClientReference(reference: TSESLint.Scope.Reference) {
32
40
  const declarator = reference.resolved?.defs[0]?.node
33
41
 
@@ -3,12 +3,13 @@ import { getDocsUrl } from '../../utils/get-docs-url'
3
3
  import { ASTUtils } from '../../utils/ast-utils'
4
4
  import { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'
5
5
  import { NoRestDestructuringUtils } from './no-rest-destructuring.utils'
6
+ import type { ExtraRuleDocs } from '../../types'
6
7
 
7
8
  export const name = 'no-rest-destructuring'
8
9
 
9
10
  const queryHooks = ['useQuery', 'useQueries', 'useInfiniteQuery']
10
11
 
11
- const createRule = ESLintUtils.RuleCreator(getDocsUrl)
12
+ const createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)
12
13
 
13
14
  export const rule = createRule({
14
15
  name,
@@ -16,7 +17,7 @@ export const rule = createRule({
16
17
  type: 'problem',
17
18
  docs: {
18
19
  description: 'Disallows rest destructuring in queries',
19
- recommended: 'warn' as any,
20
+ recommended: 'warn',
20
21
  },
21
22
  messages: {
22
23
  objectRestDestructure: `Object rest destructuring on a query will observe all changes to the query, leading to excessive re-renders.`,
@@ -3,10 +3,11 @@ import { ASTUtils } from '../../utils/ast-utils'
3
3
  import { getDocsUrl } from '../../utils/get-docs-url'
4
4
  import { detectTanstackQueryImports } from '../../utils/detect-react-query-imports'
5
5
  import type { TSESLint } from '@typescript-eslint/utils'
6
+ import type { ExtraRuleDocs } from '../../types'
6
7
 
7
8
  export const name = 'stable-query-client'
8
9
 
9
- const createRule = ESLintUtils.RuleCreator(getDocsUrl)
10
+ const createRule = ESLintUtils.RuleCreator<ExtraRuleDocs>(getDocsUrl)
10
11
 
11
12
  export const rule = createRule({
12
13
  name,
@@ -14,7 +15,7 @@ export const rule = createRule({
14
15
  type: 'problem',
15
16
  docs: {
16
17
  description: 'Makes sure that QueryClient is stable',
17
- recommended: 'error' as any,
18
+ recommended: 'error',
18
19
  },
19
20
  messages: {
20
21
  unstable: [
@@ -44,7 +45,10 @@ export const rule = createRule({
44
45
  return
45
46
  }
46
47
 
47
- const fnAncestor = ASTUtils.getFunctionAncestor(context)
48
+ const fnAncestor = ASTUtils.getFunctionAncestor(
49
+ context.sourceCode,
50
+ node,
51
+ )
48
52
  const isReactServerComponent = fnAncestor?.async === true
49
53
 
50
54
  if (
package/src/rules.ts CHANGED
@@ -2,12 +2,14 @@ import * as exhaustiveDeps from './rules/exhaustive-deps/exhaustive-deps.rule'
2
2
  import * as stableQueryClient from './rules/stable-query-client/stable-query-client.rule'
3
3
  import * as noRestDestructuring from './rules/no-rest-destructuring/no-rest-destructuring.rule'
4
4
  import type { ESLintUtils } from '@typescript-eslint/utils'
5
+ import type { ExtraRuleDocs } from './types'
5
6
 
6
7
  export const rules: Record<
7
8
  string,
8
9
  ESLintUtils.RuleModule<
9
10
  string,
10
11
  ReadonlyArray<unknown>,
12
+ ExtraRuleDocs,
11
13
  ESLintUtils.RuleListener
12
14
  >
13
15
  > = {
package/src/types.ts ADDED
@@ -0,0 +1,3 @@
1
+ export type ExtraRuleDocs = {
2
+ recommended: 'strict' | 'error' | 'warn'
3
+ }
@@ -219,9 +219,10 @@ export const ASTUtils = {
219
219
  )
220
220
  },
221
221
  getFunctionAncestor(
222
- context: Readonly<TSESLint.RuleContext<string, ReadonlyArray<unknown>>>,
222
+ sourceCode: Readonly<TSESLint.SourceCode>,
223
+ node: TSESTree.Node,
223
224
  ) {
224
- for (const ancestor of context.getAncestors()) {
225
+ for (const ancestor of sourceCode.getAncestors(node)) {
225
226
  if (ancestor.type === AST_NODE_TYPES.FunctionDeclaration) {
226
227
  return ancestor
227
228
  }