Dart Sassに移行(Gulp / Node 16 / glob対応)
node.jsのバージョンを、16.0.0にあげると、gulp-sassでエラーが出ました。
npm ERR! code 1
npm ERR! path /hoge/node_modules/node-sass
LibSassとNodeSassが非推奨になったからです。
知っていました…わかっていてちょっと後回しにしてましたが、ついにNodeのバージョンアップでひっかかりました。
これを機にDart Sassへ移行しましたが、途中で
- Nodeのバージョンが16だとgulp-sassをインストールできない
- @importを@forwardにしたのでgulp-sass-globがきかない
と、つまづきポイントがあったので、備忘録を残しておきたいと思います。
Dart Sass、gulp-sass、Node Sass
node-sassは、sassをC/C++で実装したライブラリであるLibSassへ、Node.jsのバインディングを提供するライブラリです。
gulp-sassはnode-sassやdart-sassを設定することによりsassを使用可能にできるGulp用のプラグインで、デフォルトではnode-sassが設定されます。
Dart Sassには同期処理renderSync()と、非同期処理render()があり、renderSync()はrender()の2倍以上の速さ。なので、非同期処理にしたい場合は、fibresパッケージを使用すると解決できる(同期コードパスから非同期インポーターを呼び出す)。
…とDart Sassの公式に書いてありましたが若干意訳入ってますので自信はありません。
When installed via npm, Dart Sass supports a JavaScript API that aims to be compatible with Node Sass. Full compatibility is a work in progress, but Dart Sass currently supports the
https://sass-lang.com/dart-sassrender()
andrenderSync()
functions. Note however that by default,renderSync()
is more than twice as fast asrender()
, due to the overhead of asynchronous callbacks.
gulp-sassでスイッチングする方法
gulp-sassがインストールできる場合は、compilerプロパティでとりあえず実現できます。
const gulpSass = require('gulp-sass');
const fibers = require('fibers');
gulpSass.compiler = require('sass'); //Dart Sassを指定
const sass = (done) => {
gulp
〜省略〜
.pipe(gulpSass({ fiber: fibers }))
〜省略〜
done();
};
gulpfile.jsで、gulpSassにcompilerプロパティを使ってdart sassを設定するだけです。
fibersパッケージを使う場合は、fiberオプションに渡すだけ。
…しかし、ここで問題が発生しました。
Node バージョン16でgulp-sassがインストールできない
Nodeのバージョンが16.0.0だと、そもそもgulp-sassをインストールするときに同時にnode-sassをインストールしようとするため、インストールエラーとなります。
なんとなく調べてわかったことですが、nodeのバージョンが14,15…と上がってきた時も似た様なことがあったようです。
node.jsのバージョンを切り替えて(下げて)インストールした…みたいな記事をいくつか見ました。バージョンの追いかけっこのようになっている印象です。
node-sassのgithubでは、3日前〜にタイムリーにやりとりがされていました。
このあたり。
https://github.com/sass/node-sass/issues/3077
https://github.com/sass/node-sass/pull/3090
見る限り、待っていればもしかしたらNode 16に対応するかもしれないなぁ…という印象でしたが、そもそもSassのコアチームが「LibSassとその上に構築されたNodeSassを含むパッケージが非推奨になる」という結論に達した…ということなので、
node-sassが対応するのを待つよりは、node-sassを使わずにgulpでDartSassを使う方法を考えるべきだと思いました。
でも、どうやっても、gulp-sassを使う限りは、node-sassが付いてくるようだ…と諦めかけましたが。
gulp-dart-sass
dart-sassのgulp用パッケージを発見しました。
これをつかえばいいじゃないか…!!そもそもnode-sassはもう使わないのだから、gulp-sassで切り替える必要なんてない。
こちらを使ってみることに。
const gulpDartSass = require('gulp-dart-sass');
const sass = (done) => {
gulp
〜省略〜
.pipe(gulpDartSass.sync().on('error', gulpDartSass.logError))
.pipe(gulpDartSass.sync({ outputStyle: 'expanded' }))
〜省略〜
done();
};
fibresパッケージは追加しませんでした。
理由は以下です。
- 同期処理なので必要なさそう
- fibresのREADMEに「可能であればその使用を避けることをお勧めします」と記載あり
- 現時点でNode16以降と互換性がない
- gulp-dart-sassのREADMEにfibresパッケージのことが書いてない
ちなみに、gulpを走らせるとエラーがでましたが、ちょっとしたミスによるものでした。
Assertion failed: (thread_id_key != 0x7777), function find_thread_id_key, file ../src/coroutine.cc, line 134.
fibersの読み込みがgulpfile.jsに残っていたら上記エラーになりました。
const fibers = require('fibers');
削除しました。(宣言だけでもダメなのか…)
これで、gulpを実行すると、しっかりコンパイルできました。
min()やmax()、clamp()もエラーになりません。
@importから@useへの移行
これでgulpでDart Sassが使える様になりましたが、Dart Sassへ移行するにあたって、ルール変更があります。
今まで@importで外部Sassファイルをインクルードしていたところを、@useに書きかえないといけません。
まだ@importで書かれていても使えますが、今後は廃止される予定なので、@useの書き方にしておく必要があります。
とりあえず、ステップとしては
- style.scssにて、@forwardでその他のsassを読み込む
- 変数やmixinを記述したscss(_variable.scss)を、その他ファイルで@use読み込み。その際、ネームスペースを短めに変更
とすることにしました。
@forward 'variable.scss';
@forward 'reset.scss';
@forward 'global.scss';
@forward 'component/**/*.scss';
@forward 'common/**/*.scss';
〜〜
@use 'variable' as *;
body{
color: $fontColor;
@include font-go;
}
いろいろ省いてますが、このような感じです。(@useや@forwardについては、解説してあるサイトがたくさんあるのでそちらを参考にしました)
もともと、style.scssに他の全てのscssを@importする形で作っていたので、それを@forwardに書きかえ、
各scssではmixinと変数を集約した_variable.scssを読み込むために@use 'variable' as v;を付け足し。ネームスペースがvariableのままだと長いので、vに変更して、@include v.hogeで呼び出せるようにしました。
@use 'variable' as *;
をつけたし。
これでグローバルCSSがstyle.cssに集約できました。
後日追記
以前はネームスペースをv.にし、color: v.$fontColor;
などのように「v.」を使う形にしていましたが、「*」を使えばネームスペースを指定しなくても使えることがわかりました。
CSS設計がない場合はネームスペースは便利かもしれませんが、自分はCSS設計ありきで書くので、アスタリスクを使用することにしました。
gulp-sass-globがきかない → gulp-sass-glob-use-forwardで解決
さらに問題が発生。
style.scssは、
@forward 'component/*/.scss';
のような書き方で、アスタリスクを使ってcomponentフォルダの中に入ってるもの全てを対象とするようにしていました。
これを可能にするのが、今まではgulp-sass-globというパッケージだったのですが、これが@importにしか対応していない…。
散々ググってもこれといって見つからず、npmのサイトで「glob forward」で検索かけてみると…
あった!!!
使い方はgulp-sass-globと同じでした。
google検索だけではわからんかったな…。探してみるもんだ。
インストール (もしくはpackage.jsonに書いておく)
npm i gulp-sass-glob-use-forward
gulpfile.jsで読み込み、sassコンパイルの途中で実行するようにする
const sassGlob = require('gulp-sass-glob-use-forward');
const sass = (done) => {
gulp
.src(baseDir + '/sass/**/*.scss', { sourcemaps: true })
.pipe(sassGlob()) // Sassのglobを有効にする
.pipe(gulpDartSass.sync().on('error', gulpDartSass.logError))
.pipe(gulpDartSass.sync({ outputStyle: 'expanded'}))
.pipe(gulp.dest(dist + '/css'), { sourcemaps: './maps' })
done();
};
〜〜
まとめ
ファイル単位で変数を閉じ込めるのは、何かの流れを感じます…。
JSのフレームワークを使っていると必要なものを読み込んで使うのは当たり前の感覚ですが、SCSSは個人的に制作ルールが確立しているので、グローバルの方が使いやすかっ…
とにもかくにも慣れですね。
ちなみにその後、SassからpostCSSに移行しようとしてやめた後日談もあります。
SassからPostCSSにしようとして「いや、今じゃない」と移行をやめた話
参考
Github gulp-dart-sass
https://github.com/mattdsteele/gulp-dart-sass
node-fibres Readme
https://github.com/laverdet/node-fibers
Sass:Dart Sass
https://sass-lang.com/dart-sass
gulp-sass -npm
https://www.npmjs.com/package/gulp-sass