JavaScriptファイル間の依存をmermaidで可視化するために Ruby gem "js_dependency" を作りました
概要
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
- アプリのJavaScriptのファイルが含まれるルートパス
-
-t
- 依存関係を知りたいターゲットファイルパス
- 半角スペース区切りで指定すると複数解析できます。
-t ./src/App.vue ./src/components/Sub.vue
-
-o
- 未指定でも出力結果(mermailフォーマットテキスト)は標準出力に出力されますが、ファイルに保存したい場合は指定してください。
-o ./mermaid.txt
- 未指定でも出力結果(mermailフォーマットテキスト)は標準出力に出力されますが、ファイルに保存したい場合は指定してください。
-
-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段階です。より具体的には、正規表現 を駆使しています。リンク先を見るとすっごく泥臭いのがわかるかと思います。
-
.vue
からscript
タグ箇所を抜き出す - 上述のテキストからファイルとファイル内の
import
で指定されているファイルパスの対応を保存する
indexは、上述で抜き出した内容をHashとしたものです。
Hashはファイル名をキー、importで指定されたファイルパスの配列をvalueとしたHashです。
詳しくは以下のspecを参照ください。
マーメイドテキストフォーマットは、 Flowchart ここらへんをみながら書きました。
参考
- 既存の技術(参考とした技術)
Discussion