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

@bfra.me/workspace-analyzer

v0.2.3

Published

Comprehensive monorepo static analysis for workspace configuration, dependencies, and architecture

Readme

@bfra.me/workspace-analyzer

Comprehensive monorepo static analysis for workspace configuration, dependencies, and architecture.

Features

  • Configuration Analysis: Detect inconsistencies across package.json, tsconfig.json, eslint.config.ts, and tsup.config.ts files
  • Dependency Analysis: Identify unused dependencies and circular imports with full path reporting
  • Architectural Validation: Validate against configurable rule sets (layer violations, barrel export misuse)
  • Performance Analysis: Identify tree-shaking blockers, large dependencies, and dead code
  • Modern CLI: Interactive TUI using @clack/prompts with progress reporting
  • Incremental Analysis: Caching support for fast re-analysis of large codebases
  • Multiple Output Formats: Console, JSON, and Markdown report generation

Installation

pnpm add -D @bfra.me/workspace-analyzer

Quick Start

# Analyze current workspace
npx workspace-analyzer

# Interactive mode with package selection
npx workspace-analyzer --interactive

# CI-friendly JSON output
npx workspace-analyzer --json > analysis-report.json

CLI Usage

The CLI provides a modern, interactive experience powered by @clack/prompts:

# Basic analysis
workspace-analyzer [path]

# With configuration file
workspace-analyzer --config workspace-analyzer.config.ts

# Output formats
workspace-analyzer --json          # JSON output
workspace-analyzer --markdown      # Markdown report

# Filtering options
workspace-analyzer --min-severity warning
workspace-analyzer --categories dependency,architecture

# Analysis control
workspace-analyzer --no-cache      # Disable caching
workspace-analyzer --verbose       # Detailed output
workspace-analyzer --dry-run       # Preview without analysis

CLI Options

| Option | Description | | ------------------------ | ----------------------------------------------------------- | | --config <path> | Path to configuration file | | --json | Output results as JSON | | --markdown | Output results as Markdown | | --min-severity <level> | Minimum severity to report (info, warning, error, critical) | | --categories <list> | Comma-separated categories to analyze | | --include <patterns> | Glob patterns for files to include | | --exclude <patterns> | Glob patterns for files to exclude | | --no-cache | Disable incremental analysis caching | | --cache-dir <path> | Custom cache directory | | --concurrency <n> | Maximum parallel operations | | --verbose | Enable verbose logging | | --quiet | Suppress non-essential output | | --dry-run | Preview analysis without executing | | --interactive | Interactive package selection | | --fix | Apply auto-fixes where available (placeholder) |

Visualization Command

Generate interactive dependency graph visualizations with the visualize command:

# Generate interactive HTML visualization
workspace-analyzer visualize

# Generate JSON data for external tools
workspace-analyzer visualize --format json --output graph.json

# Generate Mermaid diagram
workspace-analyzer visualize --format mermaid --output graph.mmd

# Interactive mode with options
workspace-analyzer visualize --interactive

# Custom title and node limits
workspace-analyzer visualize --title "My Project" --max-nodes 100

# Generate both HTML and JSON
workspace-analyzer visualize --format both

Visualization Options

| Option | Description | Default | | --- | --- | --- | | --output <path> | Output file path | workspace-graph.html | | --format <format> | Output format: html, json, mermaid, both | html | | --no-open | Disable auto-opening in browser | false (opens by default) | | --title <title> | Visualization title | Workspace Dependency Graph | | --max-nodes <number> | Maximum nodes to render (performance) | 1000 | | --include-type-imports | Include type-only imports in graph | false | | --interactive | Interactive mode for options selection | false |

Export Formats

HTML - Self-contained interactive visualization with D3.js:

  • Force-directed graph layout with zoom/pan controls
  • Filter by layer, severity, package scope
  • View modes: all, cycles-only, violations-only
  • Node tooltips with violation details
  • No external network requests (fully offline)

JSON - Structured data for external tools and custom processing:

{
  "nodes": [
    {
      "id": "src/utils/helpers.ts",
      "name": "helpers.ts",
      "filePath": "src/utils/helpers.ts",
      "packageName": "@myorg/utils",
      "layer": "infrastructure",
      "importsCount": 5,
      "importedByCount": 12,
      "isInCycle": false,
      "violations": [
        {
          "id": "v1",
          "message": "Layer violation: infrastructure importing from domain",
          "severity": "error",
          "ruleId": "layer-dependency"
        }
      ],
      "highestViolationSeverity": "error"
    }
  ],
  "edges": [
    {
      "source": "src/utils/helpers.ts",
      "target": "src/core/types.ts",
      "type": "static",
      "isInCycle": false
    }
  ],
  "cycles": [
    {
      "id": "cycle-1",
      "nodes": ["src/a.ts", "src/b.ts"],
      "edges": [
        {"from": "src/a.ts", "to": "src/b.ts"},
        {"from": "src/b.ts", "to": "src/a.ts"}
      ],
      "length": 2,
      "description": "Circular dependency: a.ts → b.ts → a.ts"
    }
  ],
  "statistics": {
    "totalNodes": 250,
    "totalEdges": 380,
    "totalCycles": 3,
    "nodesByLayer": {
      "domain": 50,
      "application": 80,
      "infrastructure": 120
    },
    "violationsBySeverity": {
      "critical": 0,
      "error": 5,
      "warning": 15,
      "info": 2
    },
    "packagesAnalyzed": 12,
    "filesAnalyzed": 250
  },
  "metadata": {
    "workspacePath": "/path/to/workspace",
    "generatedAt": "2025-12-10T00:00:00Z",
    "analyzerVersion": "0.1.0"
  }
}

Mermaid - Diagram markup for documentation and external rendering:

graph LR
  src_utils_helpers_ts["helpers.ts"]:::class-error
  src_core_types_ts["types.ts"]:::class-normal
  src_utils_helpers_ts --> src_core_types_ts

  classDef class-critical fill:#ff4444,stroke:#cc0000,stroke-width:3px,color:#fff
  classDef class-error fill:#ff8844,stroke:#cc4400,stroke-width:2px,color:#fff
  classDef class-warning fill:#ffcc44,stroke:#ccaa00,stroke-width:2px,color:#000
  classDef class-info fill:#4488ff,stroke:#0044cc,stroke-width:1px,color:#fff
  classDef class-normal fill:#88ccff,stroke:#0088cc,stroke-width:1px,color:#000

Common Visualization Scenarios

Investigate Circular Dependencies:

# Generate visualization focusing on cycles
workspace-analyzer visualize --title "Circular Dependencies" --output cycles.html

# Export cycle data for CI validation
workspace-analyzer visualize --format json --output cycles.json

# Generate Mermaid diagram for documentation
workspace-analyzer visualize --format mermaid --output cycles.mmd

Analyze Layer Architecture:

# Visualize architectural boundaries with violations highlighted
workspace-analyzer visualize --title "Architecture Review" --output architecture.html

# Focus on specific package scope
workspace-analyzer visualize --title "Core Packages" --output core.html

Performance Analysis:

# Limit visualization to high-impact nodes
workspace-analyzer visualize --max-nodes 200 --output critical-paths.html

# Export JSON for custom metrics calculation
workspace-analyzer visualize --format json --output metrics.json

CI/CD Integration:

# Generate artifacts for PR review
workspace-analyzer visualize --format both --no-open --output build/graph

# Validate no new circular dependencies
workspace-analyzer visualize --format json | jq '.statistics.totalCycles == 0'

Integration with External Tools

The JSON export format is designed for integration with custom analysis pipelines:

import {readFile} from 'node:fs/promises'

const data = JSON.parse(await readFile('graph.json', 'utf-8'))

// Find all files with critical violations
const criticalFiles = data.nodes.filter(
  node => node.highestViolationSeverity === 'critical'
)

// Identify largest cycles
const largeCycles = data.cycles
  .filter(cycle => cycle.length > 5)
  .sort((a, b) => b.length - a.length)

// Calculate dependency metrics
const avgImportsPerFile = data.statistics.totalEdges / data.statistics.totalNodes
const cycleRate = data.statistics.totalCycles / data.statistics.totalNodes

// Detect architectural hotspots
const layerViolations = data.nodes
  .filter(node => node.violations.some(v => v.ruleId === 'layer-dependency'))
  .map(node => ({
    file: node.filePath,
    layer: node.layer,
    violations: node.violations.length,
  }))
  .sort((a, b) => b.violations - a.violations)

// Find most connected nodes (potential coupling issues)
const highlyConnected = data.nodes
  .filter(node => node.importsCount + node.importedByCount > 20)
  .sort((a, b) =>
    (b.importsCount + b.importedByCount) - (a.importsCount + a.importedByCount)
  )

Programmatic API

analyzeWorkspace(path, options)

Analyzes an entire workspace for issues.

import {analyzeWorkspace} from '@bfra.me/workspace-analyzer'

const result = await analyzeWorkspace('./my-monorepo', {
  minSeverity: 'warning',
  categories: ['dependency', 'architecture'],
  onProgress: (progress) => {
    console.log(`${progress.phase}: ${progress.processed}/${progress.total}`)
  },
})

if (result.success) {
  console.log(`Found ${result.data.summary.totalIssues} issues`)
  for (const issue of result.data.issues) {
    console.log(`[${issue.severity}] ${issue.title}`)
    console.log(`  ${issue.location.filePath}:${issue.location.line}`)
    if (issue.suggestion) {
      console.log(`  Fix: ${issue.suggestion}`)
    }
  }
} else {
  console.error(`Analysis failed: ${result.error.message}`)
}

analyzePackages(path, packageNames, options)

Analyzes specific packages within a workspace.

import {analyzePackages} from '@bfra.me/workspace-analyzer'

const result = await analyzePackages('./my-monorepo', [
  '@myorg/core',
  '@myorg/utils',
])

if (result.success) {
  console.log(`Analyzed ${result.data.summary.packagesAnalyzed} packages`)
}

defineConfig(config)

Type-safe configuration helper.

import {defineConfig} from '@bfra.me/workspace-analyzer'

export default defineConfig({
  minSeverity: 'warning',
  categories: ['dependency', 'architecture'],
})

Configuration

Create a workspace-analyzer.config.ts file in your workspace root:

import {defineConfig} from '@bfra.me/workspace-analyzer'

export default defineConfig({
  // File patterns
  include: ['**/*.ts', '**/*.tsx'],
  exclude: ['**/node_modules/**', '**/lib/**', '**/dist/**'],

  // Analysis settings
  minSeverity: 'warning',
  categories: ['dependency', 'configuration', 'architecture'],

  // Performance
  cache: true,
  cacheDir: '.workspace-analyzer-cache',
  concurrency: 4,

  // Package patterns (for monorepo scanning)
  packagePatterns: ['packages/*', 'apps/*'],

  // Per-analyzer configuration
  analyzers: {
    'circular-import': {enabled: true, severity: 'error'},
    'unused-dependency': {enabled: true, severity: 'warning'},
    'config-consistency': {enabled: true},
    'tree-shaking-blocker': {enabled: true},
    'dead-code': {enabled: false}, // Disable specific analyzer
  },

  // Architectural rules
  architecture: {
    layers: [
      {name: 'domain', patterns: ['**/domain/**'], allowedImports: []},
      {name: 'application', patterns: ['**/application/**'], allowedImports: ['domain']},
      {name: 'infrastructure', patterns: ['**/infrastructure/**'], allowedImports: ['domain', 'application']},
    ],
    allowBarrelExports: ['**/index.ts'],
    enforcePublicApi: true,
  },
})

Configuration File Names

The analyzer searches for configuration in these files (in order):

  • workspace-analyzer.config.ts
  • workspace-analyzer.config.js
  • workspace-analyzer.config.mjs
  • workspace-analyzer.config.cjs

Built-in Analyzers

Configuration Analyzers

| Analyzer | Description | | -------------------- | ------------------------------------------------------ | | package-json | Validates package.json structure, exports, and scripts | | tsconfig | Checks TypeScript configuration consistency | | eslint-config | Validates ESLint configuration patterns | | build-config | Analyzes tsup/build configuration | | config-consistency | Cross-validates configuration across files | | version-alignment | Checks dependency version consistency across packages | | exports-field | Validates exports field against actual source files |

Dependency Analyzers

| Analyzer | Description | | ---------------------- | ----------------------------------------------------- | | unused-dependency | Detects dependencies declared but not imported | | circular-import | Finds circular import chains using Tarjan's algorithm | | peer-dependency | Validates peer dependency declarations | | duplicate-dependency | Finds duplicate dependencies across packages |

Architectural Analyzers

| Analyzer | Description | | --------------- | ------------------------------------------------- | | architectural | Enforces layer boundaries and import restrictions |

Performance Analyzers

| Analyzer | Description | | ---------------------- | ----------------------------------------------------- | | tree-shaking-blocker | Detects patterns that prevent tree-shaking | | dead-code | Finds unreachable/unused exports | | duplicate-code | Identifies similar code blocks via AST fingerprinting | | large-dependency | Flags large dependencies that may impact bundle size |

Issue Categories

| Category | Description | | ----------------- | --------------------------------------------------------- | | configuration | Package.json, tsconfig.json, and build config issues | | dependency | Unused, duplicate, or misversioned dependencies | | architecture | Layer violations, barrel export misuse, public API issues | | performance | Tree-shaking blockers, large dependencies | | circular-import | Circular dependency chains | | unused-export | Exports not used within the workspace | | type-safety | TypeScript configuration issues |

Severity Levels

| Level | Description | | ---------- | --------------------------------------------------- | | info | Informational findings, suggestions for improvement | | warning | Potential issues that should be reviewed | | error | Problems that should be fixed | | critical | Severe issues requiring immediate attention |

Architectural Rules

Layer Violation Rule

Enforces architectural layer boundaries:

defineConfig({
  architecture: {
    layers: [
      {name: 'domain', patterns: ['**/domain/**'], allowedImports: []},
      {name: 'application', patterns: ['**/app/**'], allowedImports: ['domain']},
      {name: 'infrastructure', patterns: ['**/infra/**'], allowedImports: ['domain', 'application']},
    ],
  },
})

Barrel Export Rule

Controls export * usage:

defineConfig({
  architecture: {
    // Allow barrel exports only in specific files
    allowBarrelExports: ['**/index.ts'],
    // Or disable barrel exports entirely
    // allowBarrelExports: false,
  },
})

Public API Rule

Enforces explicit public API surface:

defineConfig({
  architecture: {
    enforcePublicApi: true,
  },
})

Output Formats

Console (Default)

Colorized terminal output with severity highlighting and file locations.

JSON

Machine-readable output for CI integration:

workspace-analyzer --json > report.json
{
  "issues": [],
  "summary": {
    "totalIssues": 5,
    "bySeverity": {"warning": 3, "error": 2},
    "byCategory": {"dependency": 2, "architecture": 3},
    "packagesAnalyzed": 8,
    "filesAnalyzed": 150,
    "durationMs": 2500
  }
}

Markdown

Human-readable report for documentation or PR comments:

workspace-analyzer --markdown > ANALYSIS.md

Caching

The analyzer supports incremental caching for improved performance:

defineConfig({
  cache: true,
  cacheDir: '.workspace-analyzer-cache',
  maxCacheAge: 7 * 24 * 60 * 60 * 1000, // 7 days
  hashAlgorithm: 'sha256',
})

Cache is automatically invalidated when:

  • Source files change (content hash)
  • Configuration files change
  • Analyzer versions change

CI Integration

GitHub Actions

- name: Run workspace analysis
  run: pnpm workspace-analyzer --json > analysis-report.json

- name: Check for errors
  run: |
    if jq -e '.summary.bySeverity.error > 0' analysis-report.json; then
      echo "Analysis found errors"
      exit 1
    fi

Pre-commit Hook

# package.json
{
  "lint-staged": {
    "*.{ts,tsx}": ["workspace-analyzer --categories dependency,architecture"]
  }
}

Development

# Build the package
pnpm --filter @bfra.me/workspace-analyzer build

# Run tests
pnpm --filter @bfra.me/workspace-analyzer test

# Type check
pnpm --filter @bfra.me/workspace-analyzer type-check

Related Packages

License

MIT © Marcus R. Brown