mcpField
S
setup-ts-deep-modules

setup-ts-deep-modules

github.com/leandrocfe開発4158 回インストール11 週間前に更新
リポジトリ

setup-ts-deep-modules について

TypeScriptリポジトリでdependency-cruiserを設定し、ディープモジュールの境界を適用するスキルです。公開するルートのエントリーポイント、非公開のサブフォルダー、循環チェック、テスト、文書を整えます。

主な機能

  1. 01dependency-cruiserの境界ルール設定
  2. 02外部からのインポートをエントリーポイントに限定
  3. 03パッケージ内部の相互インポートを許可
  4. 04依存関係の循環を検出
  5. 05深いインポートの失敗テストでルールを検証
  6. 06パッケージのひな型と境界文書を追加

活用シーン

  1. 01TypeScriptリポジトリでパッケージのカプセル化を適用
  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 のディープモジュールをセットアップする

このリポジトリの各パッケージをディープモジュールにします。つまり、小さなインターフェースの背後に多くの振る舞いを隠します。パッケージの公開面はエントリーポイント、すなわちパッケージのルートにあるファイルです。サブフォルダー内のものはすべて隠されます。このスキルでは、dependency-cruiser と、エントリーポイントだけを入口にするルールを導入し、そのルールが実際に機能することを確認します。

用語(ディープモジュール、インターフェース、シーム、深さ)については、/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).

公開面はパッケージのルートファイルです。特定の index.ts に限定されません。慣例として、実装は lib/ に、テストは tests/ に配置し、すべてのパッケージが同じ2つのフォルダー構成になるようにします。ただし、ルール自体は一般的なものです。どのサブフォルダーにあるものもすべて非公開なので、フォルダーを追加するために設定を拡張することはありません。

ルールは4つあり、すべて error です。

  1. エントリーポイントの境界 — パッケージの外部にあるコード(アプリケーションコードや別のパッケージ)は、そのパッケージのエントリーポイント(ルートファイル)だけをインポートできます。サブフォルダー内のものはインポートできません。
  2. パッケージ内の自由なインポート — パッケージ自身のファイル同士は自由にインポートできます。
  3. エントリーポイント経由でのテスト — <pkg>/tests/ 内のファイルは、あらゆるパッケージのエントリーポイントと、自身の tests/ 内のフィクスチャをインポートできます。ただし、どのパッケージのサブフォルダー内の実装も(自身のものも含めて)インポートできません。パッケージ間の統合テストは問題ありませんが、ディープインポートは禁止です。
  4. 循環依存なし — 依存関係の循環を禁止します。

バレルではなくエントリーポイント。 公開面はルートにあるすべてのファイルなので、パッケージはすべてを巨大な index.ts に集約する代わりに、複数の小さなエントリーポイント(index.ts、client.ts、server.ts)を公開できます。サブツリー全体を再エクスポートするバレルファイルは推奨されません。エントリーポイントは小さく保ち、実装はサブフォルダーに隠してください。

どのパッケージがどのパッケージに依存できるかというレイヤリングは、別の関心事です。設定内のコメントアウトされたスタブとして残し、このリポジトリで追加します。

手順

1. 環境を検出する
  • パッケージマネージャー — pnpm-lock.yaml があれば pnpm、yarn.lock があれば yarn、bun.lockb があれば bun、いずれもなければ npm です。以下のすべてのコマンドでそれを使います(pnpm/yarn/npm run/bunx)。
  • パッケージのルート — src/ があれば src/packages、なければ packages を使います。リポジトリにすでに明らかな別の慣例がある場合は、選択をユーザーに確認してください。
  • 既存の設定 — .dependency-cruiser.* ファイルがあるか確認します。存在する場合は上書きせず、4つのルールとオプションをマージして、追加した内容をユーザーに伝えてください。

完了条件: パッケージマネージャー、パッケージのルート、既存設定の有無がすべて判明していること。

2. dependency-cruiser をインストールする

検出したパッケージマネージャーを使って、dependency-cruiser を devDependency としてインストールします。

完了条件: dependency-cruiser が devDependencies に含まれていること。

3. 設定を書く

dependency-cruiser.config.cjs をリポジトリのルートに .dependency-cruiser.cjs としてコピーします。手順1で検出したルートに合わせて PACKAGES_ROOT を調整します。ルールはパスの深さに基づき、拡張子に依存しないため、ほかに変更する必要はありません。

完了条件: 正しい PACKAGES_ROOT を指定した .dependency-cruiser.cjs があり、4つの forbidden ルールがすべて含まれていること。

4. チェックに組み込む
  • depcruise <packages-root>(または depcruise src)を実行する lint:boundaries スクリプトを追加します。
  • リポジトリの包括的なコマンド、つまりすでに型チェックを実行しているコマンド(例:check/ci/validate スクリプト)に組み込みます。tsconfig は変更せず、パスエイリアスも追加しないでください。
  • 包括的なスクリプトがない場合は lint:boundaries を追加し、CI に含めるようユーザーに伝えてください。

完了条件: lint:boundaries が存在し、型チェックと同じコマンドの一部として実行されること。

5. サンプル package のスキャフォールドを作成する

テンプレートとしてコピーして使える <packages-root>/example/ をコミットします。

  • index.ts — エントリーポイントです。内部ファイルに処理を委譲する関数をエクスポートします(package が明確に 深い 構造になるよう、単なるパススルーにはしません)。
  • lib/impl.ts — サブフォルダー内の内部ファイルです。index.ts からインポートし、外部からは到達できないようにします。
  • tests/example.test.ts — ../index(エントリーポイント)のみをインポートし、公開関数に対するアサーションを行います。

これはコピーするか削除するための初期テンプレートであることをユーザーに伝えます。

完了条件: サンプル package が存在し、ルートのエントリーポイントを通じて動作を公開し、impl がサブフォルダー内に隠されていること。

6. ルールが機能することを証明する

これはスキル全体の完了条件です。違反があっても失敗しない設定には意味がありません。

  1. lint:boundaries を実行します。クリーンなサンプルでは成功する必要があります。
  2. tests/example.test.ts に一時的にディープインポートを追加します(例:import { thing } from "../lib/impl")。もう一度 lint:boundaries を実行します。tests-through-entrypoints で失敗する必要があります。
  3. ディープインポートを元に戻します。もう一度実行すると成功する必要があります。

完了条件: 成功、ディープインポートで失敗、再び成功することを確認していること。手順 2 で失敗しない場合、ルールが正しく有効になっていません。完了する前に修正してください。

7. 規約を文書化する

package を置くフォルダー(<packages-root>/README.md)に、管理対象の package と同じ階層に README.md を作成し、次の内容を説明します。src/packages/<name>/ のレイアウト(ルートにエントリーポイント、実装に lib/、テストに tests/ を使用)、"package のエントリーポイント(ルートにあるファイル)からのみインポートすること"、および lint:boundaries の実行方法です。barrel file は明示的に避けるよう説明してください。サブツリー全体を index から再エクスポートするのではなく、小さなエントリーポイントを複数公開します。コピー用スニペットには、4 つのルールをそれぞれ 1 段落で記載します。

次に、リポジトリのエージェント指示ファイルから、この README へのコンテキストポインターを追加します。CLAUDE.md があればそれを、なければ AGENTS.md を使用します(どちらも存在しない場合は AGENTS.md を作成します)。1 行で十分です。例:Packages は deep modules です — 追加やインポートの前に [src/packages/README.md](./src/packages/README.md) を参照してください。 これにより、エージェントが境界ルールにぶつかる前にその存在を把握できます。

完了条件: <packages-root>/README.md が存在し、barrel を避けるよう説明していて、リポジトリの CLAUDE.md/AGENTS.md からリンクされていること。

注意事項

  • 設定内のバックリファレンス $1(dependency-cruiser のグループマッチング)によって、package は自身の内部にはアクセスでき、外部の package からはアクセスできないようになります。これを package ごとの個別ルールに分割しないでください。
  • 公開と非公開は深さで決まります。package のルートにあるファイルはエントリーポイントで、サブフォルダー内のものはすべて非公開です。慣例的なサブフォルダーは実装用の lib/ とテスト用の tests/ ですが、ルールで固定されているわけではありません。どのサブフォルダーも非公開になるため、新しいフォルダーを追加しても設定を変更する必要はありません。エントリーポイントの追加は、barrel を使わずにルートファイルを 1 つ追加するだけです。
  • package はフラットです。ルート直下に子 package を 1 階層だけ置きます。package 内部のネストの深さに制限はありません。package の中に別の package を置くことはできません。
  • "type": "module" のリポジトリでも設定の module.exports が動作するよう、.js ではなく .cjs を使用します。

コメント