npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

Readme

🏗️ tracebound

npm version npm downloads License: MIT

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
  • .gitignore aware — 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 import used 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 tracebound

Usage

# Analyze a project
tracebound analyze <path> --config <config-file>

# With a custom output directory
tracebound analyze ./my-project --config ./tracebound.yaml --out-dir ./reports

Example

# Analyze a Java Spring modular monolith
tracebound analyze ../spring-modular-monolith --config ../spring-modular-monolith/tracebound.yaml

Development 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 allowed

Collector 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 formatter

Pipeline

                ┌──────────┐
                │  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-deps

2. 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 help

Exit 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:

  1. Install the tree-sitter grammar: npm install tree-sitter-<language> --legacy-peer-deps
  2. Register in src/parser/languageMap.ts
  3. Create an adapter in src/extractor/adapters/<language>.ts
  4. Export and register it in the dependency extractor

See docs/adding-a-language.md for detailed instructions.