Slide 1

Slide 1 text

ESLint の独自ルール作成に チャレンジした話 株式会社 NoSchool meijin

Slide 2

Slide 2 text

目次 独自ルールを作ろうと思ったきっかけ 作って適用する手順 独自ルールの作りかた テストの書きかた 補足① TypeScript 対応 補足② 運用手段の候補 余談 勉強会の主催 まとめ

Slide 3

Slide 3 text

自己紹介 ニックネームは「名人」 Twitter: @Meijin_garden Web エンジニア6 年目 株式会社NoSchool CTO オンライン家庭教師マナリンク -> https://manalink.jp 好きな分野はWeb フロントエンド 最近はWYSIWYG ライブラリのSlate.js に感動した 趣味は将棋(アマチュア二段くらい)

Slide 4

Slide 4 text

独自ルールを作ろうと思ったきっかけ ESLint って気軽にコードの品質を統一できて便利やな〜 業務上で、「今後はこのライブラリは使わないで欲しい」とか「この便利関数作ったからみんな使ってね」とい ったローカルルールを、レビューではなくLint で縛れたらいいな〜 でも、ESLint のルールって字句解析の知識とかないと作るの大変そう… → ちょっと作ってみるか!

Slide 5

Slide 5 text

独自ルールの大まかな手順 今回は以下の手順で独自ルールを動作させることができた。 独自ルールを記述したJavaScript(or TS) ファイルを作成して、 rules ディレクトリに置く npm scripts で "lint": "eslint --rulesdir rules" のように記述して実行 ※ --rulesdir オプションはDeprecated なので、他の方法が有力(後述) https://eslint.org/docs/user-guide/command-line-interface

Slide 6

Slide 6 text

独自ルールの記述方法

Slide 7

Slide 7 text

本家のドキュメント 「Working with Rules 」 https://eslint.org/docs/developer-guide/working-with-rules 英語かつものすごく長く、(個人的には)理解に時間がかかるので、日本語の文献や巷のライブラリのコードを読み つつ進めるのが良いと思う。ある程度理解すると、公式ドキュメントが割と手にとるようにわかる(当社比)。 以後、これを分かっていれば公式ドキュメントの言っていることがなんとなくわかると思う知識をザッと書いていき ます。

Slide 8

Slide 8 text

ルールを記述する JS ファイルの構造 meta と create から構成される大きなオブジェクトをexport する 1 module.exports = { 2 meta: { 3 type: "suggestion", 4 // ... 5 }, 6 7 create(context) { 8 return { 9 VariableDeclaration(node) { 10 // ... 11 } 12 }; 13 14 } 15 };

Slide 9

Slide 9 text

meta その名の通りルールのメタ情報(fix できる?suggest はある?) を表現するオブジェクト ESLint v8 から必要となるプロパティが追加されたみたいなので、昔の日本語記事など一部参考にならないものがあ るかも。要注意。 https://eslint.org/docs/user-guide/migrating-to-8.0.0#rules-require-metahassuggestions-to-provide- suggestions 1 module.exports = { 2 meta: { 3 hasSuggestions: true 4 }, 5 create(context) { 6 // your rule 7 } 8 };

Slide 10

Slide 10 text

create create は context を受け取る関数で、AST のノードに対応してLint ルールを実装したオブジェクトを返す プログラム中のこの種類のノードに対しては、このチェックをする!という考えで実装する なのでLint に関係ないノードについては実装しなくてよい ここで疑問に思ったことが2 つ AST ってなに?AST のノードってなに? どうやってLint ルールを実装するか?

Slide 11

Slide 11 text

AST とはなにか ( 広義には) 文字列で表現されたプログラムから演算子や変数など、文法的に意味のある情報のみを取り出して、 木構造で表現したもの プログラムは、【プログラム全体-> 複数のクラス-> 複数の関数-> 複数の変数宣言や代入など】といったように木構造で表現できる 分解した各部品のことをノードという プログラムを木構造で表現すると、プログラムから扱いやすくなる ESLint などの「プログラムを意味的に解釈して何らかの動作をするもの」を実装しやすくなる( 他にもbabel とか、Prettier とか、 esbuild とかも多分そう)

Slide 12

Slide 12 text

AST を覗いてみる https://astexplorer.net/ で簡単な式のAST 表現を見てみると、文字列のコードが巨大なJSON として表現される ことがわかる。超絶雑に図解すると以下の感じ

Slide 13

Slide 13 text

create に Lint ルールを実装する 引数 context に含まれる、エラーや警告をレポートする report() 関数や、node から変数名を取得できる getDeclaredVariables() 関数を活用して、便利にルールを実装できる 1 // 変数名が _ から始まっていたらエラーになる独自ルールの例 2 create(context) { 3 return { 4 VariableDeclaration(node) { 5 context.getDeclaredVariables(node).forEach(variable => { 6 const name = variable.name; 7 if (name.charAt(0) === "_") { 8 context.report({ 9 node, 10 messageId: "unexpected", 11 data: { name } 12 }); 13 } 14 }); 15 } 16 }; 17 } VariableDeclaration が変数宣言ノードの種類名. context.getDeclaredVariables() で変数名を全取得. context.report() でLint 結果をレポート.

Slide 14

Slide 14 text

リファレンス リファレンスがあるので丸暗記をしなくてもOK context の仕様 https://eslint.org/docs/developer-guide/working-with-rules#the-context-object VariableDeclaration などのAST のノードの種類名 ESTree https://github.com/estree/estree/blob/master/es5.md @types/eslint https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/eslint/index.d.ts#L438

Slide 15

Slide 15 text

テストの書きかた 1 const rule = require("../no-underscore-prefix"), 2 RuleTester = require("eslint").RuleTester; 3 const ruleTester = new RuleTester(); 4 ruleTester.run("no-underscore-prefix", rule, { 5 valid: [ 6 "'hoge'", 7 // ... 8 { code: "const obj = { _name: 'hoge' };", env: { es6: true } }, 9 ], 10 invalid: [ 11 { 12 code: "var _hoge = 'hoge';", 13 errors: [{ messageId: "unexpected", data: { name: "_hoge" }, type: "VariableDeclaration", 14 }, 15 // ... 16 { code: "let x,_y = 'hoge';", env: { es6: true }, errors: [{ messageId: "unexpected", data: { 17 // ... 18 ] 19 }); RuleTester を使ってテストを実装. valid / invalid キーで正常系と異常系を書く. RuleTester のコンストラクタでオプションも色々 設定できるらしい.

Slide 16

Slide 16 text

補足① TypeScript 対応 @types/eslint を使う 以下の記事が詳しい(厳密にはESLint7 時代の記事なので少しだけ注意が必要) https://blog.sa2taka.com/post/custom-eslint-rule-with-typescript ESLint 自体はts を読み込んでくれないので、コンパイルフェーズを自前で組むことに注意

Slide 17

Slide 17 text

補足② 運用手段 冒頭で示した --rulesdir オプションはDeprecated あくまで外部プラグインとして、ローカルフォルダを読み込めるプラグインとしてのアプローチがいくつかある eslint-plugin-rulesdir https://github.com/not-an-aardvark/eslint-plugin-rulesdir eslintrc.js を使っている前提だが最も最新か? https://github.com/cletusw/eslint-plugin-local-rules 使い方の癖があるように見えるが、任意のeslintrc の形式で対応できそう

Slide 18

Slide 18 text

余談 勉強会の主催 今回ESLint の独自ルールを調べるにあたって、勉強会を企画してみました https://connpass.com/event/232064/ 2021/12/13 に開催し、共催の RyoKawamata さんと2 時間作業してなんとか形にできた(ありがとうございました) 勉強会駆動開発(Meetup Driven Development, MDD ?)よさそう 勉強会企画したい方、一緒に主催できるので声かけてくださいw

Slide 19

Slide 19 text

まとめ ESLint の独自ルールを書くために難しい知識は必要ない ドキュメントは丁寧だが、概念をつかむまで少々大変なのと、 @types/eslint などを見たほうが理解が早く 進むところがあるので、手を動かして学ぶのが一番早そう 簡単なルールだったら業務でも作れそうな気がした(まだ作ってない) 勉強会駆動開発おすすめ

Slide 20

Slide 20 text

告知

Slide 21

Slide 21 text

Twitter フォローしてね @Meijin_garden だいたいWeb 開発の話か将棋の話してます 絶賛エンジニア採用中です! オンライン家庭教師のスタートアップです! 2020 年リリースで、これから塾や家庭教師や通信教育 を置き換える教育スタイルを広げていきます 少しでも興味があればカジュアル面談歓迎なので、DM やMeety でお声がけください!!

Slide 22

Slide 22 text

ご清聴ありがとうございました