gulp watchでDart Sassの親子関係を解消する

前回はgulpでDart Sassのコンパイル環境を構築しましたが、gulp-cachedパッケージを使用した場合のgulp watchでは、 キャッシュ機能の副作用により@useを使用して読み込んでいるファイル(子)を変更しても、読み込み元のファイル(親)はコンパイルされないという不満がありました。

今回はこの問題を解決します。

まず先におさらいで、ファイル構成はこのようになっています。

├─ public/
│   └─ assets/
│       └─ css/
│           └─ コンパイルされたcss
│
├─ src/
│   └─ assets/
│       └─ scss/
│           ├─ _variable.scss(子)
│           ├─ layout.scss(親:@useで_variable.scssを読み込む)
│           └─ home.scss
│
├─ gulpfile.js
└─ package.json

冒頭でも同じことを書きましたが、_variable.scss(子)を変更しても読み込み元であるlayout.scssやhome.scss(親)に変更がないため何も処理されません。

そこで親子関係を解決してくれるgulp-sass-parentパッケージを入れる必要があります。
古めのパッケージだとgulp-sass-partials-importedですが、今回は新しいgulp-sass-parentパッケージを使います。

ただDart Sassの場合は、このパッケージを入れただけでは正常に動作しません。
依存パッケージを辿っていくと分かりますが、最終的にDart Sassの親子関係を解析しているsass-graphパッケージがDart Sassに対応していないからです。

そこでsass-graphを修正したパッケージを使うパッチを作って対応します。

対応の違いを簡単に説明すると、修正したパッケージを使う方が導入がしやすく、パッチを作る方法はソースを自由に変更できますが手順が少し多いです。

上記作業を行う前に、まずはgulp-sass-parentパッケージだけ入れて動作確認をします。

目次

gulp-sass-parentパッケージのインストールとgulpfile.jsの作成

前回のソースにgulp-sass-parentパッケージを追加して動作確認をします。

yarn add gulp-sass-parent -D

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 sassParent = require('gulp-sass-parent');  // 親子関係を解決

const paths = {
  scss: {
    src: 'src/assets/scss/**/*.scss', // コンパイル対象
    parent: 'src/assets/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(sassParent({dir: paths.scss.parent})) // ファイルキャッシュ時でも親子関係を解決する
    .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:'}));
}

/**
 * キャッシュ
 */
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タスク

10行目と15行目、29行目にsassParentの設定を追加しました。
ターミナルから yarn gulp watch コマンドを実行して監視状態にします。

# watchタスクの実行
yarn gulp watch

_variable.scssを変更して保存すると自動的にコンパイルされます。

ですが、_variable.scssのコンパイル結果は以下のように「scss dest: 0 items」となり何も変更されません。

Starting 'scss'...
scss dest: 0 items
Finished 'scss' after 30 ms

現時点ではこのようになるのでsass-graphパッケージを好みの方法で調整します。

sass-graphを修正したパッケージを使う場合はこちら
sass-graphを修正してパッチを作る場合はこちら

sass-graphを修正したパッケージを使う

修正されたパッケージを使用する場合は、package.jsonのresolutionsに変更したいパッケージを記述するだけです。

"resolutions": {
    "sass-graph": "git+https://github.com/macropygia/sass-graph.git"
  },

この記述を入れるとsass-graphは必ずこのパッケージを使うようになります。
そのため、他のパッケージでsass-graphが使用されている場合に依存を無視するため、バージョン違いによるエラーが発生する可能性があります。

ファイルを保存してyarn installコマンドを実行します。

yarn install

yarn.lockファイルにもしっかり記述されるので再度パッケージをインストールするときも安心です。

sass-graph@^3.0.4, "sass-graph@git+https://github.com/macropygia/sass-graph.git":
  version "3.0.5"
  resolved "git+https://github.com/macropygia/sass-graph.git#3fca85230f6c91dc2a65c07f8cd688761b0bf801"
  dependencies:
    glob "^7.1.7"
    lodash "^4.17.21"
    scss-tokenizer "^0.3.0"
    yargs "^17.1.1"

この状態でgulp watchコマンドを実行します。_variable.scssを変更して保存すると以下のようにlayout.scssがコンパイルされるようになります。

Starting 'scss'...
scss dest: public/assets/css/maps/layout.css.map
scss dest: public/assets/css/layout.css
scss dest: 2 items
Finished 'scss' after 164 ms

もしパッケージマネージャーがnpmの場合は、npm-force-resolutions モジュールを入れて対応する必要があるのでリンク先を参照してください。

sass-graphのパッチを作って対応する

修正するファイルはnode_modules配下のパッケージのため、再度パッケージをインストールすると元に戻ってしまいます。そのためパッチファイルを作成して、yarn installコマンドでパッケージのインストール後にパッチが自動的に適用されるようにします。

パッケージのインストール

必要なパッケージを入れます。

yarn add patch-package postinstall-postinstall -D

patch-packageはパッチの作成と適用ができるパッケージです。
postinstall-postinstallはyarnコマンドを使う場合のみ必要になるパッケージで、package.jsonのpostinstallにコマンドを書いておくと、パッケージのインストール完了後に記述したコマンドを実行させることができます。npmコマンドを使用する場合は不要です。

sass-graphパッケージの改修

sass-graphパッケージ(バージョン3.0.5)を改修します。

編集するファイルはnode_modules/sass-graph/parse-imports.jsの以下の部分です。

    if (inImport && !inParen && token[0] === 'string') {
      results.push(token[1]);
    }
    else if (token[1] === 'import' && prevToken[1] === '@') {
      if (inImport && !isIndentedSyntax) {
        throw new Error('Encountered invalid @import syntax.');
      }

      inImport = true;
    }

18行目を以下のように書き換えます。

    if (inImport && !inParen && token[0] === 'string') {
      results.push(token[1]);
    }
    else if (['import', 'use', 'forward'].includes(token[1]) && prevToken[1] === '@') {
      if (inImport && !isIndentedSyntax) {
        throw new Error('Encountered invalid @import syntax.');
      }

      inImport = true;
    }

Dart Sassの@useと@forwardを使えるように変更しました。

この状態でgulp watchコマンドを実行します。_variable.scssを変更して保存すると以下のようにlayout.scssがコンパイルされるようになります。

Starting 'scss'...
scss dest: public/assets/css/maps/layout.css.map
scss dest: public/assets/css/layout.css
scss dest: 2 items
Finished 'scss' after 164 ms

最後にパッチファイルの作成とpostinstallの設定です。

パッチファイルの作成とpostinstallの設定

パッチファイルの作成は非常に簡単で以下のコマンドを実行します。

yarn patch-package sass-graph

# 実行結果ログ
patch-package 6.4.7
• Creating temporary folder
• Installing sass-graph@3.0.5 with yarn
• Diffing your files with clean files
✔ Created file patches/sass-graph+3.0.5.patch

ログのようにpatches/sass-graph+3.0.5.patchファイルが作成されていれば正常に動作しています。

最後にpackage.jsonにpostinstallコマンドを追加して、yarn install実行後に自動的にパッチが適用されるようにします。package.jsonのscriptsに"postinstall": "patch-package"を追加します。

完成したpackage.jsonは以下のようになります。

{
  "scripts": {
    "postinstall": "patch-package"
  },
  "devDependencies": {
    "gulp": "^4.0.2",
    "gulp-autoprefixer": "^8.0.0",
    "gulp-cached": "^1.1.1",
    "gulp-dart-sass": "^1.0.2",
    "gulp-debug": "^4.0.0",
    "gulp-notify": "^4.0.0",
    "gulp-plumber": "^1.2.1",
    "gulp-sass-parent": "^2.0.2",
    "gulp-sourcemaps": "^3.0.0",
    "patch-package": "^6.4.7",
    "postinstall-postinstall": "^2.1.0"
  }
}

package.jsonを修正後に、ターミナルでyarn installコマンドを実行すると以下のようになります。
(node_modules/sass-graph/parse-imports.js を元に戻して実行するとファイルが変更されるので分かりやすいです)

yarn install

# 実行結果ログ
yarn install v1.22.10
[1/4] Resolving packages...
success Already up-to-date.
$ patch-package
patch-package 6.4.7
Applying patches...
sass-graph@3.0.5 ✔
Done in 0.41s.

Applying patches… .sass-graph@3.0.5 ✔. と表示されていればパッチが正常に適用されたことになります。

終わりに

どちらの方法も知っていれば何かの役に立つと思いますが、依存が少ないならresolutionsで対応するのが簡単なのでこちらの方法がオススメです。

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

コメント

コメントする

目次