Skip to content

Commit 133f0f3

Browse files
🐛 Find all FastAPI apps in a workspace folder, rather than just the shallowest (#69)
1 parent e3f7aa7 commit 133f0f3

1 file changed

Lines changed: 30 additions & 17 deletions

File tree

src/appDiscovery.ts

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,39 @@ export function parseEntrypointString(value: string): {
3838
}
3939

4040
/**
41-
* Scans for common FastAPI entry point files (main.py, __init__.py).
41+
* Finds all Python files containing a FastAPI() instantiation.
42+
* Uses a cheap text pre-filter to avoid tree-sitter parsing non-app files.
4243
* Returns URI strings sorted by depth (shallower first).
4344
*/
44-
async function automaticDetectEntryPoints(
45+
async function findAllFastAPIFiles(
4546
folder: vscode.WorkspaceFolder,
4647
): Promise<string[]> {
47-
const [mainFiles, initFiles] = await Promise.all([
48-
vscode.workspace.findFiles(
49-
new vscode.RelativePattern(folder, "**/main.py"),
48+
const pyFiles = await vscode.workspace.findFiles(
49+
new vscode.RelativePattern(folder, "**/*.py"),
50+
new vscode.RelativePattern(
51+
folder,
52+
"**/{.venv,venv,__pycache__,node_modules,.git,tests,test}/**",
5053
),
51-
vscode.workspace.findFiles(
52-
new vscode.RelativePattern(folder, "**/__init__.py"),
53-
),
54-
])
54+
)
55+
56+
const results: string[] = []
57+
for (const uri of pyFiles) {
58+
const fileName = uri.path.split("/").pop() ?? ""
59+
if (
60+
fileName.startsWith("test_") ||
61+
fileName.endsWith("_test.py") ||
62+
fileName === "conftest.py"
63+
)
64+
continue
65+
const content = await vscode.workspace.fs.readFile(uri)
66+
if (new TextDecoder().decode(content).includes("FastAPI(")) {
67+
results.push(uri.toString())
68+
}
69+
}
5570

56-
return [...mainFiles, ...initFiles]
57-
.map((uri) => uri.toString())
58-
.sort((a, b) => uriPath(a).split("/").length - uriPath(b).split("/").length)
71+
return results.sort(
72+
(a, b) => uriPath(a).split("/").length - uriPath(b).split("/").length,
73+
)
5974
}
6075

6176
/**
@@ -148,11 +163,11 @@ export async function discoverFastAPIApps(
148163
candidates = [pyprojectEntry]
149164
detectionMethod = "pyproject"
150165
} else {
151-
const detected = await automaticDetectEntryPoints(folder)
166+
const detected = await findAllFastAPIFiles(folder)
152167
candidates = detected.map((filePath) => ({ filePath }))
153168
detectionMethod = "heuristic"
154169
log(
155-
`Found ${candidates.length} candidate entry file(s) in ${folder.name}`,
170+
`Found ${candidates.length} candidate FastAPI file(s) in ${folder.name}`,
156171
)
157172
}
158173

@@ -183,16 +198,14 @@ export async function discoverFastAPIApps(
183198
const app = routerNodeToAppDefinition(routerNode, folder.uri.fsPath)
184199
folderApps.push(app)
185200
apps.push(app)
186-
break // TODO: Only use first successful app per workspace folder, for now
187201
}
188202
}
189203

190204
const folderRoutes = collectRoutes(folderApps)
191205

192206
if (folderApps.length > 0) {
193-
const app = folderApps[0]
194207
log(
195-
`Found FastAPI app "${app.name}" with ${folderRoutes.length} route(s) in ${app.routers.length} router(s)`,
208+
`Found ${folderApps.length} FastAPI app(s) with ${folderRoutes.length} route(s) in ${folder.name}`,
196209
)
197210
}
198211

0 commit comments

Comments
 (0)