Twigなど任意のテンプレート言語でTailwindCSSのクラスをソートできるPrettierプラグインを作った
作ったもの
ファイル内の class="..."
に合致する部分だけに prettier-plugin-tailwindcss による自動整形を適用してくれる、つまり class="..."
の部分以外には何の整形もせず、クラス名のソートだけをしてくれる Prettierプラグインです。
正確には、マッチさせるパターンは class="..."
に限らず自由に正規表現を設定できるようにしてあるので、理論上は 任意のテンプレート言語に対応できるはず です。
作った動機
最近、Symfony + Twig 構成のMPAで初めてTailwindCSSを使う機会がありました。
TailwindCSSのようなユーティリティファーストCSSフレームワークを使う場合、クラス名の並び順を少しでも意識してしまうと無駄に認知コストを消費して心理的ストレスがすごいので、クラス名は絶対に自動でソートされてほしいです。
幸い、TailwindCSS公式が提供している prettier-plugin-tailwindcss という素晴らしいPrettierプラグインを使えばクラス名は自動でソートすることができます。
試しに以下のようにして *.html.twig
ファイルを無理やり html
パーサーの適用対象にして prettier-plugin-tailwindcss
を適用してみました。
// .prettierrc
{
"plugins": ["prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.html.twig",
"options": {
"parser": "html"
}
}
]
}
すると、{% ... %}
や {{ ... }}
{# ... #}
といったTwig構文の箇所が誤ってぐちゃぐちゃに整形されてしまいました。html
パーサーが知らない語彙なので当然ですね。
どうやらTwigをパースできるPrettierプラグインを併用する必要がありそうです。ググってみると @zackad/prettier-plugin-twig が見つかりました。
ただし、これを普通に導入しても、prettier-plugin-tailwindcss
を適用するために overrides
で html
パーサーを当ててしまうとTwigパーサーは機能しなくなってしまうため、併用するにはおそらく
- https://github.com/prettier/prettier/issues/12807#issuecomment-1285968907
- https://github.com/tailwindlabs/prettier-plugin-tailwindcss/issues/31#issuecomment-1195411734
この辺のハックを使ってちょっと変なことをする必要がありそうです。(試してません🙏)
また、そもそも上記の @zackad/prettier-plugin-twig
による自動整形を試してみたところ、少なくとも <script>...</script>
で直接埋め込んでいるJSコードの改行とインデントが消されてしまう問題があり、導入は躊躇されました。(そんなコードを書くなと言われたらそれまでですが…)
欲しいのはただの ファイル内の class="..."
の中身にだけ prettier-plugin-tailwindcss
を適用してくれる君 なので、そういう機能のプラグインとして自分で作ってしまえば Twigに限らず任意のテンプレート言語で使える し便利なのでは?と思ったのでした。
使い方
npm install -D prettier \
prettier-plugin-tailwindcss \
@ttskch/prettier-plugin-tailwindcss-anywhere
でインストールします。内部的に prettier-plugin-tailwindcss
を使用するので、prettier
prettier-plugin-tailwindcss
と一緒にインストールする必要があります。
例えばTwigテンプレートを処理したい場合なら、.prettierrc
は以下のように設定します。
// .prettierrc
{
"plugins": ["prettier-plugin-tailwindcss", "@ttskch/prettier-plugin-tailwindcss-anywhere"],
"overrides": [
{
"files": "*.html.twig",
"options": {
"parser": "anywhere",
}
}
]
}
anywhere
というのが @ttskch/prettier-plugin-tailwindcss-anywhere
が提供するパーサーの名称です。
この設定でPrettierによる自動整形を実行すれば、以下のように *.html.twig
ファイル内の class="..."
の値部分にのみ prettier-plugin-tailwindcss
によるクラス名ソートが適用されます。
Before
{% extends 'base.html.twig %}
{% block content %}
<div class="space-y-4 flex-col flex">
<div class="p-4 rounded-lg bg-blue-500 text-white rounded-lg p-4">
Hello, {{ name }}!
</div>
</div>
{% endblock %}
{% endblock %}
After
{% extends 'base.html.twig %}
{% block content %}
- <div class="space-y-4 flex-col flex">
+ <div class="flex flex-col space-y-4">
- <div class="p-4 rounded-lg bg-blue-500 text-white rounded-lg p-4">
+ <div class="rounded-lg bg-blue-500 p-4 text-white">
Hello, {{ name }}!
</div>
</div>
{% endblock %}
{% endblock %}
Twigの場合、以下のように class="..."
の値部分の中に {% %}
や {{ }}
が埋め込まれていることもありえます。
<div class="space-y-4 flex-col flex {% if foo %}is-foo{% endif %}">
このような場合は、ソート対象としたいクラス属性の値部分を取り出すための正規表現を regex
オプションで明示的に設定するとよいです。
// .prettierrc
{
"plugins": ["prettier-plugin-tailwindcss", "@ttskch/prettier-plugin-tailwindcss-anywhere"],
"overrides": [
{
"files": "*.html.twig",
"options": {
"parser": "anywhere",
"regex": "class=\"([^{}\"]*)(?:\"| {)", // <-- here
}
}
]
}
上記の正規表現は、
- 以下の場合にマッチ
-
class="
で始まって -
{}"
のいずれでもない文字が繰り返されたのち -
"
または{
で閉じられた
-
- し、2 の部分が1番目のキャプチャグループとして後方参照できる
ことを表現しています。この後方参照によってプラグインがソート対象箇所を知るため、()
で括るのを忘れたり、値部分より前にキャプチャグループがあったりすると期待どおりに動作しないのでご注意ください。
この設定で実行すると、先ほどのコードは以下のように整形されます。
- <div class="space-y-4 flex-col flex {% if foo %}is-foo{% endif %}">
+ <div class="flex flex-col space-y-4 {% if foo %}is-foo{% endif %}">
{% %}
よりも前の部分だけが適切にソートされていますね。
お察しのとおり、
{% %}
が値の末尾ではなく中ほどにあったりすると、後半部分はソートされなくなってしまします。その辺は気をつけながらTwigを書いてください🙏
というわけで、@ttskch/prettier-plugin-tailwindcss-anywhere
のご紹介でした。よかったら使ってみてください&気に入っていただけた方はぜひスターお願いします!
このPrettierプラグインの作り方
こちらの記事で、このプラグインの作り方について解説しています。
よろしければ、参考にして何か素敵なプラグインを作ってみてください✨
Discussion