p.c.c. blog

メディア運営や請負ウェブ制作などを通して得た
経験や情報を
その場で消費せずアウトプットする
ための場としてのブログ。

gulp初心者だった自分が試行錯誤を重ね、遅咲きながら現段階個人的に最強のgulpfile.jsが完成したので、晒します。
自分のようなgulp導入初期の方の参考になればと思います。

gulpのファイル構成

まずはファイル構成ですが、以下のようになっています。

ファイル構成


project
  ├── node_modules
  ├── public_html
  ├── resource
  │   ├── **/*.html
  │   └── common
  │       ├── css
  │       ├── images
  │       ├── js
  │       ├── sass
  ├── gulpfile.js
  ├── package.json
  ├── .csscomb.json

こちら、構成としてはresourceフォルダがいわゆる作業用ファイル群で、public_htmlがgulpで出力した納品用ファイルとなります。
なので、当然ですがpublic_htmlはほぼ触りません。

つまり、resourceフォルダとpublic_htmlフォルダの中身って、ほぼ一緒になります。
以前は同一フォルダにgulp.destしてたんですが、sassフォルダが邪魔だったんです。
作業用と納品用で分けることでsassフォルダは除外し、完璧にクリーンな状態で納品ファイルを用意しつつ作業することができます。
このほうが精神衛生上よかった。それだけの理由ですが、多分今後色々カスタムする上でも分けてしまった方が間違いないかと思います。

package.json

package.jsonにはプロジェクト名やら作者名やらが色々かけます。WordPressで言うところのテーマのstyle.cssみたいなもんです。
ただ、大事なのはdevDependenciesで、プラグイン(とそのバージョン)の情報です。

package.json


{
  "name": "PROJECT NAME",
  "version": "1.0.0",
  "author": "",
  "devDependencies": {
    "browser-sync": "^2.12.10",
    "gulp": "^3.9.1",
    "gulp-autoprefixer": "^3.1.0",
    "gulp-cached": "^1.1.0",
    "gulp-changed": "^1.3.0",
    "gulp-csscomb": "^3.0.7",
    "gulp-notify": "^2.2.0",
    "gulp-plumber": "^1.1.0",
    "gulp-rename": "^1.2.2",
    "gulp-sass": "^2.3.1",
    "gulp-uglify": "^1.5.3",
    "gulp-watch": "^4.3.6"
  }
}

$ npm install で、ここにあるプラグインがスパッとローカルにインストールされます。
ちなみに個別インストール時の「--save-dev」ですが、これつけるとpackage.jsonに勝手に書き込んでくれるので自分の中で必須です。
個別にアンインストール時も同じですね。 $ npm uninstall xxxxxxxxx --save-dev

gulpfile.js

gulpfile.jsの基本構成は以下になります。
各細かい内容はなるべくコメントで書きました。

gulpfile.js


var gulp = require('gulp');
var browserSync = require('browser-sync');
var sass = require('gulp-sass');
var plumber = require('gulp-plumber');
var csscomb = require('gulp-csscomb');
var notify  = require('gulp-notify');
var autoprefixer = require('gulp-autoprefixer');
var uglify = require('gulp-uglify');
var rename = require('gulp-rename');
var watch = require('gulp-watch');
var changed = require('gulp-changed');
var cache = require('gulp-cached');

var destDir = 'public_html/'; // 出力用ディレクトリ
var assetsDir = 'common/';    // 案件によってcommonとかassetsとかあるんでとりあえず変数にした

gulp.task('browser-sync', function() {
  browserSync({
    server: {
      baseDir: destDir
    }
  });
});

gulp.task('sass', function () {
  return gulp.src(['resource/'+assetsDir+'sass/**/*.scss'])
    .pipe(plumber({ // gulp-plumberを咬ますとエラー時にgulpが止まらない。
        errorHandler: notify.onError('Error: <%= error.message %>') // gulp-notifyでエラー通知を表示
    }))
    .pipe(sass()) // gulp-sassでコンパイルし、
    .pipe(autoprefixer({ browsers: ['last 2 versions', 'Android 3', 'ie 9'] })) // autoprefixerかけて、(対応ブラウザ。案件によって変更する)
    .pipe(csscomb()) // gulp-csscombで整形してあげて、
    .pipe(gulp.dest('resource/'+assetsDir+'css/')) // とりあえずresource側cssフォルダに吐き出す。
});
gulp.task( 'css',function() {
    return gulp.src('resource/**/*.css')
    .pipe( cache('css-cache')) // cssをキャッシュさせつつ、
    .pipe( gulp.dest( destDir )) // destDirに出力して、
    .pipe( browserSync.stream()) // browser-syncで反映させる。
});

// jsの圧縮リネーム
gulp.task('jsmin', function () {
  gulp.src(['resource/'+assetsDir+'js/**/*.js',
    '!resource/'+assetsDir+'js/**/*.min.js']) // jQueryなどの、すでに.minなjsは除外する。
  .pipe(plumber()) // gulp-plumberを咬ますとエラー時にgulpが止まらない(cssみたいにgulp-notify書いてもエラー通知が何故か出ないのでそのまま)。
  .pipe(changed(destDir+assetsDir+'js/')) // 変更されたjsのみをgulp.dest対象にする。
  .pipe(uglify({preserveComments: 'some'})) // uglifyでjsを圧縮するがライセンス表記を残す。
  .pipe(rename({
      suffix: '.min'
  })) // .min付与
  .pipe(gulp.dest('resource/'+assetsDir+'js/')) // jsもとりあえずresource側jsフォルダに吐き出す。
});
gulp.task( 'js',function() {
    return gulp.src('resource/**/*.js')
    .pipe( cache('js-cache')) // jsをキャッシュさせつつ、
    .pipe( gulp.dest( destDir )) // destDirに出力して、
    .pipe( browserSync.stream()) // browser-syncで反映させる。
});

gulp.task( 'copyResource',function() {
    return gulp.src(['resource/**/*','!resource/'+assetsDir+'sass/','!resource/'+assetsDir+'sass/*.scss']) // sassディレクトリ以外の全ファイルを対象にし、
    .pipe( cache('resource-cache')) // キャッシュさせて、
    .pipe( gulp.dest( destDir )) // destDirに出力して、
    .pipe( browserSync.stream()) // browser-syncで反映させる。
});

// gulp-watchで監視
// ['browser-sync','copyResource','sass','jsmin']を実行してからdefaultとして内容を実行。
// gulp-watchを使うとフォルダに追加したファイルも対象に監視してくれるのでgulp再実行の必要がない。
gulp.task('default',['browser-sync','copyResource','sass','jsmin'],function(){
    watch(['resource/**/*.+(jpg|jpeg|gif|png|html|php)'], function(event){
        gulp.start(['copyResource']); // css,sass,js以外に変更があったら実行。
    });
    watch(['resource/**/*.scss'], function(event){
        gulp.start(['sass']); // sassに変更があったら実行。cssを吐き出すので下のwatchが動く。
    });
    watch(['resource/**/*.css'], function(event){
        gulp.start(['css']); // cssに変更があったら実行。つまりsassを変更したらセットで実行となる。
    });
    watch(['resource/**/*.js'], function(event){
        gulp.start(['jsmin']); // jsに変更があったら実行。.minしたjsを吐き出すので下のwatchが動く。
    });
    watch(['resource/**/*.min.js'], function(event){
        gulp.start(['js']); // .min.jsに変更があったら実行。つまりjsを変更したらセットで実行となる。
    });
});

.csscomb.json(gulp-csscombの設定ファイル)

ここでちょっと話がずれますが、もともとcompassを使っていたことと、cssのminifyの種類が4つ(?)ほどあったので納品時便利かなとか思ったこともあって、gulp-ruby-sassを使おうとおもったことがありました。
gulp-ruby-sassは、書式はググっても古いものの方が多いわコンパイル遅いわそもそもminifyの形がダサいわ見づらいわでイライラ。
そこで、以前の同僚のN澤くんがかつて「cssを綺麗に整形できるんすよ〜」とか言っていたことを思い出し、半信半疑で使ったら信じられないくらいCSSが自分好みに出力できることがわかり、1日でgulp-ruby-sassを放棄。gulp−sass & gulp-csscombが最高です。
ただ、完全に圧縮したい時はgulp-cssminを使うと早いのでそちらをお勧めします。
ということで.csscomb.jsonの例です。
ただ、完璧なジェネレータがあるのでそちらを使って、案件や自分好みにする方が良いと思います。ポチポチ押すだけで簡単なんで。
あと個人的にcompassは、もう滅んだら良いと思っています。

.csscomb.json


{
    "always-semicolon": true,
    "color-case": "lower",
    "block-indent": "\t",
    "color-shorthand": true,
    "element-case": "lower",
    "eof-newline": true,
    "leading-zero": true,
    "quotes": "single",
    "space-before-colon": "",
    "space-after-colon": "",
    "space-before-combinator": " ",
    "space-after-combinator": " ",
    "space-between-declarations": " ",
    "space-before-opening-brace": " ",
    "space-after-opening-brace": "\n\t",
    "space-after-selector-delimiter": " ",
    "space-before-selector-delimiter": "",
    "space-before-closing-brace": "\n",
    "remove-empty-rulesets": true,
    "space-between-declarations": "\n",
    "strip-spaces": true,
    "unitless-zero": true
}

gulpについて思うこと

$ gulp したら、全部タイムスタンプ更新される。これってなんとかならないのかなと。
作業中は gulp-changed と gulp-cached で更新されたファイルのみ出力されるけど、数日間の作業の差分をまとめる時などは不便。というか記憶と目視になるのであまりよくないですよね、皆さんはどうされてますか?

株式会社p.c.c.

フロントエンドの下請け・請負制作をはじめ、多数のウェブコンテンツを自社で企画・制作・運営している会社です。企業の外部ウェブ担当として顧問契約も行っています。