gulpでDart Sassのコンパイル環境構築

気が付けばLibSassが非推奨になりDart Sassへ移行が進んでいるので、今回はgulpでDart Sassのコンパイル環境構築をざっくり説明を入れながら作成していきます。

gulpのバージョンは4です。
node.jsやyarnのインストールについては省いています。
Dart Sassの細かい説明は省いています。

やること
  1. Dart Sassのコンパイル環境を作成
  2. scssタスクの自動実行
  3. watchタスクにキャッシュ機能を付ける
目次

Dart Sassのコンパイル環境を作成

ファイル構成

src/assets/scssをコンパイルしてpublic/assets/cssに出力する構成です。

├─ public/
│   └─ assets/
│       └─ css/
│           └─ コンパイルされたcss
│
├─ src/
│   └─ assets/
│       └─ scss/
│           ├─ _variable.scss
│           ├─ layout.scss
│           └─ home.scss
│
├─ gulpfile.js
└─ package.json

パッケージの追加

gulpを実行するために必要なパッケージを入れます。

yarn add -D gulp gulp-notify gulp-plumber gulp-debug

gulp-notifyはエラー通知、
gulp-plumberはエラーが発生した場合にタスクが停止するのを防止します。
gulp-debugは処理中のファイルをログに表示できます。

次にDart Sassをコンパイルするために必要なパッケージを追加します。

yarn add -D gulp-dart-sass gulp-autoprefixer gulp-sourcemaps

gulp-dart-sassは今回の主役です。
gulp-autoprefixerはベンダープレフィックスを追加してくれます。
gulp-sourcemapsはChrome等の検証ツールからコンパイル前のソースを調べやすくできます。

その他に圧縮したりするパッケージも入れたほうがいいですが、最低限これだけあれば十分かと思います。

scssファイルの作成

src/assets/scssディレクトリにlayout.scss
@useで読み込む設定ファイル_variable.scssを作成します。
ついでに複数ファイルが正常にコンパイルされるか確認するためにhome.scssを作成しておきます。

// 設定 - _variable.scss
$font_color: #262f2d;
$bg_color: #2aa47A;
// Layout - layout.scss
@use "variable" as vars;

body {
  font-size: 1.6rem;
  color: vars.$font_color;
  background: vars.$bg_color;
}
// Home - home.scss
body {
  font-size: 2rem;
}

gulpfile.jsの作成と実行

gulpfile.jsにDart Sassのコンパイルタスクを記述します。

const gulp = require('gulp');
const notify = require('gulp-notify');  // エラー通知
const plumber = require('gulp-plumber'); // エラー時のタスク停止防止
const debug = require('gulp-debug'); // ログ表示
const dartSass = require('gulp-dart-sass');
const autoprefixer = require('gulp-autoprefixer'); // ベンダープレフィックス付与
const sourcemaps = require('gulp-sourcemaps'); // ソースマップ出力

const paths = {
  scss: {
    src: 'src/assets/scss/**/*.scss', // コンパイル対象
    dest: 'public/assets/css' // 出力先
  }
}

/**
 * scssタスクで実行する関数
 */
function scss() {
  return gulp.src(paths.scss.src)
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(sourcemaps.init())
    .pipe(dartSass({
      outputStyle: 'expanded'
    }))
    .pipe(autoprefixer({
      cascade: true
    }))
    .pipe(sourcemaps.write('/maps'))
    .pipe(gulp.dest(paths.scss.dest))
    .pipe(debug({title: 'scss dest:'}));
}

exports.scss = scss; // scssタスク
exports.default = gulp.series(scss); // defaultタスク

作成したタスクが正常に動作するか確認するため、ターミナルからyarn gulpyarn gulp scss、環境によってはgulpgulp scssでコンパイルできるか試してみます。

# defaultタスクの実行
yarn gulp

# scssタスクの実行
yarn gulp scss

正常に動作した場合は以下のファイルが出力されます。

  • public/assets/css/layout.css
  • public/assets/css/home.css
  • public/assets/css/maps/layout.css.map
  • public/assets/css/maps/home.css.map

これでDart Sassのコンパイル環境は完成です。

scssタスクの自動実行

前述で作成したscssタスクを自動実行させるようにwatchタスクを作成します。
watchタスクはファイルを監視させて変更があった場合に指定したタスクを実行できます。

watchタスクの追加と実行

gulpfile.jsを以下のように変更します。(ハイライトされている行が変更点)

const gulp = require('gulp');
const notify = require('gulp-notify');  // エラー通知
const plumber = require('gulp-plumber'); // エラー時のタスク停止防止
const debug = require('gulp-debug'); // ログ表示
const dartSass = require('gulp-dart-sass');
const autoprefixer = require('gulp-autoprefixer'); // ベンダープレフィックス付与
const sourcemaps = require('gulp-sourcemaps'); // ソースマップ出力

const paths = {
  scss: {
    src: 'src/assets/scss/**/*.scss', // コンパイル対象
    dest: 'public/assets/css' // 出力先
  }
}

/**
 * scssタスクで実行する関数
 */
function scss() {
  return gulp.src(paths.scss.src)
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(sourcemaps.init())
    .pipe(dartSass({
      outputStyle: 'expanded'
    }))
    .pipe(autoprefixer({
      cascade: true
    }))
    .pipe(sourcemaps.write('/maps'))
    .pipe(gulp.dest(paths.scss.dest))
    .pipe(debug({title: 'scss dest:'}));
}

/**
 * watchタスクで実行する関数
 */
function watch() {
  return gulp.watch(paths.scss.src, gulp.series(scss))
}

exports.scss = scss; // scssタスク
exports.watch = watch; // watchタスク
exports.default = gulp.series(scss); // defaultタスク

watchタスクで実行する関数(36-41行目)とwatchタスク(44行目)を追加しました。
監視対象はscssタスクのsrcと同じsrc/assets/scss/**/*.scssです。

ターミナルからyarn gulp watchコマンドを実行すると監視状態になるので、layout.scsshome.scss を変更して保存すると自動的にコンパイルされます。

# watchタスクの実行(停止:Ctrl + C)
yarn gulp watch

これでwatchタスクは一応完成です。
次はキャッシュ機能を付けて、変更があったファイルのみコンパイルされるようにします。

watchタスクにキャッシュ機能を付ける

ここまでの流れで一応完成ですが、layout.scssの内容を変更して保存すると、関係のないhome.scssまでコンパイルされてしまいます。

Starting 'watch'...
Starting 'scss'...
scss dest: public/assets/css/maps/home.css.map  ← 変更していないファイル
scss dest: public/assets/css/home.css           ← 変更していないファイル
scss dest: public/assets/css/maps/layout.css.map
scss dest: public/assets/css/layout.css
scss dest: 4 items
Finished 'scss' after 175 ms

変更を加えていないファイルまでコンパイルされてしまうと処理時間が増えてしまうため、変更したファイルだけコンパイルされるようにgulp-cachedパッケージを追加します。

gulp-cachedパッケージの追加と実装

gulp-cachedを使うとファイルをキャッシュして、変更があるファイルのみ処理されるようになります。

ターミナルから以下のコマンドを実行してパッケージを追加します。

yarn add -D gulp-cached

gulpfile.jsを以下のように変更します。

const gulp = require('gulp');
const notify = require('gulp-notify');  // エラー通知
const plumber = require('gulp-plumber'); // エラー時のタスク停止防止
const debug = require('gulp-debug'); // ログ表示

const dartSass = require('gulp-dart-sass');
const autoprefixer = require('gulp-autoprefixer'); // ベンダープレフィックス付与
const sourcemaps = require('gulp-sourcemaps'); // ソースマップ出力

const cached  = require('gulp-cached'); // ファイルキャッシュ

const paths = {
  scss: {
    src: 'src/assets/scss/**/*.scss', // コンパイル対象
    dest: 'public/assets/css' // 出力先
  }
}

/**
 * scssタスクで実行する関数
 */
function scss() {
  return gulp.src(paths.scss.src)
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(cached('scss')) // ファイルをキャッシュ
    .pipe(sourcemaps.init())
    .pipe(dartSass({
      outputStyle: 'expanded'
    }))
    .pipe(autoprefixer({
      cascade: true
    }))
    .pipe(sourcemaps.write('/maps'))
    .pipe(gulp.dest(paths.scss.dest))
    .pipe(debug({title: 'scss dest:'}));
}

/**
 * watchタスクで実行する関数
 */
function watch() {
  return gulp.watch(paths.scss.src, gulp.series(scss))
}

exports.scss = scss; // scssタスク
exports.watch = watch; // watchタスク
exports.default = gulp.series(scss); // defaultタスク

10行目に追加したgulp-cachedパッケージを読み込み、27行目にキャッシュの設定を追加しました。

ターミナルからyarn gulp watchコマンドを実行して、layout.scsshome.scssを変更すると、1回目はキャッシュがないのですべてのファイルがコンパイルされ、2回目からは変更したファイルだけコンパイルされるようになります。

処理説明をすると実際はすべてのファイルが処理されていますが、キャッシュが行われたファイルに変更がない場合は27行目で処理が中断されます。
27行目の.pipe(cached('scss'))の前に以下を追加すると処理中のファイル名が表示されますが、コンパイル結果のログには変更があったファイルのみ表示されていることが分かります。

.pipe(require('through2').obj((file, enc, callback) => {
  console.log('file:', file.path)
  return callback(null, file);
}))

これでキャッシュ機能が正常に動いているのは確認できましたが、できれば1回目から変更したファイルだけコンパイルするのが理想的なので、先にキャッシュさせるように変更します。

先にキャッシュを行うように変更

gulpfile.jsを以下のように変更します。

const gulp = require('gulp');
const notify = require('gulp-notify');  // エラー通知
const plumber = require('gulp-plumber'); // エラー時のタスク停止防止
const debug = require('gulp-debug'); // ログ表示

const dartSass = require('gulp-dart-sass');
const autoprefixer = require('gulp-autoprefixer'); // ベンダープレフィックス付与
const sourcemaps = require('gulp-sourcemaps'); // ソースマップ出力

const cached  = require('gulp-cached'); // ファイルキャッシュ

const paths = {
  scss: {
    src: 'src/assets/scss/**/*.scss', // コンパイル対象
    dest: 'public/assets/css' // 出力先
  }
}

/**
 * scssタスクで実行する関数
 */
function scss() {
  return gulp.src(paths.scss.src)
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(cached('scss')) // ファイルをキャッシュ
    .pipe(sourcemaps.init())
    .pipe(dartSass({
      outputStyle: 'expanded'
    }))
    .pipe(autoprefixer({
      cascade: true
    }))
    .pipe(sourcemaps.write('/maps'))
    .pipe(gulp.dest(paths.scss.dest))
    .pipe(debug({title: 'scss dest:'}));
}

/**
 * scssファイルをキャッシュする関数
 */
function scssCache(){
  return gulp.src(paths.scss.src)
    .pipe(cached('scss')) // ファイルをキャッシュさせる
    .pipe(debug({title: 'scss cached:'}));
}

/**
 * watchタスクで実行する関数
 */
function watch() {
  return gulp.watch(paths.scss.src, gulp.series(scss))
}

exports.scss = scss; // scssタスク
exports.watch = gulp.series(scssCache, watch); // watchタスク
exports.default = gulp.series(scss); // defaultタスク

40-47行目にscssファイルのキャッシュを行うscssCache関数と、57行目のwatchタスクでscssCache関数を呼び出した後にwatch関数を呼ぶように変更しました。

gulp watchを実行後にlayout.scssを変更して保存するとlayout.scssのみコンパイルされます。

ただしキャッシュ機能の副作用で、@useで読み込んでいる_variable.scssを変更して保存すると何も起こらないはずです。
これは_variable.scssの読み込み元であるlayout.scssに変更がないため何も処理されないというわけです。

単純な解決方法としてはlayout.scssを変更するか、scssタスクを実行させればいいのですが何だかモヤッとします…。

この問題を解決するには、sassファイルの親子関係を管理しているsass-graphパッケージを別の物に変更するか、パッチを作って対応する方法があるので、よかったら続きの記事を読んでみてください。

  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次