Upgrade to Pro — share decks privately, control downloads, hide ads and more …

OSSのUIライブラリでESLintのカスタムルールを作っている話

Avatar for chida0909 chida0909
June 10, 2026
410

 OSSのUIライブラリでESLintのカスタムルールを作っている話

Avatar for chida0909

chida0909

June 10, 2026

Transcript

  1. © Findy Inc. TSKaigi Night talks ~after conference~/ 2026-06-10 OSS

    の UI ライブラリで ESLint のカスタムルールを 作っている話 ファインディ株式会社 千⽥ 孝明 1
  2. © Findy Inc. 2 ⾃⼰紹介 • 千⽥ 孝明(Takaaki Chida) •

    ファインディ株式会社 / Web エンジニア • Yamada UI のメンテナ • 1児の⽗
  3. © Findy Inc. 3 Yamada UI とは ⽇本発の React UI

    ライブラリ https://yamada-ui.com
  4. © Findy Inc. 4 課題 コンポーネントの誤選択 • 専⽤コンポーネントがあるのにも関わらず、 汎⽤コンポーネントを使⽤ props

    の誤った使い⽅ • デザイントークンを使わずハードコード • shorthand と longhand の混在 → 解決策: ESLint のカスタムルールを作成 Lint による検出と⾃動修正を可能にする
  5. © Findy Inc. 5 課題 コンポーネントの誤選択 • 専⽤コンポーネントがあるのにも関わらず、 汎⽤コンポーネントを使⽤ props

    の誤った使い⽅ • デザイントークンを使わずハードコード • shorthand と longhand の混在 → 解決策: ESLint のカスタムルールを作成 Lint による検出と⾃動修正を可能にする 今回はこちらについて
  6. © Findy Inc. 6 やりたいこと shorthand または longhand の統⼀について、ESLint で修正した時のイメージ

    shorthand <Box margin={4} /> // Fixed <Box m={4} /> longhand <Box m={4} /> // Fixed <Box margin={4} />
  7. © Findy Inc. 7 ESLint のカスタムルールを書く • ESLint の Rule.RuleModule

    を利⽤ • create メソッドにルールを定義 export const propsShorthand: Rule.RuleModule = { create(context) { return { // インポート文ごとに ImportDeclaration を呼ぶ ImportDeclaration(node) { }, // JSX ごとに JSXOpeningElement を呼ぶ JSXOpeningElement(node) { } } } } https://eslint.org/docs/latest/extend/custom-rules
  8. © Findy Inc. 8 仕組み • ESLint は書かれたコードを AST(抽象構⽂⽊)として認識 •

    AST explorer 等で ESLint がどう解析するかを⾒て実装する https://astexplorer.net/
  9. © Findy Inc. 9 実装で気を付けたこと(1) コンポーネントの参照元が Yamada UI か否かの判定 •

    props にルールを適⽤するために、そのコンポーネントが Yamada UI 固有のコン ポーネントか判定が必要 • Box という名前だけでは、他ライブラリ(Chakra UI, MUI 等)と区別できない • 出所はインポートにしか書かれていないため、インポート⽂を解析して判定しなけ ればならない // Box コンポーネントの重複例 import { Box } from "@yamada-ui/react" import { Box } from "@chakra-ui/react" import Box from "@mui/material/Box"; 名前の衝突が発⽣するため、 パッケージ名を確認する必要がある
  10. © Findy Inc. 10 実装で気を付けたこと(2) インポートの形式は様々あるため、それぞれ考慮が必要 named import { Box

    } from "@yamada-ui/react" <Box /> aliased import { Box as B } from "@yamada-ui/react" <B /> styled import { styled } from "@yamada-ui/react" <styled.div /> namespace import * as YUI from "@yamada-ui/react" <YUI.Box /> styled は HTML 要素を Yamada UI のスタイル機能付きコンポーネントに昇格させるファクトリのこと。 <styled.div /> の記法でスタイルをカスタマイズできる https://yamada-ui.com/ja/docs/components/styled
  11. © Findy Inc. 11 最終的な実装 1. インポート元を確認 対象パッケージ(@yamada-ui/react)からのインポートのみ追跡 2. コンポーネント

    / ネームスペースの分類 照合処理の仕⽅が変わるため 3. インポート元とコンポーネントを照合 • コンポーネントと⼀致するか → Box • ネームスペース(左端の)と⼀致するか → styled.div ⼀致すれば Yamada UI のコンポーネントと判定 → ルール適⽤
  12. © Findy Inc. 13 ルールを適⽤できないケース1 Yamada UI はコンポーネントを 2 つの⽅法で提供している

    • CLI からコンポーネントを追加した時、パスを任意で指定できる • デフォルトは @workspaces/ui だが、皆がデフォルトを使うとは限らない → 決まったパスを前提にインポート⽂を解析することができない https://yamada-ui.com/ja/docs/get-started/cli [1] モジュールからインポート @yamada-ui/react から直接読み込む [2] CLI でコードを追加 コンポーネントのソースコードをユーザー のリポジトリに追加
  13. © Findy Inc. 14 ルールを適⽤できないケース2 • 独⾃のコンポーネントに Yamada UI の

    style props を当てたいケース • この場合、コンポーネントは Yamada UI からインポートされたものではない → このケースでもインポート⽂の解析だけでは対応できない import { styled } from "@workspaces/ui" const StyledDiv = styled("div") export const CustomComponent = () => { return ( <StyledDiv p="4" bg="gray"> This is a custom component. </StyledDiv> ) } https://yamada-ui.com/ja/docs/components/styled 独⾃のコンポーネントの例 div に style props を当てた
  14. © Findy Inc. 15 型情報から判定する @typescript-eslint/parser で TypeScript Compiler API

    を使い、型を辿ってコン ポーネントの出所を特定する [1] シンボルを⾒る getSymbolAtLocation AST の識別⼦からシンボル を取得 シンボルによりインポート 元を辿れる [2] 型を辿る getTypeAtLocation コンポーネントの型を取得 し、props の型(BoxProps 等)や継承元を辿る 型をさらに追跡 [3] 宣⾔ファイルを特定 型を辿ると最終的に @yamada-ui/react に⾏き 着く Yamada UI と判定 https://typescript-eslint.io/packages/parser/
  15. © Findy Inc. 16 まとめ • コンポーネントを判定する仕組みを、作るのが予想以上に⼤変だった • AST explorer

    で構⽂⽊を⾒ながら⼿を動かしたことにより、 ESLint の仕組みが 分かるようになった • 今⽇の話で Yamada UI に少しでも興味を持っていただけたら、ぜひ触ってみて ください! 公式サイト: https://yamada-ui.com リポジトリ: https://github.com/yamada-ui/yamada-ui プルリクエスト: https://github.com/yamada-ui/yamada-ui/pull/7607