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 系のプラグインを組み合わせれば、この画像パイプラインの延長として追加できます。
