@@ -36,6 +36,7 @@ export const rule = createRule({
3636
3737 create : detectTanstackQueryImports ( ( context , _options , helpers ) => {
3838 const trackedVariables : Record < string , string > = { }
39+ const trackedCustomHooks : Record < string , string > = { }
3940 const hookAliasMap : Record < string , string > = { }
4041
4142 function getReactHook ( node : TSESTree . CallExpression ) : string | undefined {
@@ -67,6 +68,10 @@ export const rule = createRule({
6768 }
6869 }
6970
71+ function isCustomHookName ( hookName : string ) : boolean {
72+ return / ^ u s e [ A - Z 0 - 9 ] / . test ( hookName )
73+ }
74+
7075 function hasCombineProperty (
7176 callExpression : TSESTree . CallExpression ,
7277 ) : boolean {
@@ -84,6 +89,71 @@ export const rule = createRule({
8489 )
8590 }
8691
92+ function getDirectQueryHook (
93+ callExpression : TSESTree . CallExpression ,
94+ ) : string | undefined {
95+ if (
96+ callExpression . callee . type !== AST_NODE_TYPES . Identifier ||
97+ ! allHookNames . includes ( callExpression . callee . name ) ||
98+ ! helpers . isTanstackQueryImport ( callExpression . callee )
99+ ) {
100+ return undefined
101+ }
102+
103+ if (
104+ callExpression . callee . name === 'useQueries' &&
105+ hasCombineProperty ( callExpression )
106+ ) {
107+ return undefined
108+ }
109+
110+ return callExpression . callee . name
111+ }
112+
113+ function getTrackedQueryHook (
114+ callExpression : TSESTree . CallExpression ,
115+ ) : string | undefined {
116+ const directQueryHook = getDirectQueryHook ( callExpression )
117+ if ( directQueryHook !== undefined ) {
118+ return directQueryHook
119+ }
120+
121+ if ( callExpression . callee . type === AST_NODE_TYPES . Identifier ) {
122+ return trackedCustomHooks [ callExpression . callee . name ]
123+ }
124+
125+ return undefined
126+ }
127+
128+ function getReturnedQueryHook (
129+ body :
130+ | TSESTree . FunctionExpression [ 'body' ]
131+ | TSESTree . ArrowFunctionExpression [ 'body' ] ,
132+ ) : string | undefined {
133+ if ( body . type === AST_NODE_TYPES . CallExpression ) {
134+ return getDirectQueryHook ( body )
135+ }
136+
137+ if ( body . type !== AST_NODE_TYPES . BlockStatement ) {
138+ return undefined
139+ }
140+
141+ const returnStatements = body . body . filter (
142+ ( statement ) : statement is TSESTree . ReturnStatement =>
143+ statement . type === AST_NODE_TYPES . ReturnStatement ,
144+ )
145+ if ( returnStatements . length !== 1 ) {
146+ return undefined
147+ }
148+
149+ const returnArgument = returnStatements [ 0 ] ?. argument
150+ if ( returnArgument ?. type === AST_NODE_TYPES . CallExpression ) {
151+ return getDirectQueryHook ( returnArgument )
152+ }
153+
154+ return undefined
155+ }
156+
87157 return {
88158 ImportDeclaration ( node : TSESTree . ImportDeclaration ) {
89159 if (
@@ -104,23 +174,39 @@ export const rule = createRule({
104174 }
105175 } ,
106176
177+ FunctionDeclaration ( node ) {
178+ if ( node . id === null || ! isCustomHookName ( node . id . name ) ) {
179+ return
180+ }
181+
182+ const queryHook = getReturnedQueryHook ( node . body )
183+ if ( queryHook !== undefined ) {
184+ trackedCustomHooks [ node . id . name ] = queryHook
185+ }
186+ } ,
187+
107188 VariableDeclarator ( node ) {
189+ if (
190+ node . id . type === AST_NODE_TYPES . Identifier &&
191+ isCustomHookName ( node . id . name ) &&
192+ node . init !== null &&
193+ ( node . init . type === AST_NODE_TYPES . ArrowFunctionExpression ||
194+ node . init . type === AST_NODE_TYPES . FunctionExpression )
195+ ) {
196+ const queryHook = getReturnedQueryHook ( node . init . body )
197+ if ( queryHook !== undefined ) {
198+ trackedCustomHooks [ node . id . name ] = queryHook
199+ }
200+ }
201+
108202 if (
109203 node . init !== null &&
110- node . init . type === AST_NODE_TYPES . CallExpression &&
111- node . init . callee . type === AST_NODE_TYPES . Identifier &&
112- allHookNames . includes ( node . init . callee . name ) &&
113- helpers . isTanstackQueryImport ( node . init . callee )
204+ node . init . type === AST_NODE_TYPES . CallExpression
114205 ) {
115- // Special case for useQueries with combine property - it's stable
116- if (
117- node . init . callee . name === 'useQueries' &&
118- hasCombineProperty ( node . init )
119- ) {
120- // Don't track useQueries with combine as unstable
121- return
206+ const queryHook = getTrackedQueryHook ( node . init )
207+ if ( queryHook !== undefined ) {
208+ collectVariableNames ( node . id , queryHook )
122209 }
123- collectVariableNames ( node . id , node . init . callee . name )
124210 }
125211 } ,
126212 CallExpression : ( node ) => {
0 commit comments