gulpでSassコンパイルと画像圧縮のビルドタスクを組む

概要

静的サイトやCMSテーマの制作では、Sass(SCSS)のコンパイルと画像の最適化が毎回発生します。手作業でやっていると時間がかかり、やり忘れも起きがちです。この記事では、gulp(5系)を使って以下を1つのビルドパイプラインにまとめる手順を、ハンズオン形式で解説します。

  • SCSS → CSS のコンパイル
  • ベンダープレフィックスの自動付与
  • CSS の圧縮(minify)
  • ソースマップの出力
  • 画像(PNG / JPEG / GIF / SVG)の圧縮
  • ファイル変更の監視(watch)

Vite や Webpack が主流の今でも、「扱うのは CSS と画像がメイン」という静的コーディングやテーマ開発では、軽量に組める gulp が有効な選択肢になります。

環境

  • Node.js 20 / 22 / 24 のいずれかの LTS
  • gulp 5
  • npm

※バージョンは執筆時点のものです。npm install gulp で入るのは現在 gulp 5 系です。直列・並列実行を series / parallel で記述する点は gulp 4 と同じで、src / dest / series / parallel の公開 API は 4→5 で変わっていません。そのため本記事の gulpfile は gulp 5 でもそのまま動きます。

一点だけ注意があります。後述する gulp-imagemin が現在は ESM 専用(pure ESM) になっているため、本記事の gulpfile は ESM(import 構文)で書きます。ESM を扱う都合上、Node は新しめの LTS(20 以上)を前提にしてください。なお Node 18 は 2025 年 4 月に EOL 済みで、現在は推奨されません。

ディレクトリ構成

project/
├── gulpfile.js
├── package.json
├── src/
│   ├── scss/
│   │   ├── style.scss
│   │   └── _variables.scss
│   └── images/
└── dist/        ← ビルド結果
    ├── css/
    └── images/

起きがちな問題

手作業運用では次のような問題が発生します。

  • SCSS を保存するたびにコンパイルし忘れて「反映されない」と悩む
  • プレフィックスの付け忘れで一部ブラウザだけ崩れる
  • 無圧縮画像をコミットして転送量が膨らむ
  • 複数人作業でビルド手順がばらつく

これらは「保存したら自動でビルドされる」状態を作ることで解消できます。

セットアップ

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

npm init -y
npm install --save-dev gulp \
  gulp-sass sass \
  gulp-autoprefixer \
  gulp-clean-css \
  gulp-sourcemaps \
  gulp-imagemin \
  gulp-plumber
パッケージ 役割
gulp タスクランナー本体(5系)
gulp-sass + sass Dart Sass でコンパイル
gulp-autoprefixer プレフィックス自動付与
gulp-clean-css CSS 圧縮
gulp-sourcemaps ソースマップ出力
gulp-imagemin 画像圧縮(現行は ESM 専用)
gulp-plumber watch の異常終了防止

gulpfile を ESM で書くため、package.json に "type": "module" を指定します。

{
  "type": "module"
}

"type": "module" を足したくない場合は、gulpfile のファイル名を gulpfile.mjs にしても ESM として扱われます。

現在の gulp-sass は内部で使う Sass コンパイラを自分で渡す形式です。node-sass は非推奨のため、Dart Sass(sass パッケージ)を使ってください。

CommonJS のまま書きたい場合の逃げ道

事情があって require 形式(CommonJS)を維持したい場合は、gulp-imagemin が CommonJS で提供されていた最後のバージョンである 7.1.0 を固定してください。

npm install --save-dev gulp-imagemin@7.1.0

v8.0.0 以降は ESM 専用になっており、const imagemin = require('gulp-imagemin') のままだと ERR_REQUIRE_ESM で落ちます。新規に組むなら、後述の ESM 版で書くのがおすすめです。

実装例

gulpfile.js を作成します(package.json に "type": "module" がある前提の ESM 版です)。

import { src, dest, watch, series, parallel } from 'gulp';

import gulpSass from 'gulp-sass';
import * as dartSass from 'sass';
import autoprefixer from 'gulp-autoprefixer';
import cleanCSS from 'gulp-clean-css';
import sourcemaps from 'gulp-sourcemaps';
import imagemin from 'gulp-imagemin';
import plumber from 'gulp-plumber';

const sass = gulpSass(dartSass);

const paths = {
  scss: {
    src: 'src/scss/**/*.scss',
    dest: 'dist/css',
  },
  images: {
    src: 'src/images/**/*.{png,jpg,jpeg,gif,svg}',
    dest: 'dist/images',
  },
};

// Sass コンパイル
function styles() {
  return src(paths.scss.src)
    .pipe(plumber())
    .pipe(sourcemaps.init())
    .pipe(sass({ outputStyle: 'expanded' }).on('error', sass.logError))
    .pipe(autoprefixer())
    .pipe(cleanCSS())
    .pipe(sourcemaps.write('./'))
    .pipe(dest(paths.scss.dest));
}

// 画像圧縮
function images() {
  return src(paths.images.src)
    .pipe(plumber())
    .pipe(imagemin())
    .pipe(dest(paths.images.dest));
}

// 監視
function watchFiles() {
  watch(paths.scss.src, styles);
  watch(paths.images.src, images);
}

const build = parallel(styles, images);
const dev = series(build, watchFiles);

export { styles, images, build };
export { dev as watch, dev as default };

ポイントは、gulp-sass / gulp-imagemin などを import で読み込むところです。gulp-sass はデフォルトエクスポートされたファクトリ関数に Dart Sass を渡して使うため、gulpSass(dartSass) の形にしています。公開 API(src / dest / series / parallel)自体は gulp 4 と同じなので、タスクの組み立て方は従来どおりです。

対応ブラウザの指定

gulp-autoprefixer は browserslist を参照します。package.json に以下を追加して、対応ブラウザをプロジェクト全体で一元管理します。

{
  "browserslist": [
    "> 0.5%",
    "last 2 versions",
    "not dead"
  ]
}

圧縮とソースマップの両立

CSS を圧縮すると行番号が潰れ、DevTools でのデバッグがしづらくなります。sourcemaps.init() を sass() の前に、sourcemaps.write() を最後に置くことで、圧縮後でも元の SCSS の行までたどれます。

watch を落とさない工夫

gulp-plumber と sass.logError を入れておくと、SCSS の文法エラーが出ても監視プロセスが止まらず、エラー内容だけが表示されます。これが無いと、タイプミスのたびに watch を再起動することになります。

実行方法

# 1回だけビルド
npx gulp build

# 監視モード(保存するたびに自動ビルド)
npx gulp

package.json に scripts を定義しておくと便利です。

{
  "scripts": {
    "build": "gulp build",
    "watch": "gulp"
  }
}

確認方法

  • dist/css/style.css が生成され、圧縮されていること
  • dist/css/style.css.map が出力され、DevTools の Sources で元 SCSS が表示されること
  • dist/images/ の画像サイズが元より小さくなっていること
  • watch 起動中に SCSS を保存すると、自動で CSS が更新されること

つまずきポイント

  • gulp-imagemin の ERR_REQUIRE_ESM:現行(v8 以降)は ESM 専用です。require('gulp-imagemin') のままだと落ちます。gulpfile を ESM(import)にするか、CommonJS を維持するなら gulp-imagemin@7.1.0 を固定します。
  • コンパイラ未指定エラー:gulpSass(dartSass)(CommonJS なら require('gulp-sass')(require('sass')))のように Sass コンパイラを渡す形式。古い記法だと動きません。
  • gulp 3 系の情報が混ざる:gulp.task('build', ['styles']) の配列指定は gulp 4 / 5 では使えません。series / parallel を使います。
  • watch が止まる:plumber + logError で対処します。
  • SVG の見た目変化:imagemin が属性を削ることがあるため、アイコン系は出力を目視確認します。

まとめ

gulp を使えば、Sass コンパイルと画像圧縮を「保存するだけで走る」状態にできます。要点は3つです。

  • gulp 5 の series / parallel でタスクの直列・並列を明示する(API は gulp 4 と互換)
  • plumber + logError で watch を落とさない
  • sourcemaps を挟んで圧縮後もデバッグできるようにする

軽量なビルドツールとして、CSS と画像を扱う静的コーディングやテーマ開発では今でも十分現役です。まずは最小構成で組み、必要に応じて JS バンドルや WebP 変換などを足していくのがおすすめです。WebP への変換・最適化は imagemin 系のプラグインを組み合わせれば、この画像パイプラインの延長として追加できます。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。