🕌

emotionで記述したCSSのプロパティを並び替えるeslint pluginを作った

2022/05/08に公開

https://github.com/myuon/eslint-plugin-css-reorder

執筆時点ではv0.5.1が最新版です

使い方

npm/yarnで入れて、eslintのconfigに

{
  "plugins": ["css-reorder"],
  "rules": {
    "css-reorder/property-reorder": "error"
  }
}

のような記述をするだけです

作った経緯

最近はCSSはほぼemotionで書いていますが、プロパティはいい感じに並び替えて欲しい気がすることと、探しても意外に既存のプラグインがなかったので作りました。

(仕事では)stylelintを使っていましたが、いつの日からかあまりまともに動かなくなっていたことと、そもそもlinterを複数抱えるのは環境構築・メンテの面でつらみがあるのでできればeslintに一本化したい、新しく作るのも難しくはなさそうなのでやってみるかでやってみました。

中身について

やっていることはほぼ、postcssでパース→postcss-sortingでソートする→eslintに結果を渡す、くらいです。
いくつかこのプラグインで特別な処理を挟んでおり、ほとんどはCSS in JS由来の(本来のCSSでは出現しない記述を無理やりpostcssに読ませるための)部分を処理するための記述です。

以下に、このプラグインがやっている特別な処理を書きます

preprocess時にline commentをblock commentに変える

// で開始するline commentはJS由来のものなので正規表現で無理やりブロックコメントに変えています。しかし、ブロックコメント内の // を変えてしまうとコメントがネストし、postcssのパーサーがなんかうまく動かなかったりするので本当はもうちょっと真面目にやらないと死ぬケースがありそうです。
あとURLがブロックコメントになって死ぬケースが存在したので :// は変えないというだいぶアドホックな処理を入れています。悲しみ

式の埋め込み除去と復元

margin: ${prop.hoge} のように、 ${} 記法で式を埋め込めますがこれも当然postcssがエラーになるので対応しています。
パーサーを真面目に書きたくないので、この記法を発見するとCSS変数(!)に適当に置き換えて後で復元します。上記の例だと margin: var(--placeholder-0) みたいにします。なかなか最悪です
ちなみにオブジェクト自体を ${StyledComponent} みたいに埋め込むやつは対応してないです。


こうしてみると、「真面目にパーサーを書け」という感じの問題が多いです。ただ、postcssのパーサーは拡張性が基本的にないため(postcssにはpluginもありますが、pluginを使うとパーサーが非同期になってしまいます。eslintは同期parserしか受け付けないので使えないです)、CSSの完全なパーサー自作したくないなあみたいな気持ちです。もしかしたらtokenizer用意するだけでどうにかなるかもしれないですが…うーむ

作ってどうだったか

最近の趣味開発などでちょくちょく使っていますが普通に良いです。保存したら勝手に並び替えてくれるのは最高だなの気持ち。仕事でも入れていこうかな

できてないこと

いっぱいありますが、現状困ってないのが正直なところ。たまにはメンテもしようと思いますが。

  • postcssのエラーをちゃんとeslintに伝える: 雑なのでエラーだよくらいしかメッセージが出てこない欠陥メッセージになっているため
  • (上記でも書いたけど)オブジェクトそのものの埋め込み記法への対応
  • configurationによって並び替えの順番などを制御できるようにする
  • eslintのconfigをextendsで書けるようにす(plugin:css-reorder/recommended の提供)
  • 並び替えする時にwhitespaceが正しく保たれないため、並び替えるとフォーマットがおかしくなる(実際はprettierがその後直すので実害はないが…)

Discussion