@crossplatformai/dependency-graph
v0.12.0
Published
Workspace dependency graph tooling for CrossPlatform.ai projects.
Readme
@crossplatformai/dependency-graph
Workspace dependency graph tooling for CrossPlatform.ai projects.
This package provides workspace discovery, graph building, traversal, and analysis utilities for developer tooling, CI, release workflows, and repository maintenance.
Package Role
@crossplatformai/dependency-graph belongs in the developer tooling layer.
It is intended for:
- workspace analysis
- affected package detection
- release and CI automation
- dependency health checks
- repository tooling
It is not an app runtime capability.
Installation
pnpm add @crossplatformai/dependency-graphUsage
Discovering Workspaces
import { discoverWorkspaces } from '@crossplatformai/dependency-graph';
import { readFile } from 'node:fs/promises';
import { glob } from 'glob';
import { parse } from 'yaml';
const packages = await discoverWorkspaces(process.cwd(), {
fs: {
readFile: async (path, encoding) => readFile(path, encoding),
exists: async (path) => {
try {
await readFile(path);
return true;
} catch {
return false;
}
},
},
glob: {
glob: async (pattern, options) => glob(pattern, options),
},
yaml: {
parse: (content) => parse(content),
},
});Building Dependency Graph
import { buildDependencyGraph } from '@crossplatformai/dependency-graph';
const graph = buildDependencyGraph(packages);Finding Affected Packages
import { findAffectedPackages } from '@crossplatformai/dependency-graph';
const affected = findAffectedPackages(graph, 'my-package', {
includeSelf: true,
});Detecting Cycles
import { detectCycles } from '@crossplatformai/dependency-graph';
const cycles = detectCycles(graph);
if (cycles.length > 0) {
console.error('Circular dependencies detected:', cycles);
}Mapping Files to Packages
import { mapFilesToPackages } from '@crossplatformai/dependency-graph';
const changedFiles = ['apps/web/src/index.ts', 'packages/ui/src/button.tsx'];
const fileMap = mapFilesToPackages(changedFiles, packages);
console.log(fileMap);
// Map { 'web' => ['apps/web/src/index.ts'], '@repo/shared' => ['packages/ui/src/button.tsx'] }API
Types
WorkspacePackage- Package metadata from package.jsonDependencyGraph- Graph representation of package dependenciesDependencyNode- Node in the dependency graphGraphStats- Statistics about the dependency graph
Client Interfaces (Dependency Injection)
FileSystemClient- File system operations interfaceGlobClient- Glob pattern matching interfaceYamlClient- YAML parsing interface
Functions
Workspace Discovery
discoverWorkspaces(rootDir, config)- Discover all workspace packages
Graph Building
buildDependencyGraph(packages)- Build dependency graph from packages
Graph Traversal
findAffectedPackages(graph, packageName, options)- Find all packages affected by changesfindDependencyPath(graph, from, to)- Find shortest path between packagesfindAllPaths(graph, from, to)- Find all paths between packages
Graph Analysis
analyzeGraph(graph)- Get comprehensive graph statisticsdetectCycles(graph)- Detect circular dependenciesgetTransitiveDependencies(graph, packageName)- Get all transitive dependenciesgetTransitiveDependents(graph, packageName)- Get all transitive dependents
File Mapping
findPackageForFile(filePath, packages)- Find which package owns a filemapFilesToPackages(files, packages)- Map array of files to their packages
Dependency Injection Pattern
This package accepts clients supplied by the calling tool or script:
import type { WorkspaceDiscoveryConfig } from '@crossplatformai/dependency-graph';
import { readFile } from 'node:fs/promises';
import { glob } from 'glob';
import { parse as parseYaml } from 'yaml';
// Create config with real implementations
const config: WorkspaceDiscoveryConfig = {
fs: {
readFile: (path, encoding) => readFile(path, encoding),
exists: async (path) => {
try {
await readFile(path);
return true;
} catch {
return false;
}
},
},
glob: {
glob: (pattern, options) => glob(pattern, options),
},
yaml: {
parse: (content) => parseYaml(content),
},
};
// Use with dependency injection
const packages = await discoverWorkspaces(process.cwd(), config);Testing
For testing, provide mock implementations:
import { describe, it, expect, vi } from 'vitest';
const mockConfig = {
fs: {
readFile: vi.fn(),
exists: vi.fn(),
},
glob: {
glob: vi.fn(),
},
yaml: {
parse: vi.fn(),
},
};
// Mock implementations
mockConfig.fs.readFile.mockResolvedValue('{}');
mockConfig.glob.glob.mockResolvedValue(['apps/web', 'packages/ui']);
mockConfig.yaml.parse.mockReturnValue({ packages: ['apps/*', 'packages/*'] });
const packages = await discoverWorkspaces('/fake/root', mockConfig);Design Approach
This package prefers host-provided implementations for filesystem, globbing, and YAML parsing when flexibility matters.
That keeps the graph logic:
- testable
- environment-agnostic
- reusable across scripts and CI contexts
- decoupled from any one file access strategy
License
Apache-2.0
