@@ -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