tracebound
v3.0.2
Published
Multi-lingual dependency structure analyzer - enforces architectural layer boundaries across JS, TS, Python, Java, and PHP with usage-level detection (deptrac-compatible)
Downloads
33
Maintainers
Readme
🏗️ tracebound
A multi-lingual dependency structure analyzer CLI tool that enforces architectural layer boundaries across codebases written in different programming languages.
Inspired by deptrac (PHP), this tool brings the same architectural enforcement to JavaScript, TypeScript, Python, Java, and PHP — with a plugin system that makes adding more languages straightforward.
✨ Features
- Multi-language support — JS, JSX, TS, TSX, Python, Java, PHP (and easily extensible)
- Tree-sitter powered — fast, accurate AST parsing via WebAssembly grammars
- Usage-level detection — reports every line where an imported symbol is referenced, not just import statements (deptrac-compatible)
- YAML configuration — define layers, collectors, and dependency rules
- Two config formats — classic deptrac-compatible or simplified
match+rules - Violation detection — finds imports that cross forbidden layer boundaries
- Mermaid graph output — visual dependency graph with violations highlighted in red
- JSON reports — machine-readable output for CI integration
- Plugin architecture — add new languages by implementing a single adapter interface
.gitignoreaware — automatically skips ignored files and directories
📋 Supported Languages
| Language | Extensions | What's Detected |
| ---------- | ------------------ | ---------------------------------------------------------------------- |
| JavaScript | .js, .jsx | Every usage of imported names: calls, new, member access, JSX, etc. |
| TypeScript | .ts, .tsx | Same as JS + type annotations, implements, generics, as assertions |
| Python | .py | Every usage of imported names: calls, attribute access, type hints, inheritance |
| Java | .java | Every usage of imported classes: new, static calls, annotations, extends/implements, generics |
| PHP | .php | Every usage of use-imported classes: new, static calls, type hints, extends/implements |
How it works: Each adapter builds an import map from import statements, then scans the entire code body using tree-sitter queries to find every reference to those imported names. A single
importused 5 times produces 5 dependencies. This matches deptrac behaviour.
🚀 Quick Start
Installation
# Install globally via npm
npm install -g tracebound
# Or install locally in your project
npm install --save-dev traceboundUsage
# Analyze a project
tracebound analyze <path> --config <config-file>
# With a custom output directory
tracebound analyze ./my-project --config ./tracebound.yaml --out-dir ./reportsExample
# Analyze a Java Spring modular monolith
tracebound analyze ../spring-modular-monolith --config ../spring-modular-monolith/tracebound.yamlDevelopment Setup
To build from source:
# Clone the repository
git clone https://gitlab.com/tracenode/tracebound
cd tracebound
# Install dependencies
npm install --legacy-peer-deps
# Build
npm run build
# Run locally
node dist/index.js analyze <path> --config <config-file>Output:
Found 79 supported files (139 total)
Extracting dependencies...
Found 468 dependencies
──────────────────────────────────
Files: 79
Dependencies: 468
Violations: 3
Uncovered: 393
Skipped: 62
──────────────────────────────────
✗ inventory → orders .../OrderEventInventoryHandler.java (com.sivalabs.bookstore.orders.domain.models.OrderCreatedEvent)
JSON report: ./tracebound-output/report.json
Mermaid graph: ./tracebound-output/graph.mmd📂 Output
The tool generates two files in the output directory (default: ./tracebound-output/):
report.json
{
"violations": [
{
"fromLayer": "inventory",
"toLayer": "orders",
"file": "/path/to/OrderEventInventoryHandler.java",
"dependency": "com.sivalabs.bookstore.orders.domain.models.OrderCreatedEvent"
}
],
"summary": {
"totalFiles": 79,
"totalDependencies": 468,
"totalViolations": 3
}
}graph.mmd (Mermaid)
graph TD
catalog --> common
inventory -.-> orders
notifications --> orders
orders --> catalog
linkStyle 1 stroke:red,stroke-width:2px- Solid arrows (
-->) = allowed dependencies - Dashed red arrows (
-.->) = violations
⚙️ Configuration
The tool supports two YAML config formats, auto-detected at load time.
Simplified Format
Best for projects where layers map to directory paths (e.g. modular monoliths):
layers:
- name: common
match: "**/common/**"
- name: catalog
match: "**/catalog/**"
- name: orders
match: "**/orders/**"
rules:
common: [] # no dependencies allowed
catalog: [common] # catalog may depend on common
orders: [common, catalog]Classic Format
For more advanced matching with multiple collector types:
tracebound:
paths:
- ./src
layers:
- name: Controller
collectors:
- type: classLike
value: ".*Controller.*"
- name: Service
collectors:
- type: classLike
value: ".*Service.*"
- name: Repository
collectors:
- type: classLike
value: ".*Repository.*"
ruleset:
Controller:
- Service
Service:
- Repository
Repository: ~ # ~ (null) = no dependencies allowedCollector Types
| Type | Description | Example |
| ----------- | ---------------------------------------------------- | ---------------------- |
| glob | Matches file paths using glob patterns (picomatch) | **/controllers/** |
| classLike | Matches file paths using a regular expression | .*Controller.* |
🧩 Architecture
src/
├── index.ts # Entry point (shebang)
├── cli.ts # Commander program setup
├── commands/
│ └── analyze.ts # Main analyze command (wires all modules)
├── config/
│ ├── types.ts # Config type definitions
│ └── loader.ts # YAML config parser (both formats)
├── utils/
│ ├── fileScanner.ts # Recursive directory scanner
│ └── layerMatcher.ts # Maps files/imports to layers
├── parser/
│ ├── languageMap.ts # Extension → grammar registry
│ └── treeSitter.ts # Tree-sitter parser wrapper (WASM)
├── extractor/
│ ├── types.ts # Dependency type
│ ├── adapter.ts # LanguageAdapter interface
│ ├── registry.ts # AdapterRegistry class
│ ├── dependencyExtractor.ts # Orchestrator (picks adapter, parses, extracts)
│ └── adapters/
│ ├── index.ts # Re-exports all adapters
│ ├── javascript.ts # JS/JSX/TS/TSX adapter
│ ├── python.ts # Python adapter
│ ├── java.ts # Java adapter
│ └── php.ts # PHP adapter
├── rules/
│ └── engine.ts # Rule evaluation engine
├── graph/
│ └── mermaid.ts # Mermaid diagram generator
└── formatter/
└── json.ts # JSON report formatterPipeline
┌──────────┐
│ Config │ ← YAML (classic or simplified)
│ Loader │
└────┬─────┘
│
┌────▼─────┐
│ File │ ← Recursive scan, .gitignore aware
│ Scanner │
└────┬─────┘
│
┌────▼─────┐
│ Parser │ ← web-tree-sitter (WASM grammars)
│ │
└────┬─────┘
│
┌────▼─────┐
│Extractor │ ← Language adapters (plugin system)
│ │
└────┬─────┘
│
┌────────┼────────┐
│ │ │
┌────▼───┐ ┌──▼───┐ ┌─▼──────┐
│ Rule │ │Mermaid│ │ JSON │
│ Engine │ │ Graph │ │Formatter│
└────────┘ └──────┘ └────────┘🔌 Adding a New Language
Adding support for a new language requires three steps:
1. Install the tree-sitter grammar
npm install tree-sitter-<language> --legacy-peer-deps2. Register in the language map
Add an entry to src/parser/languageMap.ts:
".go": {
name: "go",
package: "tree-sitter-go",
wasmFile: "tree-sitter-go.wasm",
},3. Create a language adapter
Create src/extractor/adapters/<language>.ts with a two-phase approach:
const TreeSitter = require("web-tree-sitter") as typeof import("web-tree-sitter");
import type { Tree, Query, Node as SyntaxNode } from "web-tree-sitter";
import type { LanguageAdapter } from "../adapter";
import type { Dependency } from "../types";
// Phase 1: Import query — builds the import map
const IMPORT_PATTERN = `(import_declaration path: (interpreted_string_literal) @source)`;
// Phase 2: Usage queries — finds every reference to imported names
const USAGE_QUERIES = [
`(type_identifier) @ref`,
`(call_expression function: (identifier) @ref)`,
// ... more patterns for new, extends, annotations, etc.
];
const BUILTINS = new Set(["string", "int", "bool" /* ... */]);
export const goAdapter: LanguageAdapter = {
name: "go",
extensions: [".go"],
extract(tree: Tree, filePath: string): Dependency[] {
// Phase 1: Build import map (localName → module)
const importMap = new Map<string, string>();
// ... parse imports ...
// Phase 2: Detect usages — find every reference in the code body
const deps: Dependency[] = [];
const seen = new Set<string>();
// ... run usage queries, filter by importMap, dedup by (name, row, col) ...
return deps;
},
};Then register it:
- Export from
src/extractor/adapters/index.ts - Register in
src/extractor/dependencyExtractor.ts
📊 CLI Reference
Usage: tracebound analyze <path> [options]
Analyze dependencies in the given path
Arguments:
path Root path of the project to analyze
Options:
-c, --config <file> Path to the config file (required)
-o, --out-dir <dir> Output directory for reports (default: "./tracebound-output")
-h, --help Display helpExit Codes
| Code | Meaning |
| ---- | ---------------------------------- |
| 0 | Analysis passed — no violations |
| 1 | Violations found or runtime error |
🔑 Key Concepts
| Term | Description | | -------------- | ---------------------------------------------------------------------------------------------------- | | Layer | A logical group of files (e.g. "controllers", "services", "common") | | Collector | A pattern (glob or regex) that assigns files to a layer | | Ruleset | A map of layer → allowed dependencies. If a layer imports from a layer not in its list, it's a violation | | Violation | A usage of an imported symbol that crosses a forbidden layer boundary | | Uncovered | A dependency where the source or target couldn't be mapped to any layer (e.g. third-party imports) | | Skipped | An intra-layer dependency (same layer on both sides) — always allowed |
🛠️ Tech Stack
| Component | Technology | | ------------------- | ----------------------------------------------------- | | Runtime | Node.js | | Language | TypeScript (CommonJS) | | CLI Framework | Commander.js | | AST Parsing | web-tree-sitter (WASM) | | YAML Parsing | js-yaml | | Glob Matching | picomatch | | Graph Output | Mermaid |
📄 License
MIT
🔗 Links
- NPM Package: https://www.npmjs.com/package/tracebound
- GitLab: https://gitlab.com/tracenode/tracebound
- Issues: https://gitlab.com/tracenode/tracebound/issues
- Inspiration: deptrac (PHP architecture validator)
🤝 Contributing
Contributions are welcome! To add a new language:
- Install the tree-sitter grammar:
npm install tree-sitter-<language> --legacy-peer-deps - Register in
src/parser/languageMap.ts - Create an adapter in
src/extractor/adapters/<language>.ts - Export and register it in the dependency extractor
See docs/adding-a-language.md for detailed instructions.
