Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
初めてESLintプラグインにコントリビュートした話
Search
meijin
September 09, 2023
Technology
270
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
初めてESLintプラグインにコントリビュートした話
meijin
September 09, 2023
More Decks by meijin
See All by meijin
Technical Decisions and Reflections on "Test Maker" After Two Years of Development
texmeijin
1
120
弊社の「意識チョット低いアーキテクチャ」10選
texmeijin
5
26k
DDDを志して3年経ったら「DDDの皮を被ったクリーンアーキテクチャ」になった話【デブサミ2024夏】
texmeijin
4
4.4k
サービス黎明期にNuxt.js v2からNext.js移行を決めた理由と進め方
texmeijin
0
530
スタートアップCTOが個人開発で収益化・年13本記事発信・5件登壇を平行するための時間管理
texmeijin
4
1.2k
個人開発がおすすめな理由
texmeijin
3
1k
弊社の開発体験の良いところは?メンバーに訊いてみた!
texmeijin
0
490
先生と一緒に プロダクトを良くする アナリティクス機能の開発
texmeijin
0
130
ハードルが激低な社内勉強会を続けている話
texmeijin
0
6.3k
Other Decks in Technology
See All in Technology
LLMと共に進化するプロセスを目指して
ymatsuwitter
11
3k
もりもり新機能を一挙紹介! AgentCoreに入門して、AWS上にAIエージェントを構築しよう
minorun365
PRO
6
810
ブロックチェーン / Blockchain
ks91
PRO
0
110
生成 AI × MCP で切り拓く次世代 SRE!自律型運用への挑戦と開発者体験の進化
_awache
0
150
探して_入れて_作って_使う_Agent_Skills___LT.pdf
peintangos
2
160
「速く作る」から「正しく作る」へ ─ 生成AI時代の開発フロー改革の ロードマップと実行 ─
starfish719
0
7.6k
AI-DLCを活用した高品質・安全なAI駆動開発実践 / AI Driven Development with AI-DLC
yoshidashingo
0
140
Databricks 月刊サービスアップデート 2026年05月号
tyosi1212
0
210
Ruby::Boxでできること、Refinementsでできること
joker1007
3
390
地元にいないローカルオーガナイザーの立ち回り
uvb_76
1
470
新規ゲーム開発におけるAI駆動開発のリアル
202409e2
0
2.5k
はじめてのDatadog
kairim0
0
280
Featured
See All Featured
Taking LLMs out of the black box: A practical guide to human-in-the-loop distillation
inesmontani
PRO
3
2.3k
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
380
The SEO identity crisis: Don't let AI make you average
varn
0
480
Automating Front-end Workflow
addyosmani
1370
210k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
240
Optimising Largest Contentful Paint
csswizardry
37
3.7k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
Typedesign – Prime Four
hannesfritz
42
3.1k
It's Worth the Effort
3n
188
29k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Building Adaptive Systems
keathley
44
3k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
1
150
Transcript
初めて ESLint プラグインに コントリビュートした話 〜ESLint ルール作成のすゝめ〜 @meijin_garden / 株式会社NoSchool CTO
お話する内容 1. ESLint のルール設定にこだわる意義 2. 関わっているプロダクトで見つけた課題 3. OSS にコントリビュートした内容 対象読者
ESLint を使ったことはあるけど、使う意義があまりわかっていない方 ESLint のルールを自前で実装するイメージが湧いていない方 OSS コントリビュートのハードルが高いと思っている方
自己紹介 名人 Twitter(X): 名人|マナリンクCTO Zenn: https://zenn.dev/texmeijin 株式会社NoSchool CTO オンライン家庭教師マナリンク(https://manalink.jp/) 個人開発
テストメーカー(https://test-maker.app/) 好きな言語はTypeScript 、好きなHTTP ヘッダーはContent-Disposition 趣味 将棋☗、カメラ📸、ラム酒🥃、個人開発💻、筋トレ💪、高校野球観戦⚾
ESLint のルール設定にこだわると嬉しいこと
簡単な例え話 〜あるところに、うっかりデバッグ用のconsole.log を含んで提出されたPull Request に怒る人がいました〜
課題を「人」の問題と「仕組み」の問題に切り分け
仕組みで防げることは仕組みで防ぐ 注意する、とか気をつける、といった属人的な方針をネクストアクションにするのは最後の手段にする レビュワーが頑張る、も同じ 人間は(自分も含め)誰でもミスをする、忘れてしまう可能性がある IDE 、git hooks 、CI などを使ってチェックを自動化する 意外とできることは多い
ミスを防ぐといった後ろ向きなことだけではなく、ベストプラクティスを誰でも守れるようにする、とい った前向きな仕組み化も考えられる
ESLint でできること console.log の入れっぱなしなどイージーミスを防ぐ https://eslint.org/docs/latest/rules/no-console いわゆる書き方の好みの問題をPrj 内で統一する https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md 見やすくするが、手作業するかしないかが人によって分かれるやつ https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/order.md
社内で決めたアーキテクチャを統一する https://www.npmjs.com/package/eslint-plugin-strict-dependencies これが本スライドで話す主題です
コントリビュートしたESLint プラグイン eslint-plugin-strict-dependencies
どんなプラグイン(だった)か あるモジュールから別のモジュールをimport できる・できないのルールを規定する .eslintrc.js に設定を書き、破られていたらエラー扱いとする husky/lint-staged やCI で強制できる 利用例1 :プロダクトで決めたアーキテクチャの徹底
src/components/page は src/pages からのみ src/components/features は src/pages からのみ呼べる src/components/ui は src/components/page 、 src/components/features からのみ呼べる 利用例2 :外部ライブラリに対する腐敗防止層利用の徹底 MUI のコンポーネントは src/components/ui からのみ呼べる @sentry/react は src/libs/sentry.ts からのみ呼べる react-icons は src/components/ui/icons からのみ呼べる ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` `
設定例(※ 旧eslintrc 形式) module.exports = { plugins: ['strict-dependencies'], rules: {
'strict-dependencies/strict-dependencies': [ 'error', [ { "module": "src/components/ui", "allowReferenceFrom": ["src/components/page"], "allowSameModule": true }, { "module": "next/router", "allowReferenceFrom": ["src/libs/router.ts"], "allowSameModule": false }, ] ], }, }
利用シーンと、そこで見つけた課題 背景 弊チームでもさっそくアーキテクチャの徹底と、外部ライブラリの利用制限で用いた あるとき、メンバーが react.Suspense のラッパーを作ってくれた なので、 Suspense を直接呼ぶのではなく作ったラッパーを使うように徹底したい 気がついたこと
「 react の中の Suspense のみ利用範囲を制限したい」ケースには対応できない これまで通り設定すると、 react からのimport が全部NG になってしまう ` ` ` ` ` ` ` ` ` ` { "module": "react", "allowReferenceFrom": ["src/libs/suspense.ts"], "allowSameModule": false },
ではどうするか
import するメンバも指定できる機能を追加しよう! import A from B に対して「B から A を
import しているとき」というより細かな条件を指定できるように イメージ ` ` { "module": "react", "targetMembers": ["Suspense"], "allowReferenceFrom": ["src/libs/suspense.ts"], "allowSameModule": false },
なんか実装できそう 既存の実装を理解したら、 【 import 文において、 import 対象のモジュール名を取得する方法と、対象ファイル名を取得する方法】 がわかるはずなので、もう少し応用してimport するメンバー名を取得する方法を考えればよさそう。
機能追加するときは(一旦)ここだけ見る https://github.com/knowledge-work/eslint-plugin-strict- dependencies/blob/9e4064539a5b571efa7e8ea4c9f84a2f7f1c0926/strict-dependencies/index.js#L19 module.exports = { meta: { // meta
情報なので機能理解にあたってはスルー }, create: (context) => { // ここに色々書いてあるのも一旦スルー // ここでreturn されたものがプラグインの動作を決めるのでまずはここで全体理解 return { ImportDeclaration: checkImport, } }, }
ざっくり解説 ImportDeclaration とは AST( 後述) におけるimport 文のこと 例: import A
from B ImportDeclaration: checkImport と指定すると ESLint プログラムがimport 文を見つけたら、 checkImport 関数を実行するようになる 個人的には脳内で「 onImportDeclarationAppeared: checkImport 」といった風に読み替えて読ん でいて、イベントハンドラをプラグインを通して登録していると考えるとしっくりきています return { ImportDeclaration: checkImport, } ` ` ` ` ` ` ` ` ` `
AST(Abstract Syntax Tree) とは こちらで規定されている:https://github.com/estree/estree ※JavaScript に限らずどの言語にもある一般的な概念 AST は以下のようなツールで見れる https://astexplorer.net/
https://ts-ast-viewer.com/ 従って、import 文のことを ImportDeclaration と呼ぶのは予約語です ` `
覚えておくこと 全体的に よほどのことがない限り、AST について丸暗記したり徹底理解する必要はない 個人的には「まあ、プログラムをプログラムが解析したり変換するなら、プログラムはただの文字列 なので、プログラムが操作可能な形式に変換しないとダメやんな〜」くらいに思っておく ImportDeclaration: checkImport における checkImport
関数について 前述の通り、import 文が見つかったときにそのimport 文に対して実行する関数 第1 引数にAST でパースされた ImportDeclaration 型のオブジェクトが渡される ImportDeclaration 型の詳細はAST Explorer などで見たりtypescript-eslint を見て把握する ` ` ` ` ` ` ` `
ImportDeclaration 型 https://github.com/typescript-eslint/typescript- eslint/blob/6ed0ca43b1fea58522f1135e224ddc3fe788b40c/packages/ast- spec/src/unions/ImportClause.ts#L5 ` ` import type {
ImportDefaultSpecifier } from '../special/ImportDefaultSpecifier/spec'; import type { ImportNamespaceSpecifier } from '../special/ImportNamespaceSpecifier/spec'; import type { ImportSpecifier } from '../special/ImportSpecifier/spec'; export type ImportClause = | ImportDefaultSpecifier | ImportNamespaceSpecifier | ImportSpecifier; export interface ImportSpecifier extends BaseNode { type: AST_NODE_TYPES.ImportSpecifier; local: Identifier; imported: Identifier; importKind: ImportKind; }
実装方針 前述の知識から、今回の目的の一つである「import 対象のメンバー名を取得する」方法は node.specifiers を使う ` ` // ここのnode はImportDeclaration
型 function checkImport(node) { // 〜中略〜 // specifiers にはImportDefaultSpecifier/ImportNamespaceSpecifier/ImportSpecifier 型があり、ImportSpecifier の 場合のみimported が存在する const importedModules = node.specifiers.filter(spec => 'imported' in spec).map(spec => spec.imported.name)
テストコードと動作確認 本プラグインはありがたいことにテストコードが用意されていたので、手元にClone して実装した後にデ グレがないか実行 ローカルでの動作確認 方法は複数あると思うが、 yarn や npm はローカルにClone
したモジュールをinstall することもできる ので、手元で改修後のプラグインをinstall して自社プロダクトにて動作確認した e.g. yarn add -D ../../../hoge/eslint-plugin-strict-dependencies ` ` ` ` ` `
Pull Request 提出〜マージまで 6 月 30 日:弊社メンバーからSuspense ラッパー実装の発案があり、それに伴ってプラグインへの機能追 加を思いつく 6
月 30 日:なんとなく動くやつができる 7 月 2 日:テストコードを書き、動作確認もできたのでPR を提出 https://github.com/knowledge-work/eslint-plugin-strict-dependencies/pull/12 和製OSS なので日本語で書けたのがありがたい 8 月 18 日:なんだかんだあってPull Request をマージしていただけた🎉 ※ 今回ESLint プラグインへのコントリビュートは初めてでしたが、ESLint プラグインの作り方自体は昨年か ら知ってはいました。なので機能追加したいときにすぐに動けたと思います。今すぐ解決したいIssue がな くても、ESLint プラグインの作り方をざっくり知っておくといつか使えるかもしれません
まとめ
まとめ ESLint のルール設定にこだわると嬉しいこと プログラミングで起きる問題は、人の問題と仕組みの問題に切り分けられる 仕組みの問題のうち、いくつかはESLint で解決できる ESLint プラグインを作る/ 機能追加するときは AST
の知識は必要だが、丸暗記する必要はない 既存の実装を読んで、どういうノードがあるか、どういうノードを取得すればいいかを理解する ESLint プラグインでできることを知っておくと、いつかタイミングが来たときに役に立つ
宣伝
「マナリンク」について オンライン家庭教師マナリンク(https://manalink.jp/) コロナ禍から増え始めた新しい教育の仕事である「オンライン家庭教師」を広めるスタートアップ 先生と保護者様のマッチングサイトと、指導開始後の宿題や指導料金の管理等のツールを提供しています
弊社の開発チームについて メンバー構成 全4 名(CTO 、フルスタック2 名、React Native エンジニア1 名) 【仕組みを憎んで人を憎まず】
毎月最大 3 営業日程度「仕組み化・自動化」に関する工数を使います 実績の一例 ローカル環境の色々なデータの自動生成・破棄コマンドの作成 PHPStan の導入と設定 SQL のSlow Query 検知やN+1 の自動テスト時の検知 Mock Service Worker の導入とテストコードへの統合 renovate によるライブラリバージョンアップの自動化 勉強会 1 年以上、週1 〜2 回の社内勉強会を続けています(※ 業務時間内) https://zenn.dev/manalink_dev/articles/manalink-study-meetup-history-front-and-network
募集内容 開発メンバーを随時募集しているのですが、いきなり面接等は敷居が高いと思うので 以下募集しています! 弊社の社内勉強会にゲスト参加✏️ 平日15 時〜15 時半頃 平日夜 弊社メンバーとレンタルジムを借りて合同筋トレ💪 弊社メンバーと秋葉原の国内最大級のボルダリング上で壁登り🧱
普通にカジュアル面談(オンライン30min ) バーにお酒🥃を飲みに行く(私はラム酒がおすすめなのでラム酒デビューしたい方布教させて)
ご清聴ありがとうございました この後の懇親会でぜひお話しましょう!