gulp-sass から Scss を疑似的な動的読み込みを行う

経緯

Scss で @import, @use, @forward のような他のファイルを読み込む機能は、 @ディレクティブ 内では使用できないません。そのため、例えば以下のようなことができません。

$flag: true;

@if ($flag) {
    @use "parts1.scss";
}

あるフラグ変数(ここでは $flag ですが、実際はファイル中ではなく Gulp タスク内で管理)が true のときのみ parts1.scss を読み込む。

$flag: true;

@if ($flag) {
    @use "parts1.scss";
}
@else {
    @use "parts2.scss";
}

あるいは、フラグ変数の値によって読み込む Scss ファイルを切り替える。

この部分について、どうにかできないか(本当にやりたいのは最初の例ですが、代替案として2つ目の例でブランクの parts2.scss をダミーとして用意する方法を考える)、と。

対処

色々検索した結果、 gulp-sassincludePaths オプションを使えばそれらしいことができそうだ、ということが分かりました。

今回はたまたま自分も gulp-sass を使用している前提で、しかも Scss 本体の変数ではなく Gulp タスク内で管理しているフラグによって、というのが希望だったのでこの形はぴったりでした (この方法でなければ、上記スレの上の方にあるように、フラグをいったん Scss ファイルに変数として出力し、それを読み込むことで伝播させようと考えていました)。

const { src, dest } = require('gulp');
const plumber       = require('gulp-plumber');
const notify        = require('gulp-notify');
const sass          =  require('gulp-sass');
sass.compiler = require('sass');
const Fiber = require('fibers');
const autoprefixer  = require('gulp-autoprefixer');

// dynamic import test
const flag = true;

// scssコンパイルタスク
const scss = () => {
    let opt = {
        fiber: Fiber,
        outputStyle: 'compressed'
    };
    // dynamic import test
    if (flag) {
        opt.includePaths = [
            `src/scss/parts/enable`
        ]
    }
    else {
        opt.includePaths = [
            `src/scss/parts/disable`
        ]
    }
    return src('src/scss/**/*.scss')
        .pipe(plumber({
            errorHandler: notify.onError({
                message: 'Error: <%= error.message %>',
                title: 'scss'
            })
        }))
        .pipe(sass(opt).on('error', sass.logError))
        .pipe(autoprefixer({
            cascade: false
        }))
        .pipe(dest('dist/css'));
};

module.exports = scss;

Scss のコンパイルタスクはこのような感じで。

src/
 └ scss/
      ├ .scssファイル やディレクトリ
      ├ import/
      │   └ _index.scss
      │
      └ parts/
          ├ disable/
          │    └ parts_hoge.scss
          └ enable/
               └ parts_hoge.scss

このようなディレクトリ構造で、 src/scss/import/_index.scss から src/scss/parts の中のenable または disable のどちらかの parts_hoge.scss を読み込む、とした場合。

// dynamic import test
@use "parts_hoge";

src/scss/import/_index.scss の記述はこれだけですが、 Scss のコンパイルタスクに記述されている変数 flag によって読み込む元のディレクトリの候補が src/scss/parts/enable または src/scss/parts/disable のどちらかに切り替わる、ということです。

あとは src/scss/parts/disable/parts_hoge.scss は中身のないブランクのファイルにしておけば、 flagtrue の場合のみ css が当たる、ということになります。

出力される css の切り分けはできたので、とりあえずはこの方法でしょうかね。

参考

この記事を書いた人

アルム=バンド

フロントエンド・バックエンド・サーバエンジニア。LAMPやNodeからWP、Gulpを使ってejs,Scss,JSのコーディングまで一通り。たまにRasPiで遊んだり、趣味で開発したり。