Comprehensive rule system for validating dependencies against architectural constraints, circular dependencies, and custom business rules.
Define validation rules that enforce architectural constraints and best practices.
interface IFlattenedRuleSet {
/** Rules that forbid certain dependency patterns */
forbidden?: IRegularForbiddenRuleType[];
/** Rules that allow only certain dependency patterns */
allowed?: IRegularAllowedRuleType[];
/** Rules that require certain dependency patterns to exist */
required?: IRegularRequiredRuleType[];
/** Default severity level for allowed rule violations */
allowedSeverity?: SeverityType;
/** Global options for rule processing */
options?: IRuleSetOptions;
}
type SeverityType = "error" | "warn" | "info" | "ignore";Usage Examples:
import { cruise } from "dependency-cruiser";
const ruleSet = {
forbidden: [
{
name: "no-circular",
comment: "Circular dependencies create maintenance problems",
severity: "error",
from: {},
to: { circular: true }
},
{
name: "no-orphans",
comment: "Orphaned modules may indicate dead code",
severity: "warn",
from: { orphan: true },
to: {}
}
],
allowed: [
{
name: "only-src-imports",
comment: "Source code should only import from src or node_modules",
from: { path: "^src/" },
to: { path: "^(src/|node_modules/)" }
}
],
options: {
doNotFollow: "node_modules"
}
};
const result = await cruise(
["src"],
{ validate: true, ruleSet }
);Rules that prevent specific dependency patterns.
interface IRegularForbiddenRuleType extends IBaseRuleType {
/** Criteria for the source module of a dependency */
from: IFromRestriction;
/** Criteria for the target module of a dependency */
to: IToRestriction;
/** Scope to apply rule to - 'module' (default) or 'folder' */
scope?: RuleScopeType;
}
interface IBaseRuleType {
/** Short name for the rule (appears in violation reports) */
name?: string;
/** Severity level for violations */
severity?: SeverityType;
/** Documentation explaining why the rule exists */
comment?: string;
}
type RuleScopeType = "module" | "folder";Forbidden Rule Examples:
// Prevent circular dependencies
{
name: "no-circular",
severity: "error",
from: {},
to: { circular: true }
}
// Prevent importing core modules
{
name: "no-core-modules",
severity: "warn",
from: { path: "^src/" },
to: { dependencyTypes: ["core"] }
}
// Prevent test imports in production code
{
name: "no-test-imports-in-src",
severity: "error",
from: { path: "^src/" },
to: { path: "test/" }
}
// Prevent deprecated npm packages
{
name: "no-deprecated",
severity: "warn",
from: {},
to: {
dependencyTypes: ["npm"],
path: "^(left-pad|event-stream)$"
}
}
// Prevent unstable folder dependencies
{
name: "no-unstable-deps",
severity: "error",
scope: "folder",
from: {},
to: { moreUnstable: true }
}Rules that permit only specific dependency patterns (whitelist approach).
interface IRegularAllowedRuleType extends IBaseRuleType {
/** Criteria for the source module of a dependency */
from: IFromRestriction;
/** Criteria for the target module of a dependency */
to: IToRestriction;
/** Scope to apply rule to - 'module' (default) or 'folder' */
scope?: RuleScopeType;
}Allowed Rule Examples:
// Only allow specific imports from external modules
{
name: "only-allowed-external",
severity: "error",
from: { path: "^src/" },
to: {
path: "^(lodash|react|@types/)",
dependencyTypes: ["npm"]
}
}
// Only allow local and npm dependencies
{
name: "only-local-and-npm",
severity: "error",
from: {},
to: { dependencyTypes: ["local", "npm"] }
}
// Restrict component imports
{
name: "components-only-from-index",
severity: "error",
from: { path: "^src/(?!components/)" },
to: {
path: "^src/components/",
pathNot: "^src/components/index"
}
}Rules that enforce the existence of specific dependency patterns.
interface IRegularRequiredRuleType extends IBaseRuleType {
/** Criteria for modules that must have the required dependency */
from: IFromRestriction;
/** Criteria for the required dependency target */
to: IRequiredToRestrictionType;
/** Scope to apply rule to - 'module' (default) or 'folder' */
scope?: RuleScopeType;
}Required Rule Examples:
// Require test files for all source modules
{
name: "test-required",
severity: "warn",
from: { path: "^src/.*\\.js$" },
to: {
path: "^test/.*\\.test\\.js$",
pathNot: "node_modules"
}
}
// Require logging in error handlers
{
name: "logging-required",
severity: "error",
from: { path: "error-handler" },
to: { path: "logger" }
}Detailed criteria for matching modules in rules.
interface IFromRestriction {
/** Path pattern (regular expression) for source modules */
path?: string;
/** Inverse path pattern - modules NOT matching this pattern */
pathNot?: string;
/** Whether to match orphaned modules */
orphan?: boolean;
/** Whether to match reachable modules */
reachable?: boolean;
/** License pattern for npm modules */
license?: string;
/** License pattern to exclude */
licenseNot?: string;
}
interface IToRestriction {
/** Path pattern (regular expression) for target modules */
path?: string;
/** Inverse path pattern - modules NOT matching this pattern */
pathNot?: string;
/** Whether to match circular dependencies */
circular?: boolean;
/** Whether to match dynamic imports */
dynamic?: boolean;
/** Whether to match exotic requires */
exoticallyRequired?: boolean;
/** Whether to match modules that could not be resolved */
couldNotResolve?: boolean;
/** Whether to match pre-compilation only dependencies */
preCompilationOnly?: boolean;
/** Whether to match type-only imports */
typeOnly?: boolean;
/** Whether to match more unstable dependencies (folder scope) */
moreUnstable?: boolean;
/** Dependency types to match */
dependencyTypes?: DependencyType[];
/** Dependency types to exclude */
dependencyTypesNot?: DependencyType[];
/** Module systems to match */
moduleSystems?: ModuleSystemType[];
/** Module systems to exclude */
moduleSystemsNot?: ModuleSystemType[];
/** License pattern for npm modules */
license?: string;
/** License pattern to exclude */
licenseNot?: string;
/** Whether to match reachable modules */
reachable?: boolean;
/** Whether to match via reachability pattern */
via?: IViaRestrictionType[];
/** Whether to match via reachability NOT pattern */
viaNot?: IViaRestrictionType[];
/** Whether to match via only specified patterns */
viaOnly?: IViaRestrictionType[];
}Rules can be applied at different scopes for architectural analysis.
Module Scope (Default):
{
name: "no-module-circular",
scope: "module", // or omit for default
from: {},
to: { circular: true }
}Folder Scope:
{
name: "no-folder-circular",
scope: "folder",
from: {},
to: { circular: true }
}
{
name: "stable-dependencies-principle",
scope: "folder",
from: {},
to: { moreUnstable: true }
}Complex rule patterns for sophisticated architectural constraints.
Layered Architecture:
{
forbidden: [
{
name: "no-skip-layers",
comment: "Don't skip layers in the architecture",
from: { path: "^src/presentation/" },
to: { path: "^src/data/" } // Skip business layer
},
{
name: "no-reverse-dependencies",
comment: "Lower layers shouldn't depend on higher layers",
from: { path: "^src/data/" },
to: { path: "^src/(presentation|business)/" }
}
]
}Domain-Driven Design:
{
forbidden: [
{
name: "no-cross-domain-deps",
comment: "Domains should not directly depend on each other",
from: { path: "^src/domains/user/" },
to: { path: "^src/domains/order/" }
}
],
allowed: [
{
name: "domains-via-shared-only",
comment: "Cross-domain communication via shared interfaces only",
from: { path: "^src/domains/" },
to: { path: "^src/(shared/|domains/[^/]+/)" }
}
]
}Reachability Rules:
{
forbidden: [
{
name: "no-unreachable-from-index",
comment: "All modules should be reachable from entry points",
from: {
path: "^src/",
reachable: false
},
to: {}
}
]
}Understanding how violations are reported and handled.
interface IViolation {
/** Type of violation */
type: ViolationType;
/** Source module involved in violation */
from: string;
/** Target module involved in violation */
to: string;
/** Rule that was violated */
rule: IRuleSummary;
/** Path through dependency graph (for cycles) */
cycle?: IMiniDependency[];
/** Via path for reachability violations */
via?: IMiniDependency[];
}
interface IRuleSummary {
/** Rule name */
name: string;
/** Rule severity */
severity: SeverityType;
/** Comment explaining the rule */
comment?: string;
}
type ViolationType = "dependency" | "module" | "cycle" | "reachability" | "instability";Processing Violations:
import { cruise } from "dependency-cruiser";
const result = await cruise(["src"], {
validate: true,
ruleSet: myRuleSet
});
// Check for violations
if (result.exitCode !== 0) {
console.log(`Found ${result.output.summary.error} errors`);
console.log(`Found ${result.output.summary.warn} warnings`);
// Process each violation
result.output.summary.violations.forEach(violation => {
console.log(`${violation.rule.severity}: ${violation.rule.name}`);
console.log(` ${violation.from} -> ${violation.to}`);
if (violation.rule.comment) {
console.log(` ${violation.rule.comment}`);
}
});
}Optimize rule processing for large codebases.
const optimizedOptions = {
validate: true,
ruleSet: myRuleSet,
// Skip analyses not needed by any rules
skipAnalysisNotInRules: true,
// Use caching for repeated analyses
cache: {
folder: "node_modules/.cache/dependency-cruiser",
strategy: "metadata"
},
// Exclude irrelevant paths early
doNotFollow: "node_modules",
exclude: ["test/fixtures", "docs"]
};Test rules before applying them to ensure they work as intended.
// Test rule on small subset first
const testResult = await cruise(
["src/core"], // Small subset
{
validate: true,
ruleSet: {
forbidden: [newRule] // Test single rule
}
}
);
// Validate rule catches expected violations
if (testResult.output.summary.violations.length === 0) {
console.log("Rule may be too permissive");
} else {
console.log("Rule found violations as expected");
}