1. はじめに
Gulp+Pug+SassでWebサイトの開発環境を作りました。
何度か作っているのですが、毎回忘れてしまうのでまとめです。
※ gulp自体のインストール(グローバルインストールとローカルインストール)はされている前提です。
※ npmを使っています。
2. ディレクトリ構造
dest/
├css/
├images/
└javascripts/
index.html
src/
├images/
├javascripts/
├pages/
├pug/
└sass/
gulpfile.js
package.json
3. 環境
Gulp 4.0.2
4. インストールしたパッケージ
パッケージ | 概要 |
---|---|
gulp-pug | Pugをコンパイル |
gulp-sass | Sassをコンパイル |
gulp-autoprefixer | CSSプロパティにベンダープレフィックスを付与 |
gulp-cssmin | CSSを圧縮 |
gulp-uglify | JSを圧縮 |
browser-sync | ブラウザのリロード, ローカルサーバーの立ち上げ |
gulp-plumber | エラーを検出 |
gulp-notify | デスクトップに通知 |
del | フォルダやファイルを削除 |
インストールコマンドの一覧です。
$ npm i -D gulp-pug
$ npm i -D gulp-sass
$ npm i -D gulp-autoprefixer
$ npm i -D gulp-cssmin
$ npm i -D gulp-uglify
$ npm i -D browser-sync
$ npm i -D gulp-plumber
$ npm i -D gulp-notify
$ npm i -D del
5. gulpfile.js
gulp
を実行したら以下のような処理になるようにしました。
1. destディレクトリ配下のコンテンツを丸ごと削除
srcディレクトリとdestディレクトリにあるコンテンツに差分がない状態を保ちたいので、gulp
を実行したらdestディレクトリ配下のコンテンツを丸ごと削除します。
2. 他タスクを並列で実行
1の処理が終わったら、コンパイル、ローカルサーバー起動、ファイル監視を並列で実行します。
各処理のポイント
destディレクトリ配下のコンテンツ削除
コンテンツを削除するために使用しているdel
パッケージですが、paths.dest + '/**'
としか書いていない場合、タスクの実行が成功してもコンテンツが削除されないので気をつけましょう。
※ 公式ドキュメントを読むと、paths.dest + '/**', '!' + paths.dest
のように明示的に「親ディレクトリを削除しない」と書かないと親ディレクトリも削除対象になるということが書かれているのですが、私の環境では削除自体が実行されませんでした。
//Clean
gulp.task('clean', function(done) {
del.sync(paths.dest + '/**', '!' + paths.dest);
done();
});
Sass -> CSS
SassからCSSにコンパイルする処理では、コンパイルエラーの検知・通知、ベンダープレフィックスの自動付与、CSS圧縮が行われます。
//Sass
gulp.task('css', function() {
return gulp.src([
paths.src + '/sass/**/*.scss',
'!' + paths.src + '/sass/**/_*.scss'
])
.pipe(plumber({ //エラーを検知しデスクトップ通知
errorHandler: notify.onError("Error: <%= error.message %>")
}))
.pipe(sass()) //Sass -> CSS
.pipe(autoprefixer({ //ベンダープレフィックスを付与
overrideBrowserslist: 'last 2 versions'
}))
.pipe(cssmin()) //圧縮
.pipe(gulp.dest(paths.dest + '/css'))
});
Pug -> HTML
PugからHTMLにコンパイルする処理では、コンパイルエラーの検知・通知、読みやすいコードへの整形が行われます。
//Pug
gulp.task('html', function() {
return gulp.src([
paths.src + '/pages/**/*.pug',
'!' + paths.src + '/pages/**/_*.pug'
])
.pipe(plumber({ //エラーを検知しデスクトップ通知
errorHandler: notify.onError("Error: <%= error.message %>")
}))
.pipe(pug({pretty: true})) // 読みやすいコードに整形
.pipe(gulp.dest(paths.dest))
});
JavaScript
JavaScriptは圧縮します。
//JavaScript
gulp.task('js', function() {
return gulp.src(
paths.src + '/javascripts/**/*'
)
.pipe(uglify())//圧縮
.pipe(gulp.dest(paths.dest + '/javascripts'))
});
監視
srcディレクトリ配下の各種ファイルを監視し、変更を検知したら各種ファイル形式にあわせたコンパイルタスクを実行しリロードします。
//Watch
gulp.task('watch', function () {
const reload = () => {
browserSync.reload(); //リロード
};
gulp.watch(paths.src + '/sass/**/*.scss').on('change', gulp.series('css', reload));
gulp.watch(paths.src + '/pages/**/*.pug').on('change', gulp.series('html', reload));
gulp.watch(paths.src + '/javascripts/**/*').on('change', gulp.series('js', reload));
gulp.watch(paths.src + '/images/**/*').on('change', gulp.series('image', reload));
});
サンプルコード
gulpfile.jsの全文です。
const gulp = require('gulp');
const pug = require('gulp-pug');
const sass = require('gulp-sass');
const autoprefixer =require('gulp-autoprefixer');
const cssmin = require('gulp-cssmin');
const uglify = require('gulp-uglify');
const browsersync = require('browser-sync');
const notify = require('gulp-notify');
const plumber = require('gulp-plumber');
const del = require('del');
const paths = {
src: 'src',
dest: 'dest'
};
//Pug
gulp.task('html', function() {
return gulp.src([
paths.src + '/pages/**/*.pug',
'!' + paths.src + '/pages/**/_*.pug'
])
.pipe(plumber({
errorHandler: notify.onError("Error: <%= error.message %>")
}))
.pipe(pug({pretty: true}))
.pipe(gulp.dest(paths.dest))
});
//Sass
gulp.task('css', function() {
return gulp.src([
paths.src + '/sass/**/*.scss',
'!' + paths.src + '/sass/**/_*.scss'
])
.pipe(plumber({
errorHandler: notify.onError("Error: <%= error.message %>")
}))
.pipe(sass({
outputStyle: 'expanded'
}))
.pipe(autoprefixer({
overrideBrowserslist: 'last 2 versions'
}))
.pipe(cssmin())
.pipe(gulp.dest(paths.dest + '/css'))
});
//JavaScript
gulp.task('js', function() {
return gulp.src(
paths.src + '/javascripts/**/*'
)
.pipe(uglify())
.pipe(gulp.dest(paths.dest + '/javascripts'))
});
//Image File
gulp.task('image', function() {
return gulp.src(
paths.src + '/images/**/*'
)
.pipe(gulp.dest(paths.dest + '/images'))
});
//Browser Sync
gulp.task('browser-sync', function (done) {
browsersync({
server: { //ローカルサーバー起動
baseDir: paths.dest
}});
done();
});
//Watch
gulp.task('watch', function () {
const reload = () => {
browsersync.reload(); //リロード
};
gulp.watch(paths.src + '/sass/**/*').on('change', gulp.series('css', reload));
gulp.watch(paths.src + '/pages/**/*').on('change', gulp.series('html', reload));
gulp.watch(paths.src + '/javascripts/**/*').on('change', gulp.series('js', reload));
gulp.watch(paths.src + '/images/**/*').on('change', gulp.series('image', reload));
});
//Clean
gulp.task('clean', function(done) {
del.sync(paths.dest + '/**', '!' + paths.dest);
done();
});
//Default
gulp.task('default',
gulp.series(
'clean',
gulp.parallel(
'html',
'css',
'js',
'image',
'watch',
'browser-sync'
)));
6. さいごに
毎回Gulpの設定を忘れてしまうので、今後はこの記事を更新するようにしたいと思います。