✨ feat(vscode): FastAPI 接口卡片的调用树带上前端调用它的地方

This commit is contained in:
cruldra committed 2026-10-12 12:22:51 +11:00
1 parent d4459e93b3
commit 29cedcb751
30 files changed
+1316 -130

No files matched your search

+62 -1
View File
@@ -1,6 +1,6 @@
import type { CallEdge, CallRange, CallSymbol, CallTreeNode } from './model'
import { describe, expect, it } from 'vitest'
import { buildCallTrees } from './model'
import { buildCallTrees, nextPageUrl } from './model'
function symbol(name: string, line = 0, extra: Partial<CallSymbol> = {}): CallSymbol {
return {
@@ -159,15 +159,76 @@ describe('调用边收成调用树', () => {
expect(made.name).toBe('新建 OrderService')
expect(made.detail).toBe('进入这个类')
expect(made.where).toBe('billing/order.py:40')
expect(made.jump).toEqual({ uri: 'file:///create_order', line: 39, character: 0 })
expect(made.current).toBe(false)
expect(made.kind).toBe('class')
expect(made.children).toEqual([{
name: 'OrderService',
kind: 'class',
where: 'billing/order.py:14',
jump: { uri: 'file:///billing/order.py', line: 13, character: 0 },
current: true,
children: [],
}])
expect(JSON.stringify(trees)).not.toContain('方法')
})
})
describe('接口的调用树', () => {
const route = symbol('get_default_agent', 78, { uri: 'file:///api/agent/routes.py', path: 'api/agent/routes.py' })
it('前端发请求的那一行单独成「请求」节点,点了跳到那一行;Next.js 页面入口第二行写页面网址', () => {
const fetchDefault = symbol('fetchDefaultAgent', 9, { uri: 'file:///web/src/lib/agents.ts', path: 'web/src/lib/agents.ts' })
const page = symbol('MyAgentsPage', 4, { uri: 'file:///web/src/app/(main)/my-agents/page.tsx', path: 'web/src/app/(main)/my-agents/page.tsx' })
const sidebar = symbol('useSidebar', 3, { uri: 'file:///web/src/components/sidebar/useSidebar.ts', path: 'web/src/components/sidebar/useSidebar.ts' })
const trees = buildCallTrees([
{ ...edge(fetchDefault, route, 10, 20), request: 'GET /api/v1/agents/default' },
edge(page, fetchDefault, 6),
edge(sidebar, fetchDefault, 8),
], route)
expect(trees.map(tree => [tree.name, tree.root.detail])).toEqual([['MyAgentsPage', '页面 /my-agents'], ['useSidebar', '入口']])
const request = trees[0].root.children[0].children[0]
expect(request).toEqual({
name: '请求 GET /api/v1/agents/default',
kind: 'request',
where: 'web/src/lib/agents.ts:11',
jump: { uri: 'file:///web/src/lib/agents.ts', line: 10, character: 20 },
current: false,
children: [{
name: 'get_default_agent',
kind: 'function',
where: 'api/agent/routes.py:79',
jump: { uri: 'file:///api/agent/routes.py', line: 78, character: 0 },
current: true,
children: [],
}],
})
expect(trees[0].hits).toBe(1)
expect(trees[0].root.jump).toEqual({ uri: 'file:///web/src/app/(main)/my-agents/page.tsx', line: 4, character: 0 })
})
it('在 Python 里直接调用路由函数的不加「请求」节点', () => {
const job = symbol('nightly_job', 2, { path: 'jobs/nightly.py' })
const trees = buildCallTrees([edge(job, route, 5)], route)
expect(trees[0].root.children.map(node => node.name)).toEqual(['get_default_agent'])
})
})
describe('按 Next.js 的约定算页面网址', () => {
it('按 app 目录下的目录算:去掉路由分组和并行路由,动态段原样留', () => {
expect(nextPageUrl('web/src/app/(main)/my-agents/page.tsx')).toBe('/my-agents')
expect(nextPageUrl('app/page.tsx')).toBe('/')
expect(nextPageUrl('client/src/app/(main)/billing/orders/[orderId]/page.tsx')).toBe('/billing/orders/[orderId]')
expect(nextPageUrl('app/docs/[[...section]]/page.js')).toBe('/docs/[[...section]]')
expect(nextPageUrl('app/dashboard/@team/settings/page.jsx')).toBe('/dashboard/settings')
expect(nextPageUrl('app/(marketing)/page.ts')).toBe('/')
})
it('不是 app 目录下的 page 文件就不是页面', () => {
expect(nextPageUrl('app/layout.tsx')).toBeUndefined()
expect(nextPageUrl('pages/index.tsx')).toBeUndefined()
expect(nextPageUrl('src/apps/page.tsx')).toBeUndefined()
expect(nextPageUrl('app/page.mdx')).toBeUndefined()
expect(nextPageUrl('page.tsx')).toBeUndefined()
})
})