@tanstack/eslint-plugin-query 5.100.13 → 5.101.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.
@@ -916,12 +916,42 @@ var import_utils7 = require("@typescript-eslint/utils");
916
916
 
917
917
  // src/rules/no-rest-destructuring/no-rest-destructuring.utils.ts
918
918
  var import_utils6 = require("@typescript-eslint/utils");
919
+ var QUERY_RESULT_TYPE_NAMES = /* @__PURE__ */ new Set([
920
+ "UseBaseQueryResult",
921
+ "UseQueryResult",
922
+ "UseSuspenseQueryResult",
923
+ "DefinedUseQueryResult",
924
+ "UseInfiniteQueryResult",
925
+ "UseSuspenseInfiniteQueryResult",
926
+ "DefinedUseInfiniteQueryResult",
927
+ "QueryObserverResult",
928
+ "InfiniteQueryObserverResult"
929
+ ]);
930
+ function isQueryResultType(type) {
931
+ if (type.aliasSymbol && QUERY_RESULT_TYPE_NAMES.has(type.aliasSymbol.name)) {
932
+ return true;
933
+ }
934
+ const symbol = type.getSymbol();
935
+ if (symbol && QUERY_RESULT_TYPE_NAMES.has(symbol.name)) {
936
+ return true;
937
+ }
938
+ return type.isUnion() && type.types.some(isQueryResultType);
939
+ }
919
940
  var NoRestDestructuringUtils = {
920
941
  isObjectRestDestructuring(node) {
921
942
  if (node.type !== import_utils6.AST_NODE_TYPES.ObjectPattern) {
922
943
  return false;
923
944
  }
924
945
  return node.properties.some((p) => p.type === import_utils6.AST_NODE_TYPES.RestElement);
946
+ },
947
+ isQueryResultCall(node, parserServices) {
948
+ if (!parserServices?.program || !parserServices.esTreeNodeToTSNodeMap) {
949
+ return false;
950
+ }
951
+ const checker = parserServices.program.getTypeChecker();
952
+ const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node.callee);
953
+ const signatures = checker.getTypeAtLocation(tsNode).getCallSignatures();
954
+ return signatures.some((sig) => isQueryResultType(sig.getReturnType()));
925
955
  }
926
956
  };
927
957
 
@@ -954,11 +984,22 @@ var rule3 = createRule3({
954
984
  const queryResultVariables = /* @__PURE__ */ new Set();
955
985
  return {
956
986
  CallExpression: (node) => {
957
- if (!ASTUtils.isIdentifierWithOneOfNames(node.callee, queryHooks) || node.parent.type !== import_utils7.AST_NODE_TYPES.VariableDeclarator || !helpers.isTanstackQueryImport(node.callee)) {
987
+ if (node.parent.type !== import_utils7.AST_NODE_TYPES.VariableDeclarator) {
958
988
  return;
959
989
  }
960
990
  const returnValue = node.parent.id;
961
- if (node.callee.name !== "useQueries" && node.callee.name !== "useSuspenseQueries") {
991
+ const isDirectHook = ASTUtils.isIdentifierWithOneOfNames(node.callee, queryHooks) && helpers.isTanstackQueryImport(node.callee);
992
+ if (!isDirectHook) {
993
+ const canReportQueryResult = returnValue.type === import_utils7.AST_NODE_TYPES.Identifier || NoRestDestructuringUtils.isObjectRestDestructuring(returnValue);
994
+ if (!canReportQueryResult || !NoRestDestructuringUtils.isQueryResultCall(
995
+ node,
996
+ context.sourceCode.parserServices
997
+ )) {
998
+ return;
999
+ }
1000
+ }
1001
+ const calleeName = ASTUtils.isIdentifier(node.callee) ? node.callee.name : null;
1002
+ if (calleeName !== "useQueries" && calleeName !== "useSuspenseQueries") {
962
1003
  if (NoRestDestructuringUtils.isObjectRestDestructuring(returnValue)) {
963
1004
  return context.report({
964
1005
  node: node.parent,
@@ -1038,7 +1079,10 @@ var rule4 = createRule4({
1038
1079
  defaultOptions: [],
1039
1080
  create: detectTanstackQueryImports((context, _options, helpers) => {
1040
1081
  const trackedVariables = {};
1082
+ const trackedCustomHooks = {};
1041
1083
  const hookAliasMap = {};
1084
+ const pendingVariableDeclarators = [];
1085
+ const pendingDependencyChecks = [];
1042
1086
  function getReactHook(node) {
1043
1087
  if (node.callee.type === "Identifier") {
1044
1088
  const calleeName = node.callee.name;
@@ -1066,6 +1110,9 @@ var rule4 = createRule4({
1066
1110
  }
1067
1111
  }
1068
1112
  }
1113
+ function isCustomHookName(hookName) {
1114
+ return /^use[A-Z0-9]/.test(hookName);
1115
+ }
1069
1116
  function hasCombineProperty(callExpression) {
1070
1117
  if (callExpression.arguments.length === 0) return false;
1071
1118
  const firstArg = callExpression.arguments[0];
@@ -1075,6 +1122,59 @@ var rule4 = createRule4({
1075
1122
  (prop) => prop.type === import_utils8.AST_NODE_TYPES.Property && prop.key.type === import_utils8.AST_NODE_TYPES.Identifier && prop.key.name === "combine"
1076
1123
  );
1077
1124
  }
1125
+ function getDirectQueryHook(callExpression) {
1126
+ if (callExpression.callee.type !== import_utils8.AST_NODE_TYPES.Identifier || !allHookNames.includes(callExpression.callee.name) || !helpers.isTanstackQueryImport(callExpression.callee)) {
1127
+ return void 0;
1128
+ }
1129
+ if ((callExpression.callee.name === "useQueries" || callExpression.callee.name === "useSuspenseQueries") && hasCombineProperty(callExpression)) {
1130
+ return void 0;
1131
+ }
1132
+ return callExpression.callee.name;
1133
+ }
1134
+ function getTrackedQueryHook(callExpression) {
1135
+ const directQueryHook = getDirectQueryHook(callExpression);
1136
+ if (directQueryHook !== void 0) {
1137
+ return directQueryHook;
1138
+ }
1139
+ if (callExpression.callee.type === import_utils8.AST_NODE_TYPES.Identifier) {
1140
+ return trackedCustomHooks[callExpression.callee.name];
1141
+ }
1142
+ return void 0;
1143
+ }
1144
+ function getReturnedQueryHook(body) {
1145
+ if (body.type === import_utils8.AST_NODE_TYPES.CallExpression) {
1146
+ return getDirectQueryHook(body);
1147
+ }
1148
+ if (body.type !== import_utils8.AST_NODE_TYPES.BlockStatement) {
1149
+ return void 0;
1150
+ }
1151
+ const returnStatements = body.body.filter(
1152
+ (statement) => statement.type === import_utils8.AST_NODE_TYPES.ReturnStatement
1153
+ );
1154
+ if (returnStatements.length !== 1) {
1155
+ return void 0;
1156
+ }
1157
+ const returnArgument = returnStatements[0]?.argument;
1158
+ if (returnArgument?.type === import_utils8.AST_NODE_TYPES.CallExpression) {
1159
+ return getDirectQueryHook(returnArgument);
1160
+ }
1161
+ return void 0;
1162
+ }
1163
+ function checkDependencyArray(reactHook, depsArray) {
1164
+ depsArray.elements.forEach((dep) => {
1165
+ if (dep !== null && dep.type === import_utils8.AST_NODE_TYPES.Identifier && trackedVariables[dep.name] !== void 0) {
1166
+ const queryHook = trackedVariables[dep.name];
1167
+ context.report({
1168
+ node: dep,
1169
+ messageId: "noUnstableDeps",
1170
+ data: {
1171
+ queryHook,
1172
+ reactHook
1173
+ }
1174
+ });
1175
+ }
1176
+ });
1177
+ }
1078
1178
  return {
1079
1179
  ImportDeclaration(node) {
1080
1180
  if (node.specifiers.length > 0 && node.importKind === "value" && node.source.value === "React") {
@@ -1085,32 +1185,48 @@ var rule4 = createRule4({
1085
1185
  });
1086
1186
  }
1087
1187
  },
1188
+ FunctionDeclaration(node) {
1189
+ if (node.id === null || !isCustomHookName(node.id.name)) {
1190
+ return;
1191
+ }
1192
+ const queryHook = getReturnedQueryHook(node.body);
1193
+ if (queryHook !== void 0) {
1194
+ trackedCustomHooks[node.id.name] = queryHook;
1195
+ }
1196
+ },
1088
1197
  VariableDeclarator(node) {
1089
- if (node.init !== null && node.init.type === import_utils8.AST_NODE_TYPES.CallExpression && node.init.callee.type === import_utils8.AST_NODE_TYPES.Identifier && allHookNames.includes(node.init.callee.name) && helpers.isTanstackQueryImport(node.init.callee)) {
1090
- if ((node.init.callee.name === "useQueries" || node.init.callee.name === "useSuspenseQueries") && hasCombineProperty(node.init)) {
1091
- return;
1198
+ if (node.id.type === import_utils8.AST_NODE_TYPES.Identifier && isCustomHookName(node.id.name) && node.init !== null && (node.init.type === import_utils8.AST_NODE_TYPES.ArrowFunctionExpression || node.init.type === import_utils8.AST_NODE_TYPES.FunctionExpression)) {
1199
+ const queryHook = getReturnedQueryHook(node.init.body);
1200
+ if (queryHook !== void 0) {
1201
+ trackedCustomHooks[node.id.name] = queryHook;
1092
1202
  }
1093
- collectVariableNames(node.id, node.init.callee.name);
1203
+ }
1204
+ if (node.init !== null && node.init.type === import_utils8.AST_NODE_TYPES.CallExpression) {
1205
+ pendingVariableDeclarators.push(node);
1094
1206
  }
1095
1207
  },
1096
1208
  CallExpression: (node) => {
1097
1209
  const reactHook = getReactHook(node);
1098
1210
  if (reactHook !== void 0 && node.arguments.length > 1 && node.arguments[1]?.type === import_utils8.AST_NODE_TYPES.ArrayExpression) {
1099
- const depsArray = node.arguments[1].elements;
1100
- depsArray.forEach((dep) => {
1101
- if (dep !== null && dep.type === import_utils8.AST_NODE_TYPES.Identifier && trackedVariables[dep.name] !== void 0) {
1102
- const queryHook = trackedVariables[dep.name];
1103
- context.report({
1104
- node: dep,
1105
- messageId: "noUnstableDeps",
1106
- data: {
1107
- queryHook,
1108
- reactHook
1109
- }
1110
- });
1111
- }
1211
+ pendingDependencyChecks.push({
1212
+ reactHook,
1213
+ depsArray: node.arguments[1]
1112
1214
  });
1113
1215
  }
1216
+ },
1217
+ "Program:exit"() {
1218
+ pendingVariableDeclarators.forEach((node) => {
1219
+ if (node.init?.type !== import_utils8.AST_NODE_TYPES.CallExpression) {
1220
+ return;
1221
+ }
1222
+ const queryHook = getTrackedQueryHook(node.init);
1223
+ if (queryHook !== void 0) {
1224
+ collectVariableNames(node.id, queryHook);
1225
+ }
1226
+ });
1227
+ pendingDependencyChecks.forEach(({ reactHook, depsArray }) => {
1228
+ checkDependencyArray(reactHook, depsArray);
1229
+ });
1114
1230
  }
1115
1231
  };
1116
1232
  })