モノレポでの依存設定忘れを防ぐ
結論
eslint-plugin-importのimport/no-extraneous-dependenciesを使え。
以下、経緯。
概要
- 事の発端はこれ: gulp v4.0.2 を使っている環境で markuplint v3.3.0 を導入するとエラーになる
- Issueは https://github.com/markuplint/markuplint/issues/740
もっと早く気付けばよかったのだけど、
error: TypeError: debug_1.green.bold is not a function
という時点でansi-colorsを使った処理に関するところなので、そのパッケージまわりを見ればよかった。
越智さんが直接の依存パッケージをすべて洗ってくれていたので、その後の特定は早かった。ありがとうございます。
原因
結論から言うと、依存パッケージの設定し忘れ。
エラーが起こった箇所は@markuplint/rulesの一部のコードで起こっている。この@markuplint/rulesのdependenciesにansi-colorsを設定し忘れていただけだった。完全に凡ミス。恥ずかしいを通り越して情けない。
何故気づけなかったのか
-
@markuplint/rulesはMarkuplintのデフォルトのルールを集めたパッケージで単体で利用するものではない -
markuplint本体がenquirerというパッケージを採用していて、これがansi-colorsに依存していた -
@markuplint/rulesはモノレポでmarkuplintと一緒に管理・開発しているので、開発中はもちろん、CIのテストでもenquirer経由でansi-colorsが常にインストールされた状態なので、dependenciesにansi-colorsを設定していなくても問題なかった - さらに基本的な利用においても
markuplintは@markuplint/rulesに依存しているので、@markuplint/rulesとansi-colorsはほぼ必ず一緒にnode_modulesに入ることになる
ちなみにansi-colorsがどういうパッケージを経由して依存しているかは次のコマンドで確認できる
yarn list ansi-colors
gulpでは何が起きていたのか
問題が起こるgulp関連のパッケージはほとんどがplugin-errorを経由してansi-colorsのv1.0.1に依存している。Markuplintがenquirer経由で依存していたansi-colorsのバージョンはv4.1.1。
ツリーを作るとこんな感じ。
- dskd.jp
- └ gulp-*
- └ plugin-error
- └ ansi-colors@1.0.1
- └ plugin-error
- └ markuplint
- └ enquirer
- └ ansi-colors@4.1.1
- └ @markuplint/rules
- └ ansi-colors設定し忘れ
- └ enquirer
- └ gulp-*
ansi-colors設定し忘れている@markuplint/rulesは、import * from "ansi-colors"したときにansi-colors@1.0.1とansi-colors@4.1.1のどちらかをインポートする。どうやらここでインストールされた順番が関係してくるらしい。
越智さんの検証では、要するにansi-colors@1.0.1が先にインストールされているケースで問題が発生していた。@markuplint/rulesはansi-colors@1.0.1をインポートし、その結果v1とv4のAPIの違いからerror: TypeError: debug_1.green.bold is not a functionというランタイムエラーを引き起こしてしまったようだ。
@markuplint/rulesに"ansi-colors": "^4.1.3"を追加し、Canaryリリース(markuplint@3.0.0-dev.96+3b9f1720)をして試したところ正常に動作することを確認できた。ansi-colors@1.0.1を先にインストールしていたとしても、@markuplint/rulesがきちんとansi-colors@4.1.3を持つようになるので問題は解消された。実際に問題が解消されたnode_modulesのディレクトリを覗くと以下のように解消されている。
- node_moduules
- └ ansi-colors (1.0.1)
- └ enquirer
- └ node_modules
- └ ansi-colors (4.1.3) ※キャレット設定されているので設定バージョンより上がる
- └ node_modules
- └ @markuplint/rules
- └ node_modules
- └ ansi-colors (4.1.3)
- └ node_modules
どうやってこのミスを防ぐのか
原因がわかったところで、このミスを防ぐ方法を考えないといけない。依存パッケージによってはすぐに見つかるだろうけど、今回のは組み合わせが絶妙すぎたとも思う。gulp以外でもansi-colorsを依存にもつパッケージを組みわせてMarkuplintを利用している人もいると思うけど、おそらくansi-colorsがv1.xではなく偶然APIが一致していてエラーを起こしていないだけだろう。たまたま上手くいってる。このたまたまというのがプログラミングの世界では一番怖いんだよ。なんとかしたい。
ということで調べてみるとESLintのプラグインのeslint-plugin-importにimport/no-extraneous-dependenciesというルールがある。依存設定していなかったりdevDependenciesに設定しているものをインポートしようとすると警告するものだ。
eslint-plugin-importは他のルールを利用していて導入していたのに、このルールを有効にしていなかった…。なんてことだ。もったいないことしていたし、実際迷惑をかけてしまった。申し訳ない。
これを読んでESLintのルール周りに詳しい方がいたら、Markuplintのpackage.jsonを見てPRしてほしい。「これ導入/有効にしてないのかよ、ありえんのやけど」というやつがあったら是非よろしくおねがいします。
Discussion