Tumblr テーマを gulp でつくるタスクレシピ


## はじめに

今回書くのは、 **gulpfile.js** の仕様と中身です。技術的な話はないし メモのようなものです。

## Gulp で解決したいタスク

Tumblr をターゲットにした上のタスクですが、今回は **reac**t を使いたいので 純粋なものではないです悪しからず。

* **stylus** を使う
* **browserify** (**babelify**) を使う
* minify したものを index.html に inline 展開する
* **watchify** で差分監視する(**react** 周辺のコンパイルはくっそ時間かかるので)

#### なぜ Gulp ???

もともと私は **assemble** をしたくて **grunt** を使い続けていたんですが、

今朝方 http://mizchi.hatenablog.com/entry/2015/05/11/182118 に grunt は開発が止まっているという悲報が。。

となれば、 gulp するしかねえ！！(でも レポジトリのコミットみたら作者も頻繁ではないですがコミットはしている模様？ただ内容的にはもう保守的な感じなのでしょうか・・・)

そういう意味で。 gulp 初心者なのです。

## フォルダ構造

node module も晒す。 まぁケースによっては増えたり減ったり。

notify とかは 完全に個人趣味。


```
.
├── build
│   ├── css
│   ├── index.html
│   └── js
├── gulpfile.js
├── inlined
│   └── index.html
├── node_modules
│   ├── autoprefixer-stylus
│   ├── babelify
│   ├── browserify
│   ├── del
│   ├── gulp
│   ├── gulp-inline-source
│   ├── gulp-notify
│   ├── gulp-plumber
│   ├── gulp-stylus
│   ├── normalize.css
│   ├── plumber
│   ├── run-sequence
│   ├── vinyl-source-stream
│   └── watchify
├── package.json
└── src
    ├── css
    ├── index.html
    └── js
```

流れとしては

1. src/ から build/ に　js/app.js css/style.css index.html をつくる
2. build/ から tumblr にペーストできるように html を吐き出す。

ですね。

## gulpfile.js

長い。汚い。とりあえず。


```
var gulp = require('gulp');
var source = require('vinyl-source-stream');

var stylus = require('gulp-stylus');
var autoprefixer = require('autoprefixer-stylus');

var inline = require('gulp-inline-source');

var browserify = require('browserify');
var watchify = require('watchify');
var babelify = require('babelify');

var del = require('del');

var plumber = require('gulp-plumber');
var notify = require('gulp-notify');

var sequence = require('run-sequence');

gulp.task('default', ['init'] );

// init
gulp.task('init', function() {
  return sequence(
    ['stylus', 'html'],
    ['watch', 'browserify'],
    'inline'
  );
});

//clean build, inlined Folder
gulp.task('clean', function() {
  del(['build', 'inlined'], function() {
    console.log('finished');
  });
});

// stylus
gulp.task('stylus', function() {
  return gulp.src('./src/css/style.styl')
    .pipe(plumber({
      errorHandler: notify.onError({
        title: "Stylus Error",
        message: "<%= error.message %>"
      })
    }))
    .pipe(
      stylus({
        use: [
          function() { autoprefixer({browsers: ['ie 9']}) }
        ]
      })
    )
    .pipe(gulp.dest('./build/css'));
});

// html copy
gulp.task('html', function() {
  gulp.src('./src/index.html')
    .pipe(gulp.dest('./build'));
});

// inline js, css
gulp.task('inline', function() {
  gulp.src('./build/index.html')
    .pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
    .pipe(inline())
    .pipe(gulp.dest('./inlined/'));
});

// JS compile
gulp.task('browserify', function() {
  var bundler = browserify({
    entries: ['./src/js/app.js'],
    transform: [babelify],
    debug: true,
    cache: {}, packageCache: {}, fullPaths: true
  });

  var watcher  = watchify(bundler);

  return watcher
    .on('update', function() {
      var updateStart = Date.now();
      console.log('JS compile!');
      watcher
        .bundle()
        .on('error', notify.onError({
            title: "JS Error",
            message: "<%= error.message %>"
        }))
        .pipe(source('app.js'))
        .pipe(gulp.dest('./build/js'));
      console.log('JS compiled!', (Date.now() - updateStart) + 'ms');
    })
    .bundle() // Create the initial bundle when starting the task
    .on('error', notify.onError({
      title: "JS Error",
      message: "<%= error.message %>"
    }))
    .pipe(source('app.js'))
    .pipe(gulp.dest('./build/js'));
});

// watch html, css files
gulp.task('watch', function() {
  gulp.watch( ['./src/css/**/*.styl'], ['stylus'] );
  gulp.watch( ['./src/build/**/*'], ['inline'] );
});

```

## タスク説明

#### default

```
gulp.task('default', ['init'] );
```

init を呼びます。 関数として直接定義しても良かったですが、後々変えようとしたときにダルいだろうと思い。こういう感じ

#### init

```
gulp.task('init', function() {
  return sequence(
    ['stylus', 'html'],
    ['watch', 'browserify'],
    'inline'
  );
});
```

基本立ち上げ時に呼びたいタスクコール集。
inline の処理と絡めるとどうしてもシーケンス化する必要がありますね。
順序としては

1. stylus 変換　と html のコピー
2. watch 系のタスクを呼び出し
3. inline 化する

#### clean

```
gulp.task('clean', function() {
  del(['build', 'inlined'], function() {
    console.log('finished');
  });
});

```

一旦フォルダー類を綺麗にしたいときに特別に呼び出します。
本当は init タスクの中に含めたかったんですが、どうやら、clean で使用している　del は rm の実行コマンドを出した時点で next 処理が回って コールバックしているみたいで、、、他の処理(僕の場合は stylus )とかち合うと必ずエラーがでるという。。。(本当か？教えてエロい人)

#### stylus

```
gulp.task('stylus', function() {
  return gulp.src('./src/css/style.styl')
    .pipe(plumber({
      errorHandler: notify.onError({
        title: "Stylus Error",
        message: "<%= error.message %>"
      })
    }))
    .pipe(
      stylus({
        use: [
          function() { autoprefixer({browsers: ['ie 9']}) }
        ]
      })
    )
    .pipe(gulp.dest('./build/css'));
});
```

stylus には autoprefixer をつかう。 gulp-autoprefixer ってのもあるけど。
grunt 時代からの流れで、この使い方で。

#### html

```
gulp.task('html', function() {
  return gulp.src('./src/index.html')
    .pipe(gulp.dest('./build'));
});
```
html のコピー。何かしらのテンプレートエンジンを使うなら、ここに追記しよう。

#### inline

```
gulp.task('inline', function() {
  return gulp.src('./build/index.html')
    .pipe(plumber({errorHandler: notify.onError('<%= error.message %>')}))
    .pipe(inline())
    .pipe(gulp.dest('./inlined/'));
});
```

inline 化させる。一応 error に渡してるけど。ほぼ起こることないので外してもよし。

#### JS 

```
gulp.task('browserify', function() {
  var bundler = browserify({
    entries: ['./src/js/app.js'],
    transform: [babelify],
    debug: true,
    cache: {}, packageCache: {}, fullPaths: true
  });

  var watcher  = watchify(bundler);

  return watcher
    .on('update', function() { // When any files update
      var updateStart = Date.now();
      console.log('JS compile!');
      watcher
        .bundle() // Create new bundle that uses the cache for high performance
        .on('error', notify.onError({
            title: "JS Error",
            message: "<%= error.message %>"
        }))
        .pipe(source('app.js'))
        .pipe(gulp.dest('./build/js'));
      console.log('JS compiled!', (Date.now() - updateStart) + 'ms');
    })
    .bundle()
    .on('error', notify.onError({
      title: "JS Error",
      message: "<%= error.message %>"
    }))
    .pipe(source('app.js'))
    .pipe(gulp.dest('./build/js'));
});

```

browserify は watchify を使用するので少し違う形態で書く。
この中で watch も同時に行うのがミソ。
ちなみにこのコードは参照元を大いに丸コピ。。。参考にしました。

http://christianalfoni.github.io/javascript/2014/08/15/react-js-workflow.html

plumber は　browserify の中のエラーをハンドリングできないらしく有名。
そうなら、他でも使わずに書く選択肢もあるが、一応両方やろう。初めてだし。

ちなみに　notify でなくてもよいが error をハンドリングして適切な処置(emit('end')だったかしら)してあげないと、error したあと タスク全体 が死ぬ。ので何かしら処理をかいてあげよう。

#### watch

```
gulp.task('watch', function() {
  gulp.watch( ['./src/css/**/*.styl'], ['stylus'] );
  gulp.watch( ['./src/build/**/*'], ['inline'] );
});
```

css と inline 化の watch タスク。
js 系は先に書いた通り、　watchify の処理系に任せる。

##感想

* 疲れた。これ作るのに 3 時間はかかった。慣れたいですね。
* 初心者というか、node に慣れてない人にはとっつきにくいかも。でもやるしかない。私もできた。
* notify は良い。 別のプロジェクトは　beep させたりしてたけど、こっちでいいかな。
* ソースが汚い。。。書き手の問題でもある。 普段使わないにしても　こういうファイルは　coffee で書くのがいいんだろう。
* gulp の方が grunt に比べて柔軟ぽい！気がする。これから積極的にやっていく価値はありそうだと感じた。

##XenobladeX について

http://mizchi.hatenablog.com/entry/2015/05/11/182118

にあるが、僕も　GW の大半と有給をコンボにしてこのゲームをクリアした。
楽しかった。
初めてといっていいが オープンワールド ゲームをして、ほげえええとなったほど感じた。
ストーリーはブログにもあるような点を感じたが概ね満足である。
とにかくボリュームがあるゲームだなと思った。
私は任天堂とは全く関係のない人間だが、皆さん買って盛り上げて今後のxenoシリーズを応援しましょう！
というか　xenogears が好きなんだな。