Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
初めてESLintプラグインにコントリビュートした話
Search
meijin
September 09, 2023
Technology
290
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
AI時代のコードレビューは人に向けるな、仕組みに向けろ
texmeijin
5
3.1k
Technical Decisions and Reflections on "Test Maker" After Two Years of Development
texmeijin
1
150
弊社の「意識チョット低いアーキテクチャ」10選
texmeijin
5
27k
DDDを志して3年経ったら「DDDの皮を被ったクリーンアーキテクチャ」になった話【デブサミ2024夏】
texmeijin
4
4.6k
サービス黎明期にNuxt.js v2からNext.js移行を決めた理由と進め方
texmeijin
0
580
スタートアップCTOが個人開発で収益化・年13本記事発信・5件登壇を平行するための時間管理
texmeijin
4
1.3k
個人開発がおすすめな理由
texmeijin
3
1.1k
弊社の開発体験の良いところは?メンバーに訊いてみた!
texmeijin
0
510
先生と一緒に プロダクトを良くする アナリティクス機能の開発
texmeijin
0
150
Other Decks in Technology
See All in Technology
MCPゲートウェイを作って運用してわかったこと — Agent時代の権限管理の現在地
mtpooh
6
2.1k
ボードゲームの遊び相手をFoundation Modelsで作る / iOSDC Japan 2026
genda
0
240
Account Factory for Terraformによる 標準化されたアカウント発行の自動化
pensuke628
0
120
Mastering Agentic Development: Harness Engineering for Effective Coding Agents
konippi
3
660
AI臭い文章とは何なのか
nasuvitz
36
75k
AI Native Platform Engineering 〜PlatformとAgileで“作る速さ”を“価値”へ〜
uya116
0
540
VS Code × GitHub Copilot での Fabric 開発
ryomaru0825
1
200
Oracle Cloud Infrastructure(OCI):Onboarding Session(はじめてのOCI/Oracle Supportご利⽤ガイド)
oracle4engineer
PRO
3
21k
行動するAIのためのオントロジー | DevRev — Encraft #26.pdf
dvrv_tknrszk
2
630
Codex概要
ymiya55
0
110
私の推しは「聞いてから進む」AIです -AI-DLCに一人でアプリを作らせた話
yama3133
0
120
【データ横丁主催】AI Agentがコンテキストを使って仕事をした後、何が残るのか― 組織の経験を次の判断に引き継ぐ「Agent Memory」
shisyu_gaku
2
300
Featured
See All Featured
[SF Ruby Conf 2025] Rails X
palkan
3
1.4k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
Between Models and Reality
mayunak
4
480
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
10k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.9k
From π to Pie charts
rasagy
1
380
How GitHub (no longer) Works
holman
316
150k
How to Build an AI Search Optimization Roadmap - Criteria and Steps to Take #SEOIRL
aleyda
1
2.2k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
490
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
510
Typedesign – Prime Four
hannesfritz
42
3.2k
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 ) バーにお酒🥃を飲みに行く(私はラム酒がおすすめなのでラム酒デビューしたい方布教させて)
ご清聴ありがとうございました この後の懇親会でぜひお話しましょう!