diff --git a/gitnexus/src/core/group/extractors/workspace-extractor.ts b/gitnexus/src/core/group/extractors/workspace-extractor.ts new file mode 100644 index 000000000..fcf1f3448 --- /dev/null +++ b/gitnexus/src/core/group/extractors/workspace-extractor.ts @@ -0,0 +1,68 @@ +import type { CypherExecutor } from '../contract-extractor.js'; +import type { GroupManifestLink } from '../types.js'; +import { extractRustWorkspaceLinks } from './rust-workspace-extractor.js'; +import { extractNodeWorkspaceLinks } from './node-workspace-extractor.js'; +import { extractPythonWorkspaceLinks } from './python-workspace-extractor.js'; +import { extractGoWorkspaceLinks } from './go-workspace-extractor.js'; + +export interface WorkspaceDiscoveryResult { + links: GroupManifestLink[]; + stats: WorkspaceExtractorStats[]; +} + +interface WorkspaceExtractorStats { + ecosystem: string; + linkCount: number; + projectCount: number; +} + +export async function discoverWorkspaceLinks( + repos: Record, + repoPaths: Map, + dbExecutors?: Map, +): Promise { + const links: GroupManifestLink[] = []; + const stats: WorkspaceExtractorStats[] = []; + + const rustResult = await extractRustWorkspaceLinks(repos, repoPaths, dbExecutors); + if (rustResult.links.length > 0) { + links.push(...rustResult.links); + stats.push({ + ecosystem: 'Rust', + linkCount: rustResult.links.length, + projectCount: rustResult.discoveredCrates.size, + }); + } + + const nodeResult = await extractNodeWorkspaceLinks(repos, repoPaths, dbExecutors); + if (nodeResult.links.length > 0) { + links.push(...nodeResult.links); + stats.push({ + ecosystem: 'Node', + linkCount: nodeResult.links.length, + projectCount: nodeResult.discoveredPackages.size, + }); + } + + const pyResult = await extractPythonWorkspaceLinks(repos, repoPaths, dbExecutors); + if (pyResult.links.length > 0) { + links.push(...pyResult.links); + stats.push({ + ecosystem: 'Python', + linkCount: pyResult.links.length, + projectCount: pyResult.discoveredPackages.size, + }); + } + + const goResult = await extractGoWorkspaceLinks(repos, repoPaths, dbExecutors); + if (goResult.links.length > 0) { + links.push(...goResult.links); + stats.push({ + ecosystem: 'Go', + linkCount: goResult.links.length, + projectCount: goResult.discoveredModules.size, + }); + } + + return { links, stats }; +} diff --git a/gitnexus/src/core/group/sync.ts b/gitnexus/src/core/group/sync.ts index c852065fa..09b289033 100644 --- a/gitnexus/src/core/group/sync.ts +++ b/gitnexus/src/core/group/sync.ts @@ -8,10 +8,7 @@ import { HttpRouteExtractor } from './extractors/http-route-extractor.js'; import { GrpcExtractor } from './extractors/grpc-extractor.js'; import { TopicExtractor } from './extractors/topic-extractor.js'; import { ManifestExtractor } from './extractors/manifest-extractor.js'; -import { extractRustWorkspaceLinks } from './extractors/rust-workspace-extractor.js'; -import { extractNodeWorkspaceLinks } from './extractors/node-workspace-extractor.js'; -import { extractPythonWorkspaceLinks } from './extractors/python-workspace-extractor.js'; -import { extractGoWorkspaceLinks } from './extractors/go-workspace-extractor.js'; +import { discoverWorkspaceLinks } from './extractors/workspace-extractor.js'; import { runExactMatch } from './matching.js'; import { detectServiceBoundaries, assignService } from './service-boundary-detector.js'; import type { CypherExecutor } from './contract-extractor.js'; @@ -196,43 +193,15 @@ export async function syncGroup(config: GroupConfig, opts?: SyncOptions): Promis if (e) repoPaths.set(groupPath, e.path); } - const rustResult = await extractRustWorkspaceLinks(config.repos, repoPaths, dbExecutors); - if (rustResult.links.length > 0) { - allLinks = [...allLinks, ...rustResult.links]; + const wsResult = await discoverWorkspaceLinks(config.repos, repoPaths, dbExecutors); + if (wsResult.links.length > 0) { + allLinks = [...allLinks, ...wsResult.links]; if (opts?.verbose) { - console.log( - ` workspace-deps: discovered ${rustResult.links.length} cross-crate links from ${rustResult.discoveredCrates.size} Rust crates`, - ); - } - } - - const nodeResult = await extractNodeWorkspaceLinks(config.repos, repoPaths, dbExecutors); - if (nodeResult.links.length > 0) { - allLinks = [...allLinks, ...nodeResult.links]; - if (opts?.verbose) { - console.log( - ` workspace-deps: discovered ${nodeResult.links.length} cross-package links from ${nodeResult.discoveredPackages.size} Node packages`, - ); - } - } - - const pyResult = await extractPythonWorkspaceLinks(config.repos, repoPaths, dbExecutors); - if (pyResult.links.length > 0) { - allLinks = [...allLinks, ...pyResult.links]; - if (opts?.verbose) { - console.log( - ` workspace-deps: discovered ${pyResult.links.length} cross-package links from ${pyResult.discoveredPackages.size} Python packages`, - ); - } - } - - const goResult = await extractGoWorkspaceLinks(config.repos, repoPaths, dbExecutors); - if (goResult.links.length > 0) { - allLinks = [...allLinks, ...goResult.links]; - if (opts?.verbose) { - console.log( - ` workspace-deps: discovered ${goResult.links.length} cross-module links from ${goResult.discoveredModules.size} Go modules`, - ); + for (const s of wsResult.stats) { + console.log( + ` workspace-deps: discovered ${s.linkCount} cross-${s.ecosystem.toLowerCase()} links from ${s.projectCount} ${s.ecosystem} projects`, + ); + } } } }