コフス技術ブログ

Dart Sassマイグレーション with Gulp

この記事はにメンテナンスが行われています。

GulpでSassコンパイルを行う際に使用するコンパイラをnode-sass(LibSass)からDart Sassへの移行する際の流れを記述します。

Dart Sassへの移行の必要性については本内容では省きます。以下参考ページをご覧ください。

流れ

  1. Dart Sassとfibersのインストール
  2. gulpfile.jsで使用するコンパイラを変更
  3. 既存SassファイルをDart Sassの仕様へ移行

インストール

必要なパッケージをインストールします。

yarn add -D cross-env fibers gulp gulp-sass sass

gulpfile.jsで使用するコンパイラをDart Sassに指定する

必要最小構成で記述したgulpfile.jsの例が以下です。

gulpfile.js
'use strict'

const Fiber = require('fibers')
const gulp = require('gulp')
const sass = require('gulp-sass')

sass.compiler = require('sass') // ここでコンパイラの指定をDart Sass(npm上のパッケージ名)に指定する

// 設定
const setting = {
  "io": {
    "input": {
      "css": "src/assets/css/"
    },
    "output": {
      "css": "dist/assets/css/"
    }
  }
}

const scssCompile = () => {
  return gulp
    .src(setting.io.input.css + '**/*.scss', { sourcemaps: true })
    .pipe(sass({fiber: Fiber, outputStyle: 'compressed'}).on('error', sass.logError))
    .pipe(gulp.dest(setting.io.output.css, { sourcemaps: '/maps' }))
}

const watch = () => gulp.watch(setting.io.input.css + '**/*.scss', scssCompile)

exports.default = watch
exports.development = scssCompile
package.jsonを表示
package.json
{
  "name": "dartsass",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "watch": "cross-env NODE_ENV=development gulp",
    "development": "cross-env NODE_ENV=development gulp development"
  },
  "prettier": {},
  "devDependencies": {
    "cross-env": "^7.0.2",
    "fibers": "^5.0.0",
    "gulp": "^4.0.2",
    "gulp-sass": "^4.1.0",
    "sass": "^1.27.0"
  }
}

ポイントはsass.compiler = require('sass')と記述している箇所でコンパイラの指定を行っている点です。以下のようにコンパイラ自体の切り替えは簡単に行えます。

  • コンパイラにDart Sassを使用する場合はrequire('sass')と指定
  • コンパイラにnode-sassを使用する場合はnode-sassを別途インストールしrequire('node-sass')と指定

登場するfibersはコンパイラの高速化のために用いられます。

To avoid this performance hit, render() can use the fibers package to call asynchronous importers from the synchronous code path. To enable this, pass the Fiber class to the fiber option:

その他sass()に渡すオプションはDart Sass公式ページより確認し必要に応じて付与してください。

諸々含めた場合のgulpfile.js

上記は最小構成の記述でしたが実際に使用する場合は各種パッケージも必要になります。諸々を含めたサンプルを以下になります。

gulpfile.jsを表示
gulpfile.js
'use strict'

const autoprefixer = require('autoprefixer')
const cssnano = require('cssnano')
const cssDeclarationSorter = require('css-declaration-sorter')
const Fiber = require('fibers')
const gulp = require('gulp')
const mqpacker = require('css-mqpacker')
const postcss = require('gulp-postcss')
const sass = require('gulp-sass')

sass.compiler = require('sass')

// 設定
const setting = {
  "io": {
    "input": {
      "css": "src/assets/css/"
    },
    "output": {
      "css": "dist/assets/css/"
    }
  }
}

const scssCompile = () => {
  return gulp
    .src(setting.io.input.css + '**/*.scss', { sourcemaps: true })
    .pipe(sass({fiber: Fiber, outputStyle: 'compressed'}).on('error', sass.logError))
    .pipe(
      postcss([
        autoprefixer(),
        mqpacker(),
        cssnano({ autoprefixer: false }),
        cssDeclarationSorter({ order: 'smacss' })
      ])
    )
    .pipe(gulp.dest(setting.io.output.css, { sourcemaps: '/maps' }))
}

const scssProductionCompile = () => {
  return gulp
    .src(setting.io.input.css + '**/*.scss')
    .pipe(sass({fiber: Fiber, outputStyle: 'compressed'}).on('error', sass.logError))
    .pipe(
      postcss([
        autoprefixer(),
        mqpacker(),
        cssnano({ autoprefixer: false }),
        cssDeclarationSorter({ order: 'smacss' })
      ])
    )
    .pipe(gulp.dest(setting.io.output.css))
}

const watch = () => gulp.watch(setting.io.input.css + '**/*.scss', scssCompile)

exports.default = watch
exports.development = scssCompile
exports.production = scssProductionCompile
package.jsonを表示
package.json
{
  "name": "dartsass",
  "version": "1.0.0",
  "main": "main.js",
  "scripts": {
    "watch": "cross-env NODE_ENV=development gulp",
    "development": "cross-env NODE_ENV=development gulp development",
    "production": "cross-env NODE_ENV=production gulp production"
  },
  "prettier": {},
  "devDependencies": {
    "autoprefixer": "^9.0.0",
    "cross-env": "^7.0.2",
    "css-declaration-sorter": "^5.1.2",
    "css-mqpacker": "^7.0.0",
    "cssnano": "^4.1.10",
    "fibers": "^5.0.0",
    "gulp": "^4.0.2",
    "gulp-postcss": "^9.0.0",
    "gulp-sass": "^4.1.0",
    "sass": "^1.27.0"
  }
}

既存SassファイルをDart Sassの仕様へ移行

Dart Sassとnode-sassの記述にはいくつかの違いがありますが、大きな違いとして@importを廃止し@useへの移行が挙げられます。

現在は互換性が保たれているため使用できますが、完全廃止が明言されているためDart Sassに切り替えるタイミングでこれら記述の変更も行うべき作業になります。

具体的には以下のように@useに変更していく作業が発生します。

hoge.scss
- @import "scss/fuga";
+ @use "scss/fuga";

また@importの時とは異なり変数や関数のスコープ範囲が読み込まれたファイル内に限定されるため、単純に@useに記述を変更するだけでは移行できない場合も往々にして出てきます。

たとえば以下のように大本のstyle.cssFoundation/variables.scssProjects/top.scssをimportした時、Projects/top.scssではFoundation/variables.scss内の変数を使用する事はできません。

style.scss
@import "scss/Foundation/variables";
@import "scss/Projects/top";
scss/Foundation/variables.scss
$fontFamily: "Noto Sans CJK JP", "Noto Sans JP", sans-serif;
scss/Projects/top.scss
.hoge {
  font-family: $fontFamily; // Error: Undefined variable.
}

Dart Sassでは変数を使用したいsassファイル毎に@useを行い、名前空間はas節で指定します。ES6などでのモジュール呼び出しの形をイメージすると分かりやすいです。

scss/Projects/top.scss
@use "scss/Foundation/variables" as *;
@use "scss/Foundation/mixin" as mx;

html {
  background-color: $color--blue;

  @include mx.media(sm) {
    background-color: $color--red;
  }
}

ビルトインモジュールの使用の際も事前に@useで宣言をする必要があります。たとえばmap-getを使用する場合は以下のようにします。

hoge.scss
@use "sass:map";

.hoge {
  margin: map-get($map , $key);
}

node_modules配下よりcssファイルを読み込む際はチルダで指定することは出来ず、階層に合わせたパスの指定が必要です。

@use "~ress/ress.css"; // NG
@use "../../../node_modules/ress/ress.css"; // OK

その他にもDart Sass特有の記述や機能がありそれらを用い移行する作業が発生します。

よりSassを活用したプロジェクトだと移行に必要な作業は多く、また簡素なプロジェクトであれば@useに書き換えるレベルで対応できたりとSassの活用度(熟練度)に応じて移行コストが変わります。

実際に移行する際はSassを@importから@useに置き換えるための手引きなどのドキュメントを確認しつつ、一気に移行するのが難しい場合はスコープを区切って徐々に移行していくといいでしょう。

ちなみにnode-sass(LibSass)はすでにDeprecatedアナウンスがされていますが、メンテナンスは引き続き行われるとの事です。

Michael Mifsud, the lead maintainer of LibSass and Node Sass, has affirmed that he plans to continue maintenance on the same level as the past few years. This means that although there will be no more features added (and as such LibSass will slowly drift further and further out of compatibility with the latest CSS and Sass syntax), there will continue to be maintenance releases indefinitely.

引用:https://sass-lang.com/blog/libsass-is-deprecateda

移行ツール