MCPcopy Create free account
hub / github.com/Houseofmvps/codesight / parsePythonMountsToGraph

Function parsePythonMountsToGraph

src/detectors/routes.ts:1914–1969  ·  view source on GitHub ↗

Python/FastAPI: scan for APIRouter(prefix=...) + include_router() chains

(
  content: string,
  _entryDir: string,
  files: string[],
  project: ProjectInfo,
  mountEdges: Map<string, { prefix: string; mountedBy: string }>,
  sourceFile: string
)

Source from the content-addressed store, hash-verified

1912 mountEdges: Map<string, { prefix: string; mountedBy: string }>,
1913 sourceFile: string
1914): void {
1915 // Step 1: Build alias map: "auth_router" → "backend/routes/auth.py"
1916 // from routes.auth import router as auth_router
1917 const aliasRe = /from\s+([\w.]+)\s+import\s+router\s+as\s+(\w+)/g;
1918 const aliasMap = new Map<string, string>(); // alias → source file
1919 let m;
1920 while ((m = aliasRe.exec(content)) !== null) {
1921 const moduleDots = m[1];
1922 const alias = m[2];
1923 const modPath = moduleDots.replace(/\./g, "/");
1924 const hit = files.find(f => {
1925 const rel = f.replace(/\\/g, "/");
1926 return rel.endsWith(`/${modPath}.py`) || rel.endsWith(`${modPath}.py`);
1927 });
1928 if (hit) aliasMap.set(alias, relative(project.root, hit).replace(/\\/g, "/"));
1929 }
1930
1931 // Also handle: from routes.auth import router (no alias)
1932 const noAliasRe = /from\s+([\w.]+)\s+import\s+router(?!\s+as)\b/g;
1933 while ((m = noAliasRe.exec(content)) !== null) {
1934 const modPath = m[1].replace(/\./g, "/");
1935 const hit = files.find(f => f.replace(/\\/g, "/").endsWith(`${modPath}.py`));
1936 if (hit) aliasMap.set("router", relative(project.root, hit).replace(/\\/g, "/"));
1937 }
1938
1939 // Step 2: Find APIRouter with prefix: api_router = APIRouter(prefix="/api")
1940 const prefixRouterRe = /(\w+)\s*=\s*APIRouter\s*\([^)]*prefix\s*=\s*['"]([^'"]+)['"]/g;
1941 const routerPrefixes = new Map<string, string>(); // varName → prefix
1942 while ((m = prefixRouterRe.exec(content)) !== null) {
1943 routerPrefixes.set(m[1], m[2]);
1944 }
1945
1946 // Step 3: Chain include_router calls:
1947 // api_router.include_router(auth_router)
1948 // api_router.include_router(cv_router, prefix="/cv")
1949 const includeRe = /(\w+)\s*\.\s*include_router\s*\(\s*(\w+)(?:[^)]*prefix\s*=\s*['"]([^'"]+)['"])?\s*\)/g;
1950 while ((m = includeRe.exec(content)) !== null) {
1951 const parentVar = m[1];
1952 const childVar = m[2];
1953 const extraPrefix = m[3] || "";
1954 const parentPrefix = routerPrefixes.get(parentVar) || "";
1955 const fullPrefix = parentPrefix + extraPrefix;
1956
1957 const targetFile = aliasMap.get(childVar);
1958 if (targetFile && fullPrefix && !mountEdges.has(targetFile)) {
1959 mountEdges.set(targetFile, { prefix: fullPrefix, mountedBy: sourceFile });
1960 }
1961 }
1962}

Callers 1

resolveRoutePrefixesFunction · 0.85

Calls

no outgoing calls

Tested by

no test coverage detected