Dependency Cruiser:JS/TS 代码架构的守门员
阅读时间: 大约 9 分钟
Dependency Cruiser:JS/TS 代码架构的守门员

在前端项目里,ESLint 管”代码写得对不对”,Prettier 管”格式整不整齐”,但模块之间谁能引用谁——也就是架构层面的边界——长期处于无人自动把守的状态。仓库规模一大、AI 编码助手大量参与写代码之后,后端被前端组件偷偷引用、测试代码反向被生产代码依赖、循环依赖悄悄出现,几乎全靠 Code Review 人肉盯。荷兰开发者 sverweij 维护的开源项目 Dependency Cruiser 就是来补这个缺口的:它扫描整个仓库的依赖图,拿你自己定义的一套规则去比对,违规就报错,同时还能把依赖关系画成图。项目以 MIT 协议发布,作者自称”Made with 🤘 in Holland”。
一、它到底做两件事
官方 README 把功能概括得很直白:遍历任意 JavaScript、TypeScript、CoffeeScript、LiveScript 项目中的依赖,然后——
- 按你自己的规则校验它们(validate against your own rules);
- 报告违规,要么输出给构建流程看的文本,要么输出给人看的图。
换句话说,它不是一个新的 lint 规则集,而是一个”依赖图引擎”:底层用 acorn(README 明确致谢了 Marijn Haverbeke)解析 AST,把 import / require / define 这些语句抽成一张有向依赖图,再在图上跑规则匹配。除了裸 JS,官方声明支持 ES6、CommonJS、AMD 模块格式,以及 .jsx、.tsx、.vue、.svelte 这类框架组件文件,还能识别 Webpack 的 alias。
二、怎么用:三条命令
上手成本很低,官方给出的标准流程:
npm install --save-dev dependency-cruiser # 或 pnpm add -D
npx dependency-cruiser --init
npx dependency-cruiser src--init 会探测一下你的项目环境、问几个问题,然后生成一份 .dependency-cruiser.js 配置文件。官方称这个初始化过程会顺带写入一批”大多数项目都合理”的默认规则,包括:循环依赖、package.json 里缺失声明的依赖、孤儿模块(没人引用也不被入口引用的文件)、以及生产代码错误地依赖了 devDependencies / optionalDependencies。这意味着跑完 --init 立刻就能在 CI 里挡掉一批常见腐化,不必先写规则。
写自定义规则也很简单,README 给的样例是”禁止 test 目录以外的代码引用 test 目录”:
{
"forbidden": [
{
"name": "not-to-test",
"severity": "error",
"from": { "pathNot": "^test" },
"to": { "path": "^test" }
}
]
}规则就是”从什么路径、不许到什么路径”的正则配对,severity 分 error / warn 两档。这正是 Koala 点评里说的”配置即代码”:架构约定不再写在 Wiki 里靠人记,而是变成仓库里一份可评审、可 diff、可在 CI 强制执行的配置。
三、输出:给机器看,也给人看
校验结果默认以类似 ESLint 的文本格式输出,直接能接 CI 失败退出码:

而可视化是它的另一张牌。官方支持的输出类型相当全:dot(交给 GraphViz 渲染成 SVG/PNG,即本文首图)、html(自包含单文件报告)、mermaid、json、csv、纯文本。一行命令出图:
npx dependency-cruiser src --include-only "^src" --output-type dot | dot -T svg > dependency-graph.svgREADME 里那句”生成各种格式的依赖图,包括可以贴墙上给你奶奶看的酷炫可视化”是作者的真实口吻。图上用不同颜色高亮违规边(首图中红色箭头即跨边界的违规引用),架构腐化点一眼可见。值得一提的是,项目自己用自己做 CI 检查(README 注明见 package.json 里的 depcruise 脚本),属于 dogfooding。
四、官方自己的口径与局限
必须把官方文档里已经写明的边界讲清楚,避免被宣传口径带偏:
- 它只做静态分析。规则是基于静态可解析的
import语句匹配的,对动态require(variable)、运行时注入、通过字符串拼接绕过边界的引用,它看不到。官方在”如何添加其他 alt-js 语言”文档里也暗示了:要识别新语言,必须先有对应的解析插件,本质是 AST 层面的工作。 - 规则质量取决于团队共识。这是工具本身解决不了的问题——规则写得太松等于没写,太严(比如禁止一切跨目录引用)又会和真实的共享工具层冲突。Koala 点评里那句”规则设计需要团队对架构有清晰共识,否则容易流于形式”,说到了点子上。
- 大型仓库的图会糊。dot 输出是全量图,文件上千后可视化基本不可读,必须靠
--include-only、--do-not-follow等参数裁剪范围;这是官方 CLI 文档里提供的逃生口,而非产品缺陷。 - 版本迁移提示:README 明确标注 v12 及更老版本需要额外加
--config参数,新版已自动发现配置文件——升级旧项目时这是个容易踩的小坑。
五、适合谁,不适合谁
适合: 中大型 TS/JS monorepo 或有明确分层(前端/后端、UI/领域层、测试/生产)的团队,尤其是正在用 AI 编码助手批量生成代码、担心架构边界被快速突破的项目;想在 CI 里加一道”架构防腐”关卡的工程团队。
不适合: 几百个文件以下、分层简单的小项目——ESLint + 约定已经够用,多一层配置反而增加维护成本;以及重度依赖动态加载、运行时路由的项目(它的静态规则会漏掉真实依赖)。
六、它意味着什么
Dependency Cruiser 代表了一种正在回归的工程理念:架构纪律不该只存在于人脑和 PR 描述里,而应该像测试一样可执行、可阻断。在 AI 辅助编码让代码产量暴涨的今天,“谁能 import 谁”这类边界规则的自动化校验,和类型检查、单元测试一样,正在从加分项变成基础设施。它不新鲜(作者已维护多年),但在 AI 写代码的背景下重新变得有必要。