@@ -12,6 +12,13 @@ import type { Parser } from "./parser"
1212
1313export type { RouterNode }
1414
15+ interface ResolutionContext {
16+ projectRootUri : string
17+ parser : Parser
18+ fs : FileSystem
19+ visited : Set < string >
20+ }
21+
1522/**
1623 * Finds the main FastAPI app or APIRouter in the list of routers.
1724 * If targetVariable is specified, only returns the router with that variable name.
@@ -55,6 +62,39 @@ function createRouterNode(
5562 }
5663}
5764
65+ async function processIncludeRouters (
66+ analysis : FileAnalysis ,
67+ ownerRouter : RouterNode ,
68+ currentFileUri : string ,
69+ ctx : ResolutionContext ,
70+ ) : Promise < void > {
71+ const includes = analysis . includeRouters . filter (
72+ ( inc ) => inc . owner === ownerRouter . variableName ,
73+ )
74+ for ( const include of includes ) {
75+ log (
76+ `Resolving include_router: ${ include . router } (prefix: ${ include . prefix || "none" } )` ,
77+ )
78+ const childRouter = await resolveRouterReference (
79+ include . router ,
80+ analysis ,
81+ currentFileUri ,
82+ ctx ,
83+ )
84+ if ( childRouter ) {
85+ // Merge tags from include_router call with the router's own tags
86+ if ( include . tags . length > 0 ) {
87+ childRouter . tags = [ ...new Set ( [ ...childRouter . tags , ...include . tags ] ) ]
88+ }
89+ ownerRouter . children . push ( {
90+ router : childRouter ,
91+ prefix : include . prefix ,
92+ tags : include . tags ,
93+ } )
94+ }
95+ }
96+ }
97+
5898/**
5999 * Builds a router graph starting from the given entry file.
60100 * If targetVariable is specified, only that specific app/router will be used.
@@ -68,10 +108,7 @@ export async function buildRouterGraph(
68108) : Promise < RouterNode | null > {
69109 return buildRouterGraphInternal (
70110 entryFileUri ,
71- parser ,
72- projectRootUri ,
73- fs ,
74- new Set ( ) ,
111+ { projectRootUri, parser, fs, visited : new Set ( ) } ,
75112 targetVariable ,
76113 )
77114}
@@ -81,12 +118,10 @@ export async function buildRouterGraph(
81118 */
82119async function buildRouterGraphInternal (
83120 entryFileUri : string ,
84- parser : Parser ,
85- projectRootUri : string ,
86- fs : FileSystem ,
87- visited : Set < string > ,
121+ ctx : ResolutionContext ,
88122 targetVariable ?: string ,
89123) : Promise < RouterNode | null > {
124+ const { projectRootUri, parser, fs, visited } = ctx
90125 // Check if file exists
91126 if ( ! ( await fs . exists ( entryFileUri ) ) ) {
92127 log ( `File not found: "${ entryFileUri } "` )
@@ -174,42 +209,15 @@ async function buildRouterGraphInternal(
174209 const rootRouter = createRouterNode ( appRouter , appRoutes , resolvedEntryUri )
175210
176211 // Process include_router calls to find child routers
177- for ( const include of analysis . includeRouters ) {
178- log (
179- `Resolving include_router: ${ include . router } (prefix: ${ include . prefix || "none" } )` ,
180- )
181- const childRouter = await resolveRouterReference (
182- include . router ,
183- analysis ,
184- resolvedEntryUri ,
185- projectRootUri ,
186- parser ,
187- fs ,
188- visited ,
189- )
190- if ( childRouter ) {
191- // Merge tags from include_router call with the router's own tags
192- if ( include . tags . length > 0 ) {
193- childRouter . tags = [ ...new Set ( [ ...childRouter . tags , ...include . tags ] ) ]
194- }
195- rootRouter . children . push ( {
196- router : childRouter ,
197- prefix : include . prefix ,
198- tags : include . tags ,
199- } )
200- }
201- }
212+ await processIncludeRouters ( analysis , rootRouter , resolvedEntryUri , ctx )
202213
203214 // Process mount() calls for subapps
204215 for ( const mount of analysis . mounts ) {
205216 const childRouter = await resolveRouterReference (
206217 mount . app ,
207218 analysis ,
208219 resolvedEntryUri ,
209- projectRootUri ,
210- parser ,
211- fs ,
212- visited ,
220+ ctx ,
213221 )
214222 if ( childRouter ) {
215223 rootRouter . children . push ( {
@@ -234,11 +242,9 @@ async function resolveRouterReference(
234242 reference : string ,
235243 analysis : FileAnalysis ,
236244 currentFileUri : string ,
237- projectRootUri : string ,
238- parser : Parser ,
239- fs : FileSystem ,
240- visited : Set < string > ,
245+ ctx : ResolutionContext ,
241246) : Promise < RouterNode | null > {
247+ const { projectRootUri, parser, fs, visited } = ctx
242248 const parts = reference . split ( "." )
243249 const moduleName = parts [ 0 ]
244250 // For dotted references like "api_routes.router", extract the attribute name
@@ -258,35 +264,7 @@ async function resolveRouterReference(
258264 )
259265
260266 // Process include_router calls owned by this router (nested routers)
261- const routerIncludes = analysis . includeRouters . filter (
262- ( inc ) => inc . owner === moduleName ,
263- )
264- for ( const include of routerIncludes ) {
265- log (
266- `Resolving nested include_router: ${ include . router } (owner: ${ moduleName } , prefix: ${ include . prefix || "none" } )` ,
267- )
268- const childRouter = await resolveRouterReference (
269- include . router ,
270- analysis ,
271- currentFileUri ,
272- projectRootUri ,
273- parser ,
274- fs ,
275- visited ,
276- )
277- if ( childRouter ) {
278- if ( include . tags . length > 0 ) {
279- childRouter . tags = [
280- ...new Set ( [ ...childRouter . tags , ...include . tags ] ) ,
281- ]
282- }
283- routerNode . children . push ( {
284- router : childRouter ,
285- prefix : include . prefix ,
286- tags : include . tags ,
287- } )
288- }
289- }
267+ await processIncludeRouters ( analysis , routerNode , currentFileUri , ctx )
290268
291269 return routerNode
292270 }
@@ -362,46 +340,17 @@ async function resolveRouterReference(
362340 )
363341
364342 // Process include_router calls owned by this router (nested routers)
365- const routerIncludes = importedAnalysis . includeRouters . filter (
366- ( inc ) => inc . owner === attributeName ,
343+ await processIncludeRouters (
344+ importedAnalysis ,
345+ routerNode ,
346+ importedFileUri ,
347+ ctx ,
367348 )
368- for ( const include of routerIncludes ) {
369- log (
370- `Resolving nested include_router: ${ include . router } (owner: ${ attributeName } , prefix: ${ include . prefix || "none" } )` ,
371- )
372- const childRouter = await resolveRouterReference (
373- include . router ,
374- importedAnalysis ,
375- importedFileUri ,
376- projectRootUri ,
377- parser ,
378- fs ,
379- visited ,
380- )
381- if ( childRouter ) {
382- if ( include . tags . length > 0 ) {
383- childRouter . tags = [
384- ...new Set ( [ ...childRouter . tags , ...include . tags ] ) ,
385- ]
386- }
387- routerNode . children . push ( {
388- router : childRouter ,
389- prefix : include . prefix ,
390- tags : include . tags ,
391- } )
392- }
393- }
394349
395350 return routerNode
396351 }
397352 // If not found as a router, fall through to try building from file
398353 }
399354
400- return buildRouterGraphInternal (
401- importedFileUri ,
402- parser ,
403- projectRootUri ,
404- fs ,
405- visited ,
406- )
355+ return buildRouterGraphInternal ( importedFileUri , ctx )
407356}
0 commit comments