LoginSignup
61
37

More than 5 years have passed since last update.

dependency-cruiserを使って依存関係を検証し潜在的なバグを潰す

Last updated at Posted at 2018-09-04

sverweij/dependency-cruiser: Validate and visualize dependencies. With your rules. JavaScript, TypeScript, CoffeeScript. ES6, CommonJS, AMD.

このツールが何を行うかというと、依存関連を検証してくれる。
例えばフロントエンドのコードの規模が20万行だとかそんな規模だとすると、使われていないファイルだとか相互依存になっているファイルが見つかることがままある。
このツールを使うことでそれらの発見が容易になる。

yarn add --dev dependency-cruiser

設定ファイルが以下のコマンドで生成できる。

npx depcruise --init

デフォルトでは以下のようなファイルが生成される。

{
  "forbidden": [
    {
      "name": "not-to-test",
      "comment": "Don't allow dependencies from outside the test folder to test",
      "severity": "error",
      "from": {
        "pathNot": "^(test|spec)"
      },
      "to": {
        "path": "^(test|spec)"
      }
    },
    {
      "name": "not-to-spec",
      "comment": "Don't allow dependencies to (typescript/ javascript/ coffeescript) spec files",
      "severity": "error",
      "from": {},
      "to": {
        "path": "\\.spec\\.(js|ts|ls|coffee|litcoffee|coffee\\.md)$"
      }
    },
    {
      "name": "no-circular",
      "severity": "warn",
      "comment": "Warn in case there's circular dependencies",
      "from": {},
      "to": {
        "circular": true
      }
    },
    {
      "name": "no-orphans",
      "severity": "info",
      "comment": "Inform in case there's orphans hiding in the code base",
      "from": {
        "orphan": true,
        "pathNot": "\\.d\\.ts$"
      },
      "to": {}
    },
    {
      "name": "no-deprecated-core",
      "comment": "Warn about dependencies on deprecated core modules.",
      "severity": "warn",
      "from": {},
      "to": {
        "dependencyTypes": [
          "core"
        ],
        "path": "^(punycode|domain|constants|sys|_linklist)$"
      }
    },
    {
      "name": "no-deprecated-npm",
      "comment": "These npm modules are deprecated - find an alternative.",
      "severity": "warn",
      "from": {},
      "to": {
        "dependencyTypes": [
          "deprecated"
        ]
      }
    },
    {
      "name": "not-to-unresolvable",
      "comment": "Don't allow dependencies on modules dependency-cruiser can't resolve to files on disk (which probably means they don't exist)",
      "severity": "error",
      "from": {},
      "to": {
        "couldNotResolve": true
      }
    },
    {
      "name": "not-to-dev-dep",
      "severity": "error",
      "comment": "Don't allow dependencies from src/app/lib to a development only package",
      "from": {
        "path": "^(src|app|lib)",
        "pathNot": "\\.spec\\.(js|ts|ls|coffee|litcoffee|coffee\\.md)$"
      },
      "to": {
        "dependencyTypes": [
          "npm-dev"
        ]
      }
    },
    {
      "name": "no-non-package-json",
      "severity": "error",
      "comment": "Don't allow dependencies to packages not in package.json (except from within node_modules)",
      "from": {
        "pathNot": "^node_modules"
      },
      "to": {
        "dependencyTypes": [
          "unknown",
          "undetermined",
          "npm-no-pkg",
          "npm-unknown"
        ]
      }
    },
    {
      "name": "optional-deps-used",
      "severity": "info",
      "comment": "nothing serious - but just check you have some serious try/ catches around the import/ requires of these",
      "from": {},
      "to": {
        "dependencyTypes": [
          "npm-optional"
        ]
      }
    },
    {
      "name": "peer-deps-used",
      "comment": "Warn about the use of a peer dependency (peer dependencies are deprecated with the advent of npm 3 - and probably gone with version 4).",
      "severity": "warn",
      "from": {},
      "to": {
        "dependencyTypes": [
          "npm-peer"
        ]
      }
    },
    {
      "name": "no-duplicate-dep-types",
      "comment": "Warn if a dependency you're actually using occurs in your package.json more than once (technically: has more than one dependency type)",
      "severity": "warn",
      "from": {},
      "to": {
        "moreThanOneDependencyType": true
      }
    }
  ],
  "options": {
    "doNotFollow": "node_modules"
  }
}

とりあえず、相互に依存したものをチェックするのだけやってみる。
dependency-cruiser.jsonを以下のように書き換える。
no-circularだけ有効化した形だ。

{
  "name": "no-circular",
  "severity": "error",
  "comment": "Warn in case there's circular dependencies",
  "from": {},
  "to": {
    "circular": true
  }
}

そして大袈裟に書くが以下のようなtsを用意する。
デフォルトでTypeScriptをサポートしているので便利。

src/main.ts

import { sub } from './sub'

export function main() {
  return 'main'
}

sub()

src/sub.ts

import { main } from './main'

export function sub() {
  return 'sub'
}

main()

実行

npx depcruise -v .dependency-cruiser.json src

  error no-circular: src/main.ts → src/sub.ts
  error no-circular: src/sub.ts → src/main.ts

✖ 2 dependency violations (2 errors, 0 warnings). 2 modules cruised.

スクリーンショット 2018-09-04 20.42.54.png

このように相互依存が明らかになって、潜在的なバグを減らすことができる。

またno-orphansを有効にすればどこからも参照されていない独立したファイルを見つけることも簡単だ。

まとめ

小中規模であれば特に効果を発揮しないかも知れないが、巨大なプロジェクトに出会ったときに非常に役に立つツールだと思う。

61
37
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
61
37