mcpField
S
setup-ts-deep-modules

setup-ts-deep-modules

github.com/leandrocfe开发4158 次安装11 周前更新
代码仓库

关于 setup-ts-deep-modules

为 TypeScript 仓库配置 dependency-cruiser,以强制执行深模块边界。它设置公开根目录入口、私有子目录、循环依赖检查、测试与文档。

主要功能

  1. 01配置 dependency-cruiser 边界规则
  2. 02限制外部导入仅使用包入口
  3. 03允许包内文件相互导入
  4. 04检测依赖循环
  5. 05用失败的深层导入测试验证规则
  6. 06添加包脚手架与边界文档

适用场景

  1. 01在 TypeScript 仓库强制包封装
  2. 02阻止深层导入与依赖循环
  3. 03建立可复用的包布局与检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/leandrocfe/skills --skill setup-ts-deep-modules

安装后在 Claude Code 中输入/setup-ts-deep-modules确认已加载。

附带文件agents/openai.yamldependency-cruiser.config.cjs

详细介绍

设置 TS 深层模块

让此仓库中的每个 package 都成为一个深层模块(deep module):在小巧的接口背后封装大量行为。package 的公共表面是它的 entry points——package 根目录中的文件;子文件夹中的所有内容都隐藏起来。此 skill 会安装 dependency-cruiser 并配置规则,让 entry points 成为唯一的入口,然后验证这些规则确实生效。

关于相关术语(deep module、interface、seam、depth),请运行 skill /codebase-design,并在整个过程中使用其中的语言。

此配置强制采用的格式

src/packages/
  <name>/
    index.ts        ← um entry point (público). Importe este de fora.
    client.ts       ← outro entry point. Packages podem expor VÁRIOS.
    lib/            ← implementação: escondida de fora, livre pra importar entre si.
    tests/          ← testes co-localizados + fixtures (uma subpasta, então privada).

公共表面是 package 的根目录文件,而不是指定的 index.ts。按照惯例,实现在 lib/ 中,测试在 tests/ 中,这样每个 package 都有相同的双文件夹格式。不过,规则本身是通用的:任何子文件夹中的任何内容都是私有的,因此你绝不会为了添加文件夹而扩展配置。

四条规则,全部设为 error:

  1. Entry-point boundary — package 外部的代码(应用代码或其他 package)只能导入该 package 的 entry points(其根目录文件),不能导入其子文件夹中的任何内容。
  2. Intra-package freedom — package 自身的文件可以自由相互导入。
  3. Tests through the entry points — <pkg>/tests/ 下的文件可以导入任何 package 的 entry points,以及自身 tests/ 中的 fixtures,但绝不能导入任何 package 子文件夹中的内部内容(包括自身的)。跨 package 的集成测试没问题;deep imports 不行。
  4. No cycles — 不允许依赖循环。

Entry points,而不是 barrel。 因为公共表面是所有根目录文件,package 可以公开多个小型 entry points(index.ts、client.ts、server.ts),而不是将所有内容都汇聚到一个庞大的 index.ts 中。不建议使用重新导出整个子树的 barrel 文件——保持 entry points 精简,并将实现在子文件夹中隐藏起来。

分层(哪些 packages 可以依赖哪些 packages)是另一项关注点,在配置中以注释 stub 的形式保留,供此仓库后续填写。

步骤

1. 检测环境
  • Package manager — pnpm-lock.yaml → pnpm,yarn.lock → yarn,bun.lockb → bun,否则使用 npm。以下所有命令都使用检测到的 package manager(pnpm/yarn/npm run/bunx)。
  • Packages 根目录 — 如果存在 src/,则使用 src/packages,否则使用 packages。如果仓库已有其他明显约定,请与用户确认选择。
  • 现有配置 — 检查是否存在 .dependency-cruiser.* 文件。如果存在,不要覆盖:合并四条规则和相关选项,并告知用户添加了哪些内容。

完成条件: 已知 package manager、packages 根目录以及现有配置状态。

2. 安装 dependency-cruiser

使用检测到的 package manager,将 dependency-cruiser 安装为 devDependency。

完成条件: dependency-cruiser 已列入 devDependencies。

3. 编写配置

将 dependency-cruiser.config.cjs 复制到仓库根目录,命名为 .dependency-cruiser.cjs。将 PACKAGES_ROOT 调整为步骤 1 中检测到的根目录。规则基于路径深度,与扩展名无关,因此不需要其他调整。

完成条件: .dependency-cruiser.cjs 已存在,且 PACKAGES_ROOT 正确,四条 forbidden 规则均已配置。

4. 加入检查
  • 添加 lint:boundaries 脚本:depcruise <packages-root>(或 depcruise src)。
  • 将其加入仓库的统一检查命令,也就是已经运行 typecheck 的命令(例如 check / ci / validate 脚本)。不要修改 tsconfig 或添加 path aliases。
  • 如果没有统一检查脚本,则添加 lint:boundaries,并告知用户将其加入 CI。

完成条件: lint:boundaries 已存在,且会作为与 typecheck 相同命令的一部分运行。

5. 搭建示例 package 的脚手架

创建一个已提交到仓库的 <packages-root>/example/,作为可复制的模板:

  • index.ts — 一个入口点。导出一个委托给内部文件的函数(这样 package 就能明显体现出 深模块 的特性,而不是简单转发)。
  • lib/impl.ts — 一个位于子文件夹中的内部文件,由 index.ts 导入,外部无法访问。
  • tests/example.test.ts — 只导入 ../index(一个入口点),并对公开函数进行断言。

告诉用户这是一个可复制或删除的初始模板。

完成标准: 示例 package 已存在,通过根入口点公开其行为,并将 impl 隐藏在子文件夹中。

6. 验证规则确实生效

这是整个 skill 的完成标准——无法在违规时失败的配置毫无价值。

  1. 运行 lint:boundaries。干净的示例应当通过。
  2. 临时在 tests/example.test.ts 中添加一个深层导入(例如:import { thing } from "../lib/impl")。再次运行 lint:boundaries——应当因 tests-through-entrypoints 而失败。
  3. 撤销深层导入。再次运行——应当通过。

完成标准: 你已观察到先通过,再因深层导入而失败,最后再次通过。如果第 2 步没有失败,说明规则未正确启用——请先修复,再结束。

7. 记录约定

在packages 文件夹中编写一个 README.md(<packages-root>/README.md)——放在它所约束的 packages 旁边,涵盖以下内容:src/packages/<name>/ 布局(根目录下的入口点、用于实现的 lib/、用于测试的 tests/)、“只通过 package 的入口点(其根目录下的文件)导入”,以及如何运行 lint:boundaries。明确不建议使用 barrel 文件——应公开多个小型入口点,而不是通过一个 index 重新导出整个子树。在可复制模板中保留这四条规则,每条写成一个段落。

接着,从仓库的 Agent 指令文件中添加一个上下文指针指向该文件——若存在 CLAUDE.md,则使用它;否则使用 AGENTS.md(如果都不存在,则创建 AGENTS.md)。一行即可,例如:Packages 是深模块——添加或导入 package 前,请参阅 [src/packages/README.md](./src/packages/README.md)。 这样 Agent 就能发现边界规则,而不是意外触犯它。

完成标准: <packages-root>/README.md 已存在且明确不建议使用 barrel;仓库的 CLAUDE.md/AGENTS.md 链接到该文件。

备注

  • 配置中的反向引用 $1(dependency-cruiser 的分组匹配)让 package 可以访问自身内部文件,而外部无法访问——不要把它拆成针对每个 package 的独立规则。
  • 公开与私有由深度决定:package 根目录下的文件是入口点;子文件夹中的任何内容都是私有的。惯用子文件夹是 lib/(实现)和 tests/,但规则不会将其硬编码——任何子文件夹都是私有的,因此新增文件夹永远不需要修改配置。添加入口点只需在根目录中添加文件——无需使用 barrel。
  • Packages 采用扁平结构:根目录下只有一层直接子项。package 的内部文件可以按需任意嵌套;package 内不能包含另一个 package。
  • 使用 .cjs(而不是 .js),这样配置中的 module.exports 在 "type": "module" 的仓库中也能正常工作。

评论

支持 Markdown