stylelintを使うためにSassからScssに乗り換えた話

TL;DR

auto-fix機能付きでSassに対応しているlinterが見つからなかったので、stylelintを使うために、SassからScssに乗り換えた。
変換については、sassc-rubyを使った簡単なスクリプトと、エディタの置換機能を用いて行った。

はじめに

OHEYAGOを開発している田渕です!

OHEYAGOの開発では歴史的な経緯により(後述)、リリースのタイミングまでスタイルシート言語としてSassを採用していました。
しかし、Sassではスッキリと書ける反面、lintが使えていませんでした。
lintを導入するために様々な試行錯誤を行いましたが、結局うまく行かなかったので、最終的にはScssに乗り換えました。

同じような悩みを持っている方に届けばと思い、記事にしてみます!

歴史的経緯

(興味のない方は読み飛ばしていただいて結構です)

開発最初は、FLOCSSとScssを用いてフロントエンド開発を行っていました。
しかし途中でFLOCSSの長いクラス名が辛くなり、徐々にCSS componentに置き換えていくことになりました。
その際にwebpackの設定で、Scssはグローバルに読み込んでFLOCSSのまま用い、新しく変更する部分ではSassを使ってCSS componentとして使うというようにし、デザインを壊さないまま連続的な移行を行いました。

参考までに、webpackの設定の一部を公開します。

rules: [
  {
    test: /\.scss$/,
    use: [
      { loader: MiniCssExtractPlugin.loader, options: { hmr: true, reloadAll: true } },
      { loader: 'css-loader',
        options: {
          sourceMap: true,
          importLoaders: 3,
        },
      },
      'postcss-loader',
      'resolve-url-loader',
      { loader: 'sass-loader', options: { sourceMap: true } },
    ],
  },
  {
    test: /\.sass$/,
    use: [
      'style-loader',
      { loader: 'css-loader',
        options: {
          sourceMap: true,
          modules: true,
          localIdentName: '[local]--[hash:base64:8]',
          importLoaders: 3,
        },
      },
      'postcss-loader',
      'resolve-url-loader',
      { loader: 'sass-loader', options: { sourceMap: true } },
    ],
  }
]

しかし、移行中に気づいたのですが、Sassに対するいい感じのlinterがありませんでした。
その結果、リリースするまでlintなしで駆け抜けてしまいました。

試したこと

lintが効いていない100ファイル以上のsassファイルを前に、開発者とデザイナーは毎日嘆いていました。
何度もsassファイルにlintを使おうと試行錯誤をしたので、その例を列挙します。

sass-lintを使おうとする

sass-lintを使おうとしました。
残念ながらauto-fixができませんでした。既に300件程度のviolationがあり、修正は断念しました。 (あと、メンテが終わっていることも問題となりました)

stylelintのcustom-syntaxを使おうとする

こちらのissueを見つけました。
closedなのであまり期待はしていませんでしたが、案の定--custom-syntax sassとしても、うまく動きません。
(もしくはparser書けと言われても、、、さすがにやりたくない。。。)

stylelintのsugarSSへのlintを使おうとする

同じissueを見ると、同じIndented StyleのsugarSSにはstylelintが対応していそうでした。
そこで、sassファイルをsugarSSとしてstylelintに解釈させました。

一部のファイルを除いてfixまでうまく動きましたが、interpolationを使っているファイルではsyntaxエラーとなってしまいました。

sugarSSでinterpolationを書く術があれば、該当箇所と拡張子を変えるだけでsugarSSに乗り換えられることに気づいたので、調べました。
しかし、こちらのissueで、

SugarSS is not Sass. You need postcss-sass.

とバッサリ切られてしまいました。

デザイナーの方になんとかsugarSSで開発できないかと尋ねましたが、どうやら厳しいようでした。
(sugarSSはInterpolationもmapもなく、ドキュメントも少ないそうです)

SassからScssに乗り換えることになった

いろいろ試しましたが、やはりSassを使い続けるのは辛いという結論に至りました。
となればScssに乗り換えるしかありません。 調べると、ruby-sassを用いる方法ばかりしかHitしませんが、

Ruby Sass Has Reached End-of-Life

と書いてありますね。私はとても悲しい。

言われるがままsassc-rubyを調べると、やっと光明を見つけました。(まだ少し苦労ポイントはあるのですが)

Additionally, you can use SassC::Sass2Scss to convert Sass syntax to Scss syntax.

ついに我々にlintが訪れるぞ!と思いましたが、コマンド一発ではSassをScssに変更できなさそうです。
ここらへんで、Sassはもはやあまり使われていないのだろうということに気づき始めてしまいました。

sassc-rubyを用いたスクリプトを書く

sassc-rubySassC::Sass2Scss.convertで一発で変換できればよかったのですが、どうやら違う様子。
改行もしてくれないので、自分で頑張るしかありませんでした。
とはいえ、scss記法は改行すべき位置がわかりやすいので、以下に示すくらいのスクリプトでなんとかなりました。

require 'sassc'

Dir.glob('app/**/*.sass').each do |input_path|
  output_path = input_path.sub(/.sass$/, '.scss')
  sass = File.read(input_path)
  scss = SassC::Sass2Scss.convert(sass).gsub('{ ', "{\n")
                                       .gsub(';', ";\n")
                                       .gsub('}', "}\n")
  File.write(output_path, scss)
end

インデントが壊れることについては、stylelint --fixを流すとすべて直してくれました。

最後に、ファイル名を一括置換し、webpackの設定を書き換えて、無事SassからScssへの移行が完了しました!

f:id:ktabuchi:20191102182219p:plain
該当PRの変更量の多さ

おわりに

調べてもほとんど情報が見つからないことばかりだったので、かなり苦労してしまいました。
微妙な技術選定をしてしまうと、後々に苦労するということを思い知りました。

同じようなことで悩んでる方の参考になれば嬉しいです!