mrkeyoor.com_
Sat 08 Aug 22:52 UTC
npmCLI & Toolingupdated 08 Aug 2026

dependency-cruiser

dependency-cruiser scans JavaScript, TypeScript, JSX, Vue, Svelte, CoffeeScript, and related source files, resolves their imports, and turns the result into an enforceable dependency graph. You describe forbidden, allowed, or required relationships in a configuration file, then run the CLI in CI like a structural linter. It can catch circular imports, unresolvable modules, undeclared packages, cross-layer dependencies, and isolated features that reach into one another. The same scan can produce text, JSON, Mermaid, D2, GraphViz, HTML, and other reports, so it serves both architecture enforcement and investigation.

Verdict

The strongest JavaScript dependency-policy tool when a team is willing to encode its architecture and keep the resolver configuration honest. Skip it for a one-off cycle check or on older Node releases, where a narrower tool creates much less ceremony.

API stability3/5The core forbidden-rule model and depcruise workflow have survived many releases, and version 18 still supports familiar from and to path restrictions, severities, and reporters. Major upgrades are not passive, though: the current package is ESM-first, its API returns promises, configuration discovery changed in earlier majors, and version 18 raises the runtime floor to Node 22, 24, or 26+.
Docs5/5The repository has separate, current references for the CLI, rule syntax, options, API, result format, reporters, recipes, and troubleshooting. The FAQ directly explains missing TypeScript compiler discovery, type-only dependency handling, ESM config naming, alias resolution, and oversized graphs. Examples include both shell commands and complete rule objects, not just a feature list.
Maintenance5/5Version 18.1.1 was published on August 2, 2026, the repository was pushed on August 8, 2026, and the latest release notes show ongoing dependency updates, build work, and fixes. The project has 37 open issues and pull requests combined, a manageable queue beside recent releases, and it validates its own architecture in its build scripts.
Ecosystem4/5The package records 3,122,239 downloads for the measured week and the repository has 7,043 stars. It understands ES modules, CommonJS, AMD, TypeScript, JSX, Vue, Svelte, CoffeeScript, and LiveScript when their compilers are available, and it connects to tsconfig and webpack resolution. Its integrations are broad, but architecture rules remain specific to this tool rather than a shared standard.

Use it if

  • You need CI to reject imports that cross architectural boundaries, such as UI code reaching directly into persistence
  • You want one tool to find cycles, orphan files, unresolved imports, and package.json dependency mistakes across a large JavaScript or TypeScript codebase
  • You need dependency graphs that can be filtered, collapsed to package or folder level, and emitted as Mermaid, D2, GraphViz, JSON, or HTML
  • You are adopting architecture rules gradually and need a baseline file that separates known violations from new ones
Skip it if

Setup reality

Install it locally as a development dependency with npm install --save-dev dependency-cruiser, then run npx depcruise --init. The initializer inspects the project, asks questions, and creates a .dependency-cruiser.js or .dependency-cruiser.cjs file; pure ESM projects need the .cjs form when the config uses module.exports. Version 18.1.1 will not install on the Node 20 line because its engine range is ^22 || ^24 || >=26. TypeScript, Vue, Svelte, CoffeeScript, and other non-plain-JavaScript analysis depends on the relevant compiler being installed where dependency-cruiser can find it. Run depcruise --info to confirm that detection. For TypeScript path aliases and type-only imports, pass or configure tsconfig.json and enable tsPreCompilationDeps when those edges matter. Webpack-specific aliases need its resolve configuration or equivalent enhanced-resolve options. The generated recommended rules often reveal existing cycles, orphan entry points, and unresolved aliases on the first run; use targeted exclusions or a known-violations baseline instead of switching rules off blindly. Text and JSON reporting need no system package, but an SVG pipeline using output type dot requires the separate GraphViz dot executable. Keep --include-only and doNotFollow scoped, especially in a monorepo, or the scan and graph can expand through far more files than intended.

Patterns

Install locally and generate a starter configinstall-and-init

npm install --save-dev dependency-cruiser
npx depcruise --init
npx depcruise src

Version 18 requires Node 22, 24, or 26+. A local install is recommended so compiler discovery matches the project.

Reject circular dependenciesfail-on-cycles

module.exports = {
  forbidden: [
    {
      name: 'no-circular',
      severity: 'error',
      from: {},
      to: { circular: true },
    },
  ],
  options: { doNotFollow: { path: 'node_modules' } },
};

Severity error gives the err reporter a nonzero exit code, which is what makes the rule useful in CI.

Keep the domain layer independent of infrastructureenforce-layers

module.exports = {
  forbidden: [
    {
      name: 'domain-not-to-infrastructure',
      comment: 'Domain code must not import adapters or database code',
      severity: 'error',
      from: { path: '^src/domain/' },
      to: { path: '^src/(infrastructure|adapters)/' },
    },
  ],
};

Path restrictions are regular expressions matched against normalized module paths, so anchor them to avoid accidental substring matches.

Stop sibling features from importing each otherisolate-features

module.exports = {
  forbidden: [
    {
      name: 'features-not-to-features',
      severity: 'error',
      from: { path: '(^src/features/)([^/]+)/' },
      to: { path: '^$1', pathNot: '$1$2' },
    },
  ],
};

The $1 and $2 values come from capture groups in from.path; changing that expression can silently change what pathNot means.

Prevent production code from importing dev dependenciesblock-dev-dependencies

module.exports = {
  forbidden: [
    {
      name: 'not-to-dev-dep',
      severity: 'error',
      from: { path: '^src/' },
      to: { dependencyTypes: ['npm-dev'] },
    },
  ],
};

Exclude tests, stories, and build scripts in from.pathNot if they legitimately import packages from devDependencies.

Include TypeScript type-only and pre-compilation edgesinclude-type-imports

module.exports = {
  forbidden: [],
  options: {
    tsConfig: { fileName: 'tsconfig.json' },
    tsPreCompilationDeps: true,
    doNotFollow: { path: 'node_modules' },
  },
};

TypeScript must be installed where dependency-cruiser can discover it; check with npx depcruise --info when imports are missing.

Render a filtered SVG dependency graphcreate-svg-graph

npx depcruise src \
  --include-only '^src/' \
  --output-type dot \
  | dot -T svg > dependency-graph.svg

The final dot command comes from GraphViz and is not installed by dependency-cruiser. Filtering first keeps the graph legible.

Write a Mermaid graph for Markdowncreate-mermaid-graph

npx depcruise src \
  --include-only '^src/' \
  --output-type mermaid \
  --output-to dependency-graph.mmd

The Mermaid reporter minifies names by default to stay within renderer input limits; it is less customizable than GraphViz output.

Inspect one module and its neighborsfocus-module

npx depcruise src \
  --focus '^src/services/billing\.ts$' \
  --focus-depth 2 \
  --output-type text

Quote regular expressions so the shell does not expand or reinterpret them differently across platforms.

Show modules affected by changes since maincheck-affected

npx depcruise src \
  --affected main \
  --include-only '^src/' \
  --output-type text

The affected option is command-line only; the current type reference says it is ignored when placed in the API options or config file.

Adopt rules without accepting new violationsbaseline-existing-violations

npx depcruise src --output-type baseline \
  --output-to .dependency-cruiser-known-violations.json

# Later CI runs ignore the recorded set but still fail on new errors
npx depcruise src --ignore-known

Commit the baseline and shrink it as violations are fixed; regenerating it on every CI run would hide regressions.

Cruise source files from an ESM scriptuse-programmatic-api

import { cruise } from 'dependency-cruiser';

const result = await cruise(['src'], {
  includeOnly: '^src/',
  doNotFollow: 'node_modules',
  outputType: 'json',
});

if (result.exitCode !== 0) process.exitCode = result.exitCode;
console.log(result.output);

The current API is asynchronous and the package root exposes an ESM import. Validation also requires validate: true and a ruleSet.

Alternatives

PackageRegistryPick it when
madgenpmChoose it for quick circular-dependency checks and simple visual graphs when you do not need a full rule language
dpdmnpmChoose it for a smaller TypeScript-focused cycle detector with straightforward command-line output
eslint-plugin-boundariesnpmChoose it when architectural import rules should run inside the ESLint workflow developers already use
knipnpmChoose it when the real goal is unused files, exports, and dependencies rather than graph policy