トップ/記事一覧

eslint-plugin-strict-dependencies のコードリーディング

📆2022/10/11(最終更新日:2022/10/13)🔖 ESLint

背景・やったこと

React+TSプロジェクトで便利だったLint/Format設定紹介 の依存関係のチェックの章を読んで、便利だなと思った反面、どういう仕組みで動かしているのか全く想像も付かなかったのでインプットしようと思ったのがきっかけ。自分たちのプロダクトにも導入したいルールだが導入するにしてもどういう仕組みで動いているのかきちんと理解できていないとあまり良くないし、自前で書けるなら自前で書いた方が依存を減らせて良いかなと思ってインプットをした。

メモを取りながらインプットしたので、そのままインプットの時系列順に書いていく。

Eslint の plugin についてのインプット

  • plugin を使うとルールを追加できるっぽい。
  • プラグインから読み込んだルールに関しては プラグイン名/ルール名 という記述方式っぽい。 plugin1/rule1 はどういう仕組みで動くのかは不明。
  • JavaScript

    { "plugins": [ "plugin1" ], "rules": { "eqeqeq": "off", "curly": "error", "quotes": ["error", "double"], "plugin1/rule1": "error" } }
  • https://eslint.org/docs/latest/user-guide/configuring/rules
  • eslint-plugin-strict-dependencies について

  • モジュールの依存関係のルールを追加してくれるプラグイン。
  • 使い方は上記と同様 plugin として読み込んで、ルールを追加するだけ。
  • JavaScript

    "rules": { "strict-dependencies/strict-dependencies": [ "error", [ { "module": "src/components/page", "allowReferenceFrom": ["src/pages"], // components/page can't import other components/page "allowSameModule": false }, ] ] }
  • https://www.npmjs.com/package/eslint-plugin-strict-dependencies
  • ルールは module allowReferenceFrom allowSameModule の3つのプロパティを受け取るインターフェイスになっている。 module はルールを設定するモジュール(glob 対応)、 allowReferenceFrom はモジュールの読み込みを許可するディレクトリ(ホワイトリスト形式、glob 対応)、 allowSameModule はどういつディレクトリ内の他ファイルから読み込みを許可するかの真偽値。
  • eslint-plugin-strict-dependencies のコードリーディング

    改めて整理すると、イシューとしては「Linter ルールをこのプラグインを使わず書けるかどうか」なるべく外部モジュールには依存しない方が良いという方針があるという前提。外部のプラグイン使わずに設定できるものであるならば抽象化されたものを使わず自前で記述してしまおうという考えでコードリーディングしていく。

  • プラグイン本体のファイル:https://github.com/knowledge-work/eslint-plugin-strict-dependencies/blob/main/strict-dependencies/index.js
  • context からユーザー側(.eslintrc)の設定値を読み込むことができるっぽい(たぶん eslint の rule の仕様だけど未調査)
  • create が本体っぽい、返り値は { ImportDeclaration: checkImport } というオブジェクト、 ImportDeclaration は AST の import 部分のチェック(仮説)、 checkImport を自前の関数としている
  • context.report(node, import '${importPath}' is not allowed from ${relativeFilePath}.) report メソッドでエラー文的なのを定義できるっぽい。これがエラーもしくは警告文として出力される(仮説:eslint の rule の仕様だろうけど詳しくはドキュメント読んでない。たぶん合ってるので特に深堀りはしない)
  • ImportDeclaration 周りと、create の返り値の定義について eslint のルールの仕様をインプットしないとよく分からない。
  • eslint のルール(create 周り)の仕様のインプット

  • ここを読む。 https://eslint.org/docs/latest/developer-guide/working-with-rules
  • create の返却値について。3パターンあるらしい、 [node 名][node 名]:exit[イベント名] の3パターン
  • AST の解析中のどのタイミングで関数を発火させるかが異なるっぽいが、それぞれの違いはよく分からなかった(up と down の違いは知識不足で理解できずだった)
  • create (function) returns an object with methods that ESLint calls to “visit” nodes while traversing the abstract syntax tree (AST as defined by ESTree) of JavaScript code:
  • if a key is a node type or a selector, ESLint calls that visitor function while going down the tree
  • if a key is a node type or a selector plus :exit, ESLint calls that visitor function while going up the tree
  • if a key is an event name, ESLint calls that handler function for code path analysis
  • イベント名の場合は引数にイベント名も追加されるっぽい。今回のコードリーディング対象は [node 名] パターンなので関係ない。
  • 今回は [node 名] パターンなので、引数は1つで node が入ってきてるはず。
  • 改めて、eslint-plugin-strict-dependencies のコードリーディング

    少しインプットできたので戻る。

  • 今回で言う checkImport 関数は ImportDeclaration (node 名)がキーなので、引数で node を受け取っている
  • node には source があり、その中に value がある(*1)value から import 元の文字列を取得できる。
  • import 元の文字列と、ユーザーが .eslintrc で指定した文字列(glob ルールも可)を比較して、ルールを満たすか満たさないかを判定してくれていた。
  • ようやく「読める……読めるぞ……」になった。

    結論

    結局自前で書くにしてもルールファイルは書かないといけないし、glob パターン対応周りとかもありがたいので、プラグイン使わせてもらった方が良さそうな気がする。便利なものを作ってくれてありがとうございますのお気持ち。

    🐈

    (*1) AST Explorer なるサイトがあったので、そこに import 文を追記して試してみて ImportDeclaration という文字列を発見。AST に初めて触れた。

    https://astexplorer.net/