@@ -9,9 +9,13 @@ export type { RouterNode }
99
1010/**
1111 * Finds the main FastAPI app or APIRouter in the list of routers.
12+ * We want to prioritize FastAPI apps over APIRouters.
1213 */
1314function findAppRouter ( routers : RouterInfo [ ] ) : RouterInfo | undefined {
14- return routers . find ( ( r ) => r . type === "FastAPI" || r . type === "APIRouter" )
15+ return (
16+ routers . find ( ( r ) => r . type === "FastAPI" ) ??
17+ routers . find ( ( r ) => r . type === "APIRouter" )
18+ )
1519}
1620
1721/**
@@ -36,13 +40,14 @@ function buildRouterGraphInternal(
3640) : RouterNode | null {
3741 // Resolve the full path of the entry file if necessary
3842 let resolvedEntryFile = entryFile
43+
3944 if ( ! existsSync ( resolvedEntryFile ) && ! isAbsolute ( entryFile ) ) {
4045 resolvedEntryFile = join ( projectRoot , entryFile )
4146 }
47+
4248 if ( ! existsSync ( resolvedEntryFile ) ) {
4349 return null
4450 }
45-
4651 // Prevent infinite recursion on circular imports
4752 if ( visited . has ( resolvedEntryFile ) ) {
4853 return null
@@ -85,6 +90,10 @@ function buildRouterGraphInternal(
8590 }
8691
8792 // Find all routers included in the app
93+ // Only include routes that belong directly to the app (not to local APIRouters)
94+ const appRoutes = analysis . routes . filter (
95+ ( r ) => r . object === appRouter . variableName ,
96+ )
8897 const rootRouter : RouterNode = {
8998 filePath : resolvedEntryFile ,
9099 variableName : appRouter . variableName ,
@@ -93,7 +102,7 @@ function buildRouterGraphInternal(
93102 tags : appRouter . tags ,
94103 line : appRouter . line ,
95104 column : appRouter . column ,
96- routes : analysis . routes . map ( ( r ) => ( {
105+ routes : appRoutes . map ( ( r ) => ( {
97106 method : r . method ,
98107 path : r . path ,
99108 function : r . function ,
@@ -113,18 +122,17 @@ function buildRouterGraphInternal(
113122 parser ,
114123 visited ,
115124 )
116- if ( ! childRouter ) {
117- continue
118- }
119- // Merge tags from include_router call with the router's own tags
120- if ( include . tags . length > 0 ) {
121- childRouter . tags = [ ...new Set ( [ ...childRouter . tags , ...include . tags ] ) ]
125+ if ( childRouter ) {
126+ // Merge tags from include_router call with the router's own tags
127+ if ( include . tags . length > 0 ) {
128+ childRouter . tags = [ ...new Set ( [ ...childRouter . tags , ...include . tags ] ) ]
129+ }
130+ rootRouter . children . push ( {
131+ router : childRouter ,
132+ prefix : include . prefix ,
133+ tags : include . tags ,
134+ } )
122135 }
123- rootRouter . children . push ( {
124- router : childRouter ,
125- prefix : include . prefix ,
126- tags : include . tags ,
127- } )
128136 }
129137
130138 // Process mount() calls for subapps
@@ -137,21 +145,21 @@ function buildRouterGraphInternal(
137145 parser ,
138146 visited ,
139147 )
140- if ( ! childRouter ) {
141- continue
148+ if ( childRouter ) {
149+ rootRouter . children . push ( {
150+ router : childRouter ,
151+ prefix : mount . path ,
152+ tags : [ ] ,
153+ } )
142154 }
143- rootRouter . children . push ( {
144- router : childRouter ,
145- prefix : mount . path ,
146- tags : [ ] ,
147- } )
148155 }
149156
150157 return rootRouter
151158}
152159
153160/**
154161 * Resolves a router/app reference to its RouterNode.
162+ * Used for include_router and mount calls.
155163 */
156164function resolveRouterReference (
157165 reference : string ,
@@ -164,9 +172,37 @@ function resolveRouterReference(
164172 const parts = reference . split ( "." )
165173 const moduleName = parts [ 0 ]
166174
175+ // First, check if this is a local router defined in the same file
176+ const localRouter = analysis . routers . find (
177+ ( r ) => r . variableName === moduleName && r . type === "APIRouter" ,
178+ )
179+ if ( localRouter ) {
180+ // Filter routes that belong to this router (decorated with @router.method)
181+ const routerRoutes = analysis . routes . filter ( ( r ) => r . object === moduleName )
182+ return {
183+ filePath : currentFile ,
184+ variableName : localRouter . variableName ,
185+ type : localRouter . type ,
186+ prefix : localRouter . prefix ,
187+ tags : localRouter . tags ,
188+ line : localRouter . line ,
189+ column : localRouter . column ,
190+ routes : routerRoutes . map ( ( r ) => ( {
191+ method : r . method ,
192+ path : r . path ,
193+ function : r . function ,
194+ line : r . line ,
195+ column : r . column ,
196+ } ) ) ,
197+ children : [ ] ,
198+ }
199+ }
200+
201+ // Otherwise, look for an imported router
167202 const matchingImport = analysis . imports . find ( ( imp ) =>
168203 imp . names . includes ( moduleName ) ,
169204 )
205+
170206 if ( ! matchingImport ) {
171207 return null
172208 }
@@ -182,6 +218,7 @@ function resolveRouterReference(
182218 projectRoot ,
183219 parser ,
184220 )
221+
185222 if ( ! importedFilePath ) {
186223 return null
187224 }
0 commit comments