Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/core/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import { readFileSync } from "node:fs"
import type { Tree } from "web-tree-sitter"
import { logError } from "../utils/logger"
import {
decoratorExtractor,
findNodesByType,
Expand Down Expand Up @@ -55,10 +56,12 @@ export function analyzeFile(
const code = readFileSync(filePath, "utf-8")
const tree = parser.parse(code)
if (!tree) {
logError(`Failed to parse file: "${filePath}"`)
return null
}
return analyzeTree(tree, filePath)
} catch {
} catch (error) {
logError(`Error reading file: "${filePath}"`, error)
return null
}
}
13 changes: 13 additions & 0 deletions src/core/routerResolver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { existsSync } from "node:fs"
import { isAbsolute, join } from "node:path"
import { log } from "../utils/logger"
import { analyzeFile } from "./analyzer"
import { resolveNamedImport, resolveRouterFromInit } from "./importResolver"
import type { FileAnalysis, RouterInfo, RouterNode } from "./internal"
Expand Down Expand Up @@ -62,10 +63,12 @@ function buildRouterGraphInternal(
}

if (!existsSync(resolvedEntryFile)) {
log(`File not found: "${entryFile}"`)
return null
}
// Prevent infinite recursion on circular imports
if (visited.has(resolvedEntryFile)) {
log(`Skipping already visited file: "${resolvedEntryFile}"`)
return null
}

Expand All @@ -74,9 +77,14 @@ function buildRouterGraphInternal(
// Analyze the entry file
let analysis = analyzeFile(resolvedEntryFile, parser)
if (!analysis) {
log(`Failed to analyze file: "${resolvedEntryFile}"`)
return null
}

log(
`Analyzed "${resolvedEntryFile}": ${analysis.routes.length} routes, ${analysis.routers.length} routers, ${analysis.includeRouters.length} include_router calls`,
)

// Find FastAPI instantiation (filter by targetVariable if specified)
let appRouter = findAppRouter(analysis.routers, targetVariable)

Expand Down Expand Up @@ -130,6 +138,9 @@ function buildRouterGraphInternal(

// Process include_router calls to find child routers
for (const include of analysis.includeRouters) {
log(
`Resolving include_router: ${include.router} (prefix: ${include.prefix || "none"})`,
)
const childRouter = resolveRouterReference(
include.router,
analysis,
Expand Down Expand Up @@ -220,6 +231,7 @@ function resolveRouterReference(
)

if (!matchingImport) {
log(`No import found for router reference: ${reference}`)
return null
}

Expand All @@ -236,6 +248,7 @@ function resolveRouterReference(
)

if (!importedFilePath) {
log(`Could not resolve import: ${matchingImport.modulePath}`)
return null
}

Expand Down
11 changes: 10 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
EndpointTreeProvider,
} from "./providers/EndpointTreeProvider"
import { TestCodeLensProvider } from "./providers/TestCodeLensProvider"
import { disposeLogger, log } from "./utils/logger"

let parserService: Parser | null = null

Expand All @@ -25,7 +26,13 @@ function navigateToLocation(location: SourceLocation): void {
}

export async function activate(context: vscode.ExtensionContext) {
// Initialize parser
const extensionVersion =
vscode.extensions.getExtension("FastAPILabs.fastapi-vscode")?.packageJSON
?.version ?? "unknown"
log(
`FastAPI extension ${extensionVersion} activated (VS Code ${vscode.version})`,
)

parserService = new Parser()
await parserService.init({
core: vscode.Uri.joinPath(
Expand Down Expand Up @@ -166,7 +173,9 @@ function registerCommands(
}

export function deactivate() {
log("Extension deactivated")
parserService?.dispose()
parserService = null
clearImportCache()
disposeLogger()
}
36 changes: 36 additions & 0 deletions src/utils/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Output channel logger for the FastAPI extension.
* Provides visibility into extension activity for troubleshooting.
*
* Uses LogOutputChannel for colored log levels and automatic timestamps.
*/

import * as vscode from "vscode"

let outputChannel: vscode.LogOutputChannel | null = null

function getOutputChannel(): vscode.LogOutputChannel {
if (!outputChannel) {
outputChannel = vscode.window.createOutputChannel("FastAPI", { log: true })
}
return outputChannel
}

export function log(message: string): void {
getOutputChannel().info(message)
}

export function logError(message: string, error?: unknown): void {
if (error instanceof Error) {
getOutputChannel().error(`${message}: ${error.message}`)
} else if (error !== undefined) {
getOutputChannel().error(`${message}: ${String(error)}`)
} else {
getOutputChannel().error(message)
}
}

export function disposeLogger(): void {
outputChannel?.dispose()
outputChannel = null
}