🐈

JavaScriptファイル間の依存をmermaidで可視化するために Ruby gem "js_dependency" を作りました

2022/09/24に公開

概要

JavaScriptのファイル修正を安心して行うためにgem js_dependency を作りました。

修正したファイルの参照・被参照をmermaid形式で可視化できるため、影響範囲調査が捗ります。

既存でもmadgeなどがありますが、VueのSingle File Component (.vue)に対応していなかったので作りました。

背景

毎回、プロジェクトのコードをいかに早く理解するかは重要です。規約のあるRailsのコードリーディングでは困ることはあまりありませんが、JavaScriptは毎回難儀しています。

JavaScriptのコードリーディングを容易にする1手法としてファイルの依存性を可視化が有効と考え、可視化ライブラリを調べた所、以下3つありました。

しかし、いずれもVueのSingle File Componentに対応していません。仕事で必要なのは、Vueなので、VueのSFCファイルでも可視化できるようにライブラリを作ることにしました。

環境

言語はRubyです。gem js_dependencyを作りました。

VueなのでJavaScriptで書いた方が楽だろう思っていましたが、書き始めると、静的に文字列(テキストファイル)を解析するだけだったので、手になじむRubyで書く事にしました。

使い方

junara/js_dependencyをご覧ください。以下抜粋して解説

インストール

普通に以下のかんじ。

gem install js_dependency
group :development, :test do
  gem "js_dependency"
end

下記のコマンドでversion番号が出力されれればインストール成功です。

js_dependency version
# => "X.X.X"

使用法

依存グラフを出力する(mermailフォーマット出力)

js_dependency -s ./src -t ./src/App.vue -o ./mermaid.txt -c 2 -p 2 -n 1
  • -s
    • アプリのJavaScriptのファイルが含まれるルートパス
      • -s ./src
  • -t
    • 依存関係を知りたいターゲットファイルパス
    • 半角スペース区切りで指定すると複数解析できます。
      • -t ./src/App.vue ./src/components/Sub.vue
  • -o
    • 未指定でも出力結果(mermailフォーマットテキスト)は標準出力に出力されますが、ファイルに保存したい場合は指定してください。
      • -o ./mermaid.txt
  • -c
    • ターゲットファイルの何代目の子孫まで表示するかの数値
      • -c 2
  • -p
    • ターゲットファイルの何代目の親まで表示するかの数値
      • -p 2
  • -n
    • ファイル名をどのぐらいの長さ(パス)で表示するか
      • -n 1

Orphan(どこからも依存されていない)ファイルを出力

どこからも依存されていないファイルを出力できます。
どこからも依存されていないということは、消し忘れたファイルである可能性が高いので、削除すべきファイルを見つけ出すために使えます。

js_dependency orphan -s ./src

その他option

  • -e

    • 解析から除外したいファイル名に含まれる文字列を指定
    • 半角スペース区切りで複数指定できます
      • -e hoge fuga puyo
  • -a

    • エイリアスパスを指定
      • -a @:./pages
    • @をつかってパスを指定しているときにつかってください。
  • 設定ファイル./.js_dependency.ymlの利用

    • 以下のファイルをコマンド実行箇所に作成し保存すると上述のoptionの入力を省略できます。
    • もし、両方入力した場合は、cliのoptionが優先されます。
src_path: ./src # Root folder
target_path: ./src/App.vue # Target file tha you want to analyze
target_paths: # Target files tha you want to analyze
  - ./src/App1.vue
  - ./src/App2.vue
child_analyze_level: 2 # Output level of child analyze
parent_analyze_level: 2 # Output level of parent analyze
name_level: 1 # Output name level
output_path: ./mermaid.txt # Output file path
alias_paths: # Alias path
  "@": ./pages # absolute path or relative path of src_path
excludes: # Exclude words that you want to skip from export.
  - excludeWord1
  - excludeWord2

GitHub Actionsとの連携

より使いやすくするために、プルリクエスト時に解析結果をcommentで追加できるGitHub Actionsを作成しました。readme by junara · Pull Request #1 · junara/test_js_dependency_gh_action

junara/pull_request_comment_of_javascript_dependency_graph

下記のように導入できます。

name: Add js_dependency analysis report in pull request comment
on:
  pull_request:

jobs:
  js_dependency:
    name: Export Mermaid by js_dependency
    runs-on: ubuntu-latest
    steps:
      - uses: junara/pull_request_comment_of_javascript_dependency_graph@main
        with:
          src_path: src
          child_analyze_level: 2
          parent_analyze_level: 2
          name_level: 2
          alias_paths: "@:./pages"
          exclude_output_names: "pages"
          github_token: ${{ secrets.GITHUB_TOKEN }}

解説

どうやって依存関係を見つけているの?

JavaScriptファイル(.vue含む)を静的に解析して必要な情報を抜き出しindexを作成します。
indexからターゲットファイルを検索して、ヒットした箇所をmermaidファイルフォーマットに出力します。

必要な情報の抜き出しは大きく分けて以下の2段階です。より具体的には、正規表現 を駆使しています。リンク先を見るとすっごく泥臭いのがわかるかと思います。

indexは、上述で抜き出した内容をHashとしたものです。
Hashはファイル名をキー、importで指定されたファイルパスの配列をvalueとしたHashです。
詳しくは以下のspecを参照ください。

js_dependency/index_creator_spec.rb at 91935fb8d387922ba0cc4af92b0ca35f7534194d · junara/js_dependency

マーメイドテキストフォーマットは、 Flowchart ここらへんをみながら書きました。

参考

Discussion