LoginSignup
4
1

dependency-cruiserについて理解する

Last updated at Posted at 2023-09-30

概要
dependency-cruiser

「循環依存を検知」
「package privateな設計を守る」

このようなことを実現してくれるライブラリです!

スライド版はこちらです!

目次

  1. 背景 🖼️
  2. 使い方 🚧
    2.1 install
    2.2 configファイル作成
    2.3 依存関係グラフ作成
    2.4 依存関係の検証
  3. 実際にコマンド叩いてみる 🧪
    3.1 install
    3.2 configファイル作成
    3.3 依存関係グラフ作成
    3.4 依存関係の検証
  4. ユースケース 💡
    4.1 github-actionsで循環依存関係になっているものを検出して知らせる
    4.2 package private のような設計を作る
  5. 最後に 🚢
  6. 参考 ✨

1. 背景 🖼️

  • こちらのイベントでdependency-cruiserという言葉を聞いて気になったからです。

  • 業務でTypeScriptのプロジェクトを担当してます。
    exportするとプロジェクト全体にexportされてしまうため、依存関係を保護し続けるのは難しいです。そのため、思いも寄らぬ依存関係が発じたり、循環参照が発生したりする恐れがあります。
    dependency-cuiserはそんな時に有効だと知りました。

1. dependency-cruiserとは

image.png
https://raw.githubusercontent.com/sverweij/dependency-cruiser/main/doc/assets/sample-dot-output.png

これは、JavaScript、TypeScript、LiveScript、またはCoffeeScriptプロジェクトの依存関係を調べ、次のようにします。

  • 自分のルールと照らし合わせて検証する
  • テキスト、グラフィックででルール違反を報告する

以下はreactの依存関係グラフです。

2. 使い方 🚧

2.1 install

npm install --save-dev dependency-cruiser
# or
yarn add -D dependency-cruiser
pnpm add -D dependency-cruiser

2.2 configファイル作成

npx depcruise --init

環境を少し調べ、いくつか質問をし
プロジェクトに適応した dependency-cruiser.js 設定ファイルを作成するそうです

  • 循環する依存関係
  • package.jsonで見つからない依存関係
  • 孤児
  • dev-またはoptionalDependenciesに依存するプロダクションコードの検出
    等、ほとんどのプロジェクトで意味のあるルールを追加してくれます

2.3 依存関係グラフ作成

graphvizをinstallしてください

brew install graphviz

例)src ディレクトリ内の依存関係のグラフを作成する

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

2.4 依存関係の検証

例)src ディレクトリ

npx depcruise src

ルールと照らし合わせて検証し、違反があればESLintのような形式で表示します。
image.png
https://raw.githubusercontent.com/sverweij/dependency-cruiser/main/doc/assets/sample-err-output.png

3. 実際にコマンド叩いてみる 🧪

sampleリポジトリで動かしながら進めていきます。

3.1 install

# npm i --save-dev dependency-cruiser

3.2 configファイル作成

# npm npx depcruise --init

以下のようなことを聞かれました

# npx depcruise --init
✔ Where do your source files live? … src
✔ Do your test files live in a separate folder? … no
✔ Looks like you're using a 'tsconfig.json'. Use that? … yes
✔ Full path to your 'tsconfig.json › tsconfig.json
✔ Also regard TypeScript dependencies that exist only before compilation? … yes
✔ Looks like you're using webpack - specify a webpack config? … yes

  ✔ Successfully created '.dependency-cruiser.js'

srcディレクトリと同じ階層にconfigファイル(.dependency-cruiser.js)が作成されました。
image.png

3.3 依存関係グラフ作成

graphvizを使えるようにinstallします。

FROM node:lts-bullseye
RUN apt-get update && apt-get install -y graphviz
WORKDIR /app

docker-compose buildして以下を実行します。

# npx depcruise src --output-type dot | dot -T svg > output.svg

github上でsvgファイルを見ると以下のようになりました。
image.png

3.4 依存関係の検証

# npx depcruise src

✔ no dependency violations found (2 modules, 1 dependencies cruised)

依存関係の違反は見つかりませんでした。
srcディレクトリ配下にはファイルが2つ、依存関係が1つあります。
とのことでした。

依存関係の違反を起こしてみます(循環依存)

index.ts
import { sub } from "./calc";
console.log(sub(1, 1));

export const violation = "violation";
calc.ts
import { violation } from "./index";

export function sub(a: number, b: number) {
  console.log(violation);
  return a - b;
}

# npx depcruise src

  warn no-circular: src/calc.ts → 
      src/index.ts →
      src/calc.ts

✘ 1 dependency violations (0 errors, 1 warnings). 2 modules, 2 dependencies cruised.

依存関係グラフにも循環してるのが確認できます。
image.png

実際に処理を実行すると、violationの値はundefinedで表示されてます。

# node dist/content_scripts.js
undefined
0

エラーになるケースや、ならないケースはありますが、意図しない挙動をするということを確認できました。

依存関係の違反(循環依存)をwarnからerrorに変更し、validateします。

severity: 'warn',をseverity: 'error'にします。

.dependency-cruiser.js
/** @type {import('dependency-cruiser').IConfiguration} */
module.exports = {
  forbidden: [
    {
      name: 'no-circular',
      severity: 'error',
      comment:
        'This dependency is part of a circular relationship. You might want to revise ' +
        'your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ',
      from: {},
      to: {
        circular: true
      }
    },

この設定でvalidateします。
errorとして表示されました。

# npx depcruise src

  error no-circular: src/calc.ts → 
      src/index.ts →
      src/calc.ts

✘ 1 dependency violations (1 errors, 0 warnings). 2 modules, 2 dependencies cruised.

image.png
依存関係グラフも赤字で表示されました。

4. ユースケース 💡

4.1 github-actionsで循環依存関係になっているものを検出して知らせる

dependency-test.yml
name: Dependency Test CI

on:
  push:
    branches:
      - '*'
  pull_request:
    branches:
      - '*'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3

    - name: Use Node.js
      uses: actions/setup-node@v3
      with:
        node-version: 18

    - name: Install dependencies
      run: cd app && npm ci

    - name: Run dependency test
      run: cd app && npx depcruise src 

以下のようにfailになってくれました。
image.png

4.2 package private のような設計を作る

内部パッケージのような挙動を実現するルールを作成できます。

  • _から始まるディレクトリ内部のファイルは、同ディレクトリ内のファイルまたは、直上のディレクトリのファイルからのみimport可能
  • _から始まるファイルは同階層に置かれたファイルからのみimport可能

fromがimport(require)する側で、toがexportする側です。
pathがルールを適用するファイルパスで、pathNotが適用されないファイルパスです。

.dependency-cruiser.js
module.exports = {
  forbidden: [
    {
      name: `1. '_'から始まるディレクトリ内部のファイルは、同ディレクトリ内のファイルまたは、一つ上の階層のディレクトリのファイルからのみimport可能`,
      severity: 'error',
      from: { path: ['(.*)\\/.*\\.ts'], pathNot: ['.*\\.spec\\.ts$'] },
      to: {
        path: ['_\\w+\\/\\w+\\.ts$'],
        pathNot: ['$1\\/_\\w+\\/\\w+\\.ts$', '$1\\/\\w+\\.ts$'],
      },
    },
    {
      name: `2. '_'から始まるファイルは同階層に置かれたファイルからのみimport可能`,
      severity: 'error',
      from: { path: ['(.*)\\/.*\\.ts$'], pathNot: ['.*\\.spec\\.ts$'] },
      to: {
        path: ['.*\\/_\\w+.ts$'],
        pathNot: ['$1\\/_\\w+.ts$'],
      },
    },
]

例で以下のフォルダ構成で解説します。

src/
├── _package1/
│   ├── _package2/
│   │   ├── _private.ts 
│   │   └──calc.ts 
│   └── importer.ts
├── index.ts
└── exporter.ts
# npx depcruise src

  error 2. '_'から始まるファイルは同階層に置かれたファイルからのみimport可能: src/_package1/importer.ts → src/_package1/_package2/_private.ts
  error 1. '_'から始まるディレクトリ内部のファイルは、同ディレクトリ内のファイルまたは、一つ上の階層のディレクトリのファイルからのみimport可能: src/index.ts → src/_package1/_package2/calc.ts

✘ 2 dependency violations (2 errors, 0 warnings). 5 modules, 5 dependencies cruised.

以下画像が依存関係グラフで赤字がエラーです。

image.png

5. 最後に 🚢

  • 循環依存関係を自動的に検出してくれるのはありがたいなと思いました!!
    中々原因が掴めず苦労したことが過去ありましたので。。
  • private pacakgeな設計ができるので心置きなくモジュール管理できるようになるのも魅力的です。

6. 参考 ✨

他にも依存関係の管理に関する記事を書いています 💪

参考になる記事を書いてくださった皆様に感謝致します

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1