Skip to content

Commit 079d3c2

Browse files
♻️ Refactor tree utils and EndpointTreeProvider (#29)
1 parent ead498e commit 079d3c2

11 files changed

Lines changed: 505 additions & 337 deletions

File tree

src/appDiscovery.ts

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,11 @@ import type { Parser } from "./core/parser"
1010
import { findProjectRoot, uriPath } from "./core/pathUtils"
1111
import { buildRouterGraph } from "./core/routerResolver"
1212
import { routerNodeToAppDefinition } from "./core/transformer"
13+
import { collectRoutes, countRouters } from "./core/treeUtils"
1314
import type { AppDefinition } from "./core/types"
1415
import { vscodeFileSystem } from "./providers/vscodeFileSystem"
1516
import { log } from "./utils/logger"
16-
import {
17-
countRouters,
18-
countRoutes,
19-
createTimer,
20-
trackEntrypointDetected,
21-
} from "./utils/telemetry"
17+
import { createTimer, trackEntrypointDetected } from "./utils/telemetry"
2218

2319
export type { EntryPoint }
2420

@@ -176,28 +172,27 @@ export async function discoverFastAPIApps(
176172

177173
if (routerNode) {
178174
const app = routerNodeToAppDefinition(routerNode, folder.uri.fsPath)
179-
// Count all routes: direct routes + routes in all routers (recursively)
180-
const countRoutes = (routers: typeof app.routers): number =>
181-
routers.reduce(
182-
(sum, r) => sum + r.routes.length + countRoutes(r.children),
183-
0,
184-
)
185-
const totalRoutes = app.routes.length + countRoutes(app.routers)
186-
log(
187-
`Found FastAPI app "${app.name}" with ${totalRoutes} route(s) in ${app.routers.length} router(s)`,
188-
)
189175
folderApps.push(app)
190176
apps.push(app)
191177
break // TODO: Only use first successful app per workspace folder, for now
192178
}
193179
}
194180

181+
const folderRoutes = collectRoutes(folderApps)
182+
183+
if (folderApps.length > 0) {
184+
const app = folderApps[0]
185+
log(
186+
`Found FastAPI app "${app.name}" with ${folderRoutes.length} route(s) in ${app.routers.length} router(s)`,
187+
)
188+
}
189+
195190
// Track entrypoint detection per workspace folder
196191
trackEntrypointDetected({
197192
duration_ms: folderTimer(),
198193
method: detectionMethod,
199194
success: folderApps.length > 0,
200-
routes_count: countRoutes(folderApps),
195+
routes_count: folderRoutes.length,
201196
routers_count: countRouters(folderApps),
202197
})
203198
}

src/core/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ export { Parser } from "./parser"
1111
export { findProjectRoot } from "./pathUtils"
1212
export { buildRouterGraph, type RouterNode } from "./routerResolver"
1313
export { routerNodeToAppDefinition } from "./transformer"
14+
export {
15+
collectRoutes,
16+
countRouters,
17+
countRoutesInRouter,
18+
findRouter,
19+
} from "./treeUtils"
1420
export type {
1521
AppDefinition,
1622
HTTPMethod,

src/core/treeUtils.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import type { AppDefinition, RouteDefinition, RouterDefinition } from "./types"
2+
3+
/** Finds the first router matching a condition across all apps. */
4+
export function findRouter(
5+
apps: AppDefinition[],
6+
predicate: (router: RouterDefinition) => boolean,
7+
): RouterDefinition | undefined {
8+
function findIn(routers: RouterDefinition[]): RouterDefinition | undefined {
9+
for (const r of routers) {
10+
if (predicate(r)) return r
11+
const found = findIn(r.children)
12+
if (found) return found
13+
}
14+
return undefined
15+
}
16+
for (const app of apps) {
17+
const found = findIn(app.routers)
18+
if (found) return found
19+
}
20+
return undefined
21+
}
22+
23+
/** Collects all routes from all apps, including nested router routes. */
24+
export function collectRoutes(apps: AppDefinition[]): RouteDefinition[] {
25+
function fromRouters(routers: RouterDefinition[]): RouteDefinition[] {
26+
return routers.flatMap((r) => [...r.routes, ...fromRouters(r.children)])
27+
}
28+
return apps.flatMap((app) => [...app.routes, ...fromRouters(app.routers)])
29+
}
30+
31+
/** Counts all routes in a router, including nested routers. */
32+
export function countRoutesInRouter(router: RouterDefinition): number {
33+
return (
34+
router.routes.length +
35+
router.children.reduce((sum, child) => sum + countRoutesInRouter(child), 0)
36+
)
37+
}
38+
39+
/** Counts total routers across all apps. */
40+
export function countRouters(apps: AppDefinition[]): number {
41+
function count(routers: RouterDefinition[]): number {
42+
return routers.reduce((sum, r) => sum + 1 + count(r.children), 0)
43+
}
44+
return apps.reduce((sum, app) => sum + count(app.routers), 0)
45+
}

src/extension.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { discoverFastAPIApps } from "./appDiscovery"
77
import { clearImportCache } from "./core/importResolver"
88
import { Parser } from "./core/parser"
99
import { stripLeadingDynamicSegments } from "./core/pathUtils"
10+
import { collectRoutes, countRouters } from "./core/treeUtils"
1011
import type { AppDefinition, SourceLocation } from "./core/types"
1112
import {
1213
type EndpointTreeItem,
@@ -16,8 +17,6 @@ import {
1617
import { TestCodeLensProvider } from "./providers/testCodeLensProvider"
1718
import { disposeLogger, log } from "./utils/logger"
1819
import {
19-
countRouters,
20-
countRoutes,
2120
createTimer,
2221
flushSessionSummary,
2322
getInstalledVersions,
@@ -110,7 +109,7 @@ export async function activate(context: vscode.ExtensionContext) {
110109
trackActivation({
111110
duration_ms: elapsed(),
112111
success,
113-
routes_count: countRoutes(apps),
112+
routes_count: collectRoutes(apps).length,
114113
routers_count: countRouters(apps),
115114
apps_count: apps.length,
116115
workspace_folder_count: vscode.workspace.workspaceFolders?.length ?? 0,
@@ -139,7 +138,7 @@ export async function activate(context: vscode.ExtensionContext) {
139138
})
140139
}
141140

142-
const endpointProvider = new EndpointTreeProvider(apps, groupApps)
141+
const endpointProvider = new EndpointTreeProvider(apps, groupApps(apps))
143142
const codeLensProvider = new TestCodeLensProvider(parserService, apps)
144143

145144
// File watcher for auto-refresh
@@ -149,7 +148,7 @@ export async function activate(context: vscode.ExtensionContext) {
149148
refreshTimeout = setTimeout(async () => {
150149
if (!parserService) return
151150
const newApps = await discoverFastAPIApps(parserService)
152-
endpointProvider.setApps(newApps, groupApps)
151+
endpointProvider.setApps(newApps, groupApps(newApps))
153152
codeLensProvider.setApps(newApps)
154153
}, 300)
155154
}
@@ -215,7 +214,7 @@ function registerCommands(
215214
if (!parserService) return
216215
clearImportCache()
217216
const newApps = await discoverFastAPIApps(parserService)
218-
endpointProvider.setApps(newApps, groupApps)
217+
endpointProvider.setApps(newApps, groupApps(newApps))
219218
codeLensProvider.setApps(newApps)
220219
},
221220
),
@@ -235,8 +234,7 @@ function registerCommands(
235234
async () => {
236235
const workspacePrefix =
237236
vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? ""
238-
const items = endpointProvider
239-
.getAllRoutes()
237+
const items = collectRoutes(endpointProvider.getApps())
240238
.map((route) => {
241239
const path = stripLeadingDynamicSegments(route.path)
242240
return {

0 commit comments

Comments
 (0)