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

TypeScript Language Service Plugin で CSS Module...

TypeScript Language Service Plugin で CSS Modules の開発体験を改善する

コンポーネントに CSS を当てる手法の1つに、CSS Modules があります。広く使われている手法ですが、エディタ上の開発体験が悪いという欠点がありました。*.tsx と *.module.css の Language Server が分かれているために、*.tsx と *.module.css を横断する言語機能 (Rename,Find All References ) の挙動に問題があるのです。

長らくこの問題は解決困難と思われてました。しかし TypeScript Language Service Plugin を使うと、実は解決できるのです。この発表では、TypeScript Language Service Plugin とは何か、そしてそれを使って作ったツールについて紹介します。

- TypeScript Language Service Plugin とは
- CSS Modules Kit の紹介
- Volar.js を使って .module.css を TypeScript コードに偽装する
- Navigation 機能の実装 (Go to Definition, Rename, Find All References)
- 開発体験を改善するその他の工夫
- tsc による型検査のサポート
- 壊れかけのファイルのサポート
- linter-plugin の提供
- ts-plugin の課題

Avatar for mizdra

mizdra

May 23, 2025
Tweet

More Decks by mizdra

Other Decks in Programming

Transcript

  1. CSS Modules とは • CSS をローカルスコープ化する仕組み • CSS はグローバルスコープ ◦

    .foo { .. } はページ内全ての .foo に適用される • CSS Modules を使うと... ◦ 特定のコンポーネントの .foo にだけ適用できる 5
  2. 動作しない言語機能の例 • コードジャンプ ◦ 型定義に飛んでしまう (※1) • Find All Refefences

    ◦ .css 側から実行すると... ◦ .tsx が表示されない • Rename ◦ .tsx 側から実行すると... ◦ .css 側が rename されない 9 ※1 declare module "*.module.css" {...} と書かれた型定義がプロジェクト内に 存在する時の話。
  3. 言語機能はどう実装されているのか • いくつか実装方法はあるが... ◦ Language Server Protocol に則った方法が主流 • Language

    Server Protocol (LSP) ◦ 2016 年に Microsoft が発表 ◦ エディタに言語機能を実装するための標準仕様 11
  4. LSP の基本コンセプト • 『Language Server Protocol の仕様 及び実装方法 (*2)』より ◦

    • エディタとは別に Language Server (LS) がある ◦ これが言語機能を実装してる • エディタは LS を介し、ユーザに言語機能を提供 12 LSPの基礎コンセプトは、IDEがサポートしていた各機能(言語サービスと 呼ばれます)を標準化し、エディタ本体から分離するというものです。 *2: https://zenn.dev/mtshiba/books/language_server_protocol/viewer/02_introduction より引用
  5. 通常 LS は言語ごとに分かれてる 15 .ts .css エディタは言語ごとに専用の LS と通信 TS

    の言語機能を実装 (※3) CSS の言語機能を実装 (※4) ※3 厳密には JavaScript の言語機能も提供 ※4 厳密には Less や Sass の言語機能も提供 tsserver vscode-css-languageserver
  6. 改めて: なぜ CSS Modules で 言語機能が動作しないのか • TS の LS

    は TS 向けの言語機能しか実装してない ◦ .css 向けの言語機能は未実装 • クラスの Rename を行うと... ◦ TS の LS は *.tsx の Rename だけ行う • つまり言語ごとに LS が分かれているため... ◦ TS/CSS 横断の言語機能を提供できない 16
  7. React CSS modules • VS Code 拡張 • 拡張機能向けの API

    を用いて... ◦ 主要な言語機能をサポート 18 リポジトリ: https://github.com/Viijay-Kr/react-ts-css
  8. VS Code でしか動かない • React CSS Modules が VS Code

    拡張なので • 「VS Code 使ってくれ!」で良いかもしれないが... ◦ ツールの都合で VS Code 勧めたくない • VS Code 以外でも動くツールが欲しい 20
  9. CSS Modules Kit • CSS Modules のためのツールキット • 主要な言語機能をサポート •

    LSP をサポートするエディタで動く ◦ VS Code, Zed は動作確認済み ◦ NeoVim / Emacs は動作確認してないけど...動くはず 23
  10. デモ: 紹介する言語機能 • コードジャンプ • Find All References • Rename

    • className prop の補完 • クラスを追加するショートカット 25
  11. デモ: className prop の補完 • 通常 className と打って補完すると... ◦ className=""

    と入力される • ところで CSS Modules では... ◦ className={styles.xxx} と書くことがほとんど • そこで CSS Modules Kit では ◦ className={} と入力するようカスタマイズしてる 26
  12. CSS Modules Kit の構成 • 3種類のツールから構成 • codegen ◦ *.module.css.d.ts

    を生成する CLI ツール • ts-plugin ◦ エディタに言語機能を提供するツール • linter-plugin ◦ CSS Modules 向けの linter rule を提供する plugin 30
  13. CSS Modules Kit の構成 • 3種類のツールから構成 • codegen ◦ *.module.css.d.ts

    を生成する CLI ツール • ts-plugin ◦ エディタに言語機能を提供するツール • linter-plugin ◦ CSS Modules 向けの linter rule を提供する plugin 31 これについて解説
  14. ts-plugin の仕組み • TypeScript Language Service Plugin を使ってる ◦ TypeScript

    の LS (tsserver) の Plugin 機構 ◦ LSP request を横取りし、response を書き換えられる ▪ tsserver の挙動をカスタマイズできる • これを使い... ◦ tsserver が .css をサポートするよう、拡張してる 32
  15. ts-plugin による拡張イメージ 33 .ts .css TS + CSS の言語機能を実装 (※3)

    CSS の言語機能を実装 (※4) tsserver vscode-css-languageserver .css ts-plugin
  16. Volar.js • 言語ツール (LS, Linter, …)を作るためのフレームワーク ◦ Vue や Astro

    で使われてる • 詳しくは Vue Fes Japan の発表資料見て! 35 https://speakerdeck.com/mizdra/vue-language-server-karasheng-mareta-volar- dot-js-to-soregami-meruke-neng-xing
  17. Volar.js • 非 TS ファイルを tsserver に読ませる機能がある ◦ .vue とか

    .astro とか • ts-plugin ではこれを利用し... ◦ .css を tsserver に読み込ませてる 36
  18. コードの mapping • TS コードの偽装だけでは... ◦ コードジャンプや Rename ができない ◦

    .module.css ⇔ .d.ts の対応関係が不明なため • そこで、Volar.js にその対応関係を教えている 45
  19. ts-plugin の注意点 • ts-plugin は TS/CSS 横断の言語機能のみ実装 ◦ コードジャンプ、Rename、Find All

    References • CSS の標準的な言語機能は未実装 ◦ プロパティの補完、CSS 構文エラーの表示、色のプレビュー ◦ これは CSS の LS でしか実装されてない • 良い開発体験を得るには... ◦ TS/CSS 両方の LS を共存させないといけない 53
  20. 再掲: ts-plugin による拡張イメージ 54 .ts .css TS + CSS の言語機能を提供

    (※3) CSS の言語機能を提供 (※4) tsserver vscode-css-languageserver .css ts-plugin 両方を .css の LS として使用してる ※3 厳密には JavaScript の言語機能も提供 ※4 厳密には Less や Sass の言語機能も提供
  21. 1ファイルに対する複数 LS の割り当て • LSP の仕様では規定されてない ◦ エディタが独自に実装してる • (少なくとも)

    主要なエディタは実装してる ◦ NeoVim, Emacs, VS Code, Zed • 実装してないエディタでは ts-plugin 動かないかも 55
  22. 工夫1: tsc による型検査のサポート • ts-plugin により styles に型が付くが... ◦ ts-plugin

    は LS の挙動を変えるだけ ◦ tsc では styles に型が付かない! • そこで... ◦ .module.css.d.ts をファイルに書き出すツールを提供 ▪ codegen ◦ これを併用することで、tsc でも厳密な型検査が可能 57
  23. 工夫2: 壊れかけのファイルのサポート • エディタで編集中の CSS ファイルは不完全 ◦ } や ;

    が抜けてたり • 素朴に ts-plugin で扱おうとすると... ◦ CSS をパースする段階でコケてしまう • そこで... ◦ postcss-safe-parser を使用 ▪ 構文エラーがあってもパースを継続する ◦ 壊れかけの CSS でも言語機能が提供できるように 58
  24. 工夫3: linter-plugin の提供 • CSS Modules 向けの静的検査をしたくなる ◦ 未使用のクラスの警告など •

    そのためのツールも提供 ◦ stylint-plugin / eslint-plugin ◦ どっちも同じ rule を提供 60
  25. 例: Zed におけるセットアップ 1. 「CSS Modules Kit」拡張機能をインストール ◦ これで ts-plugin

    が tsserver に読み込まれるように 2. settings.json に下図の設定を追加 ◦ .css の LS として vtsls (tsserver のラッパー) を登録 63
  26. 例: NeoVim におけるセットアップ 1. npm i -g @css-modules-kit/ts-plugin 2. init.lua

    に右の設定を追記 ◦ plugin を tsserver にロードさせる ◦ .css の LS として tsserver を登録 64
  27. 課題2: VS Code で .css から Rename できない • .css

    から Rename すると... ◦ .css 側は Rename されるが .tsx 側はされない • TS/CSS の LS で Rename request が取り合いになってる ◦ (VS Code では) CSS の LS が優先される模様 ◦ ts-plugin に Rename request が届かないため... ▪ 言語横断で Rename できない • 今のところ良い解決策なし 65
  28. 今後の展望 • 対応エディタ増やす ◦ リクエストあれば教えて! • 便利な言語機能を追加する ◦ ホバーで CSS

    のソースを表示したい ◦ AI でなにか • tsgo どうしよう ◦ Language Service Plugin は .vue / .astro でも使われてる ◦ 全く動かないことはないだろうと思ってる 67
  29. まとめ • CSS Modules の言語機能が動作しない問題 ◦ VS Code 拡張で解決できるが、VS Code

    でしか動かない • そこで CSS Modules Kit 作った ◦ 多くのエディタに対応するため Language Service Plugin を使用 ◦ Volar.js を使って、.css を tsserver に読み込ませる • いくつか課題はあるものの... ◦ 開発体験を改善する豊富な言語機能が使えるように • 是非使ってみてください 68