domino
A high-performance Rust implementation of True Affected — semantic change detection for monorepos using the Oxc parser.
Overview
domino is a drop-in replacement for the TypeScript version of traf, providing the same semantic analysis capabilities with significantly better performance thanks to Rust and the Oxc parser.
Features
- Semantic Change Detection: Analyzes actual code changes at the AST level, not just file changes
- Cross-File Reference Tracking: Follows symbol references across your entire workspace
- Lockfile Change Detection: Detects dependency version changes in npm, yarn, pnpm, and bun lockfiles and traces affected projects
- Fast Oxc Parser: 3-5x faster than TypeScript's compiler API
- Workspace Support: Works with Nx, Turborepo, and generic npm/yarn/pnpm/bun workspaces
- Global Invalidation: Honors Nx
namedInputsworkspace-root patterns (e.g.sharedGlobals) and TurborepoglobalDependenciesfromturbo.json/turbo.jsonc, so a change to a shared root file marks every project affected - Root tsconfig changes: A change to the workspace-root
tsconfig.base.jsonis handled in every workspace type by--root-tsconfig-strategy(Node:rootTsconfigStrategy). The default,aliases, narrows a change tocompilerOptions.pathsalone: files using an added, removed or re-targeted alias are affected, whether they import it,requireit,import()it, side-effect-import it or re-export it (config and other non-source files in a project root count too;.git,node_modulesand files whose first 8 KiB are not UTF-8 are skipped, and a project rooted at the workspace root is checked through itssourceRootonly), and what uses them is traced. An import that binds a name is traced like a changed symbol, through the code using it; a named re-export by its name; anexport *by every name importers take from the barrel, except names the barrel exports itself; and a file that reaches the alias without a binding has every export traced (as does one that only mentions it in a comment,jest.mock()ordeclare module, an accepted over-selection). Any other change (another compiler option,baseUrl,extends, a childpathsappearing or vanishing underextends, an unparseable file, or an alias key with a*other than a trailing/*) affects every project.allalways affects every project;nonetreats the file like any other root file. AnamedInputsentry that lists the file still invalidates globally whatever the strategy - Module Resolution: Uses oxc_resolver (same as Rolldown and Nova) for accurate module resolution
Quick Start
# Run directly with npx (no installation required)
npx @front-ops/domino@latest affected
# Show all projects in the workspace
npx @front-ops/domino@latest affected --all
Installation
Using npx (Recommended)
No installation required! Just run:
npx @front-ops/domino@latest affected
Binary Installation
If you prefer to use the standalone binary:
# Clone and build from source
git clone [email protected]:frontops-dev/domino.git
cd domino
cargo build --release
# The binary will be available at ./target/release/domino
# You can then run it from anywhere:
/path/to/domino/target/release/domino affected
Usage
Using npx
# Show all projects in the workspace
npx @front-ops/domino affected --all
# Find affected projects (compared to origin/main)
npx @front-ops/domino affected
# Use a different base branch
npx @front-ops/domino affected --base origin/develop
# Compare specific commits (useful in CI patch pipelines)
npx @front-ops/domino affected --base abc123 --head def456
# Output as JSON
npx @front-ops/domino affected --json
# Enable debug logging
npx @front-ops/domino affected --debug
# Generate a detailed report
npx @front-ops/domino affected --report report.html
Using the Binary
If you've built the binary from source:
# Show all projects in the workspace
domino affected --all
# Find affected projects (compared to origin/main)
domino affected
# Use a different base branch
domino affected --base origin/develop
# Compare specific commits (useful in CI patch pipelines)
domino affected --base abc123 --head def456
# Output as JSON
domino affected --json
# Enable debug logging
domino affected --debug
# Generate a detailed report
domino affected --report report.html
Options
--base <BRANCH>: Base branch to compare against (default:origin/main)--head <COMMIT>: Head commit to compare (defaults to working tree)--all: Show all projects regardless of changes--json: Output results as JSON--report <PATH>: Generate a detailed analysis report--debug: Enable debug logging--cwd <PATH>: Set the current working directory--lockfile-strategy <STRATEGY>: Lockfile change detection strategy (default:direct)--root-tsconfig-strategy <STRATEGY>: How atsconfig.base.jsonchange affects projects:aliases,allornone(default:aliases)
Lockfile Change Detection
domino automatically detects when your lockfile changes and identifies which projects are affected by dependency version updates. This works with all major package managers:
| Package Manager | Lockfile |
|---|---|
| npm | package-lock.json |
| yarn | yarn.lock |
| pnpm | pnpm-lock.yaml |
| bun | bun.lock |
Three strategies are available via --lockfile-strategy:
none— Ignore lockfile changes entirelydirect(default) — Mark projects that directly import an affected dependencyfull— Likedirect, but also traces the full reference chain (e.g. iflib-achanged andProjectAimports it,fullfollows all re-exports oflib-asymbols to find additional affected projects)
The detection is transitive: if a deeply nested dependency changes, domino walks the reverse dependency graph to find which direct dependency was affected, then finds all projects importing that dependency.
# Default: detect lockfile changes with "direct" strategy
domino affected
# Disable lockfile detection
domino affected --lockfile-strategy none
# Full reference chain tracing
domino affected --lockfile-strategy full
How It Works
- Git Diff Analysis: Detects which files and specific lines have changed
- Semantic Parsing: Parses all TypeScript/JavaScript files using Oxc
- Symbol Resolution: Identifies which symbols (functions, classes, constants) were modified
- Reference Finding: Recursively finds all cross-file references to those symbols
- Lockfile Analysis: Detects dependency version changes and traces affected imports
- Project Mapping: Maps affected files to their owning projects
- Implicit Dependencies: Expands Nx
implicitDependenciesagainst known project names, including glob patterns (app-*,integration-*-module) and!exclusions — same idea as Nx/minimatch, not path globs - Target Config Links: Follows projects named in a
project.jsontarget'sinputs({ "input": "production", "projects": ["shared-config"] }) anddependsOn({ "projects": ["api"], "target": "build" }, or the"api:build"string form), which create no project-graph edge of their own.nx.jsontargetDefaultssupply those fields for targets a project declares without setting them, with Nx's key precedence (executor, then target name, then target-name glob, matched minimatch-style); a project's own field wins. Theselfanddependencieskeywords add no link, tag selectors (tag:x) and extglob keys (@(a|b)) are not supported, andfilters on array-formtargetDefaultsentries are not evaluated (their links always apply, and a fully filtered key does not hide the next one). Target config declared inpackage.json#nx, and targets inferred by Nx plugins, are not read.
Performance
Thanks to Rust and Oxc, domino is significantly faster than the TypeScript version:
- Parsing: 3-5x faster using Oxc
- Memory: Lower memory footprint
- Startup: Near-instant startup time
Comparison with TypeScript Version
| Feature | TypeScript | Rust |
|---|---|---|
| Parser | ts-morph (TypeScript compiler) | Oxc parser |
| Speed | Baseline | 3-5x faster |
| Memory | Baseline | ~50% less |
| Binary Size | Requires Node.js + deps | Single standalone binary |
| Startup Time | ~1-2s | <100ms |
Architecture
Core Components
- Git Integration (
src/git.rs): Parses git diffs to identify changed files and lines - Workspace Discovery (
src/workspace/): Discovers projects in Nx, Turbo, and generic npm/yarn/pnpm/bun workspaces - Semantic Analyzer (
src/semantic/analyzer.rs): Uses Oxc to parse and analyze TypeScript/JavaScript - Reference Finder (
src/semantic/reference_finder.rs): Tracks cross-file symbol references - Lockfile Analyzer (
src/lockfile.rs): Parses lockfiles, builds reverse dependency graphs, and detects affected packages - Core Algorithm (
src/core.rs): Orchestrates the affected detection logic
Key Technologies
- Oxc: High-performance JavaScript/TypeScript parser and toolchain
- oxc_resolver: Module resolution (used by Rolldown, Nova, knip)
- clap: CLI argument parsing
- git2: Git integration
- serde: JSON/YAML parsing
Development
Quick Reference
# Build (debug)
cargo build
# Build (release)
cargo build --release
# Run tests
cargo test
# Run integration tests (must be serial)
cargo test --test integration_test -- --test-threads=1
# Run from source
cargo run -- affected --all
# Format code
cargo fmt
# Lint code
cargo clippy
# Enable debug logging
RUST_LOG=domino=debug cargo run -- affected
License
MIT - see LICENSE.
Credits
This is a Rust port of the original traf TypeScript implementation.
Built with:
- Oxc - The JavaScript Oxidation Compiler
- oxc_resolver - Fast module resolution