僕なりのフロントエンド開発環境

ちょっと古くなってきたので、タスクをアップデートした
http://qiita.com/koh110/items/83cb29f8ecc2738ea8bb

---

最近フロントエンドよりの開発をする事が多く、そろそろ面倒くさい部分を自動化せねばなぁと思ってgulpのタスクなどを色々まとめて、僕の考えた最強のフロントエンド開発環境を整えた。
随時アップデートはしていくつもりだけど、しばらくはこれを使ってみようと思う。
ひと通りは以下のリポジトリにまとめた。
https://github.com/koh110/minjsapp

簡単に他のリポジトリで利用できるようにファイルだけcopyするシェルスクリプト付き

```
$ git clone https://github.com/koh110/minjsapp.git
$ cd minjsapp
$ ./copy.sh /path/to/yourproject
```

# 各種自動化
[gulp](https://www.npmjs.com/package/gulp)
[gulp-load-plugins](https://www.npmjs.com/package/gulp-load-plugins)
軽く触ってみた感じだとGruntに比べてタスクが書きやすいなと感じたので、こちらを導入。
そういえばyeomanってすっかり聞かなくなったなぁ……
gulp.jsをかけばすごく簡単に色んな処理を自動化できる。
実行も簡単で以下の例だと、hogeというタスクとデフォルトで起動するタスクを設定している。
hogeタスクを起動するとコンソールにhogeが出力される。
デフォルトタスクにはhogeタスクを設定しているので、gulpと打つだけでhogeタスクも起動する。
デフォルトで実行したいタスクはここに配列でどんどんつっこんでいくだけ。

```gulp.js
'use strict';

var gulp = require('gulp');

// 自分で書いた自動化タスク
gulp.task('hoge', function() {
  console.log('hoge');
});

gulp.task('fuga', function() {
  console.log('fuga');
});

// デフォルトで起動するタスク
gulp.task('default', ['hoge', 'fuga']);

```

```
$ gulp hoge
> hoge

$ gulp
> hoge
> fuga
```

gulp-load-pluginsはgulp-ほげほげというプラグインを簡単に使えるようにしてくれるので、導入。
慣習的に$という名前をつけるらしいのでそれにならっている。

```gulp.js
var $ = require('gulp-load-plugins')();

~~
.pipe($.concat()) // みたいな感じでrequireしなくてもgulp-concatを使ったりできる
~~
```


# ブラウザテストがめんどくさい
[browser-sync](https://www.npmjs.com/package/browser-sync)
[run-sequence](https://www.npmjs.com/package/run-sequence)  
開発中何がめんどくさいって各種ブラウザでの動作テストがめんどくさい。
根本的な解決はロジックにテストを書くことなんだろうけど、さくっと動作確認したいので、各ブラウザの操作を同期してくれるbrowser-syncをサーバにして確認。
これだけで http://localhost:8282 がデフォルトのブラウザで起動するし、chromeとfirefoxで同時に開くと、chromeでinputに文字を入力したのにfirefoxにも同じ文字が入力される、firefoxでスクロールするとchromeもスクロールされてる、みたいな動きをしてくれる。
単純に見てて面白い。

```gulp.js
// サーバ起動
gulp.task('server', function() {
  browserSync({
    port: 8282,
    server: {
      baseDir: './app/',
      index  : 'index.html'
    }
  });
});
```

jsファイルとかcssとか書きかわったらオートリロードして欲しいので、リロードのタスクを書いて、必要なファイルはwatchするタスクを書いて、その後にリロードタスクを呼ぶ感じに。
watchタスクはjsで結合処理とかのタスクを実行更新した後でリロードしたいので、run-seqenceというタスクの実行順序を担保するものを使います。  
gulp4.0になったらデフォルトでその機能が入るのでいらなくなるらしいです。はやくでて欲しい。

```gulp.js
var runSequence = require('run-sequence');

// サーバ再起動
gulp.task('reloadServer', function () {
  browserSync.reload();
});

// watch
gulp.task('watch', function() {
  gulp.watch('./app/index.html', {
    runSequence('inject', 'reloadServer');
  });
  gulp.watch(['./app/js/*.js', './app/js/controller/*.js'], function() {
    runSequence('js', 'reloadServer');
  });
  gulp.watch('./app/css/**/*.css', function() {
    runSequence('css', 'reloadServer');
  });
})
```

# gulpのタスク内でのエラー
[gulp-plumber](https://www.npmjs.com/package/gulp-plumber)
[node-notifier](https://www.npmjs.com/package/node-notifier)
jsのコンパイルやcssの連結タスクが文法ミスとかでエラーになるとgulpがそこで落ちて、gulp自体を再起動しないといけなくてストレスが溜まるのでgulp-plumberでエラーを抑制。
そしてエラーが起きた箇所でnode-notifierでデスクトップ通知をしてターミナルをいちいち見に行かなくてもタスクが失敗した事に気づけるように。

```gulp.js
// jsのlint処理
gulp.task('lint', function() {
  return gulp.src(['./app/js/*.js']) // ここでエラーが発生するとgulpが落ちて止まる
    .pipe($.plumber({
      errorHandler: function(error) {
        // ここでエラーをキャッチできる
        var title = '[task]' + taskName + ' ' + error.plugin;
        var errorMsg = 'error: ' + error.message;
        console.error(title + '\n' + errorMsg);
        // node-notifierがデスクトップ通知をしてくれる
        notifier.notify({
          title: title,
          message: errorMsg,
          time: 3000
        });
      }
    }))
    .pipe($.eslint())
    .pipe($.eslint.format())
    .pipe($.eslint.failOnError())
    .pipe($.plumber.stop());
});
```

# jsの文法を揃えるのが面倒くさい
[gulp-eslint](https://www.npmjs.com/package/gulp-eslint)
スペースの数とかインデントの空白の数とかシングルクオート、ダブルクオートとかとか……
自分で書いてても時々間違えるので指摘してもらいたい、のでeslintを導入。
jshintではなくeslintにした理由は独自ルールとかもプラグインみたいに導入できるよ、という記述を見たから。そこまで使いこなしてないけど。
linterのタスクは上にも記述しているので割愛。
.eslintrcはまだそんなにかっちり決めてない。他にはどんなルールを入れた方がいいのだろうか。

```json
{
  "env": {
    // window.~は結構使うので
    "browser": true,
    "es6": true,
    "node": true
  },
  "rules": {
    // console.logは開発中は結構使うのでwarningで留める
    "no-console": [1],
    // 使ってない変数は無駄なので教えて欲しいからwarning
    "no-unused-vars": [1, {"vars": "all", "args": "after-used"}],
    // 'use strict';はとりあえず必須にしておく
    "strict": [2, "global"],
    // indentは半角スペース2個が好き
    "indent": [2, 2],
    // シングルクオートの方が好きなので
    "quotes": [2, "single"],
    // スネークケースよりはキャメルケースで記述する方が慣れているけど、時々間違えるので統一させるためにエラーを出す
    "camelcase": [2, {"properties": "always"}],
    // スペースは1個だけ開ける
    "no-multi-spaces": [2],
    // スペースとタブが入り混じるとエンジニアは死ぬ
    "no-mixed-spaces-and-tabs": [2],
    // if文の後の中括弧はスペースあける派なので
    "space-before-blocks": [2, "always"]
  }
}
```

# ES6で書きたい
[gulp-babel](https://www.npmjs.com/package/gulp-babel)
そろそろ時代の流れもES6になり始めてきた気がするので今のうちにES6の文法で書けるようになりたいので、ES6で書いてbabelでコンパイル。
babelでコンパイルしたものを最終的に1ファイルに連結してその1ファイルだけをhtmlで読み込めばすむようにjsのビルド用タスクを作成。
lintを通して文法がオッケーだったらjsのビルドを走らせる。
babelによるビルドが完了すると、gulp-concatで一つのファイルにまとめられて、app.jsという名前で出力される。
文法ミスとかでビルドエラーが起きても起動中のサーバが止まらないようにgulp-plumberでエラーをキャッチしておいて、node-notifyで開発中にも気づけるようにしておく。

```gulp.js
// js系処理
// es6からes5への変換 -> js連結
gulp.task('js', ['lint'], function() {
  return gulp.src(['./app/js/*.js'])
    .pipe($.plumber({
      errorHandler: function(error) {
        // えらー通知
      }
    }))
    .pipe($.babel())
    .pipe($.concat('./app/app.js'))
    .pipe($.plumber.stop())
    .pipe(gulp.dest('./app'));
});
```

# CSSのやりくりがとても面倒くさい
[gulp-pleeease](https://www.npmjs.com/package/gulp-pleeease)
[gulp-concat](https://www.npmjs.com/package/gulp-concat)
自分はここが一番面倒くさくて一番解決したかった所。
cssを自分で書くことも多いけど、デザイナーにあげてもらったものを使う事も多い。
そのため、SassやLESSに完全移行できないのがネックだった。
もらったcssはフォルダに適当に突っ込んだらいい感じにhtmlに読み込んで欲しい。

また、ベンダープレフィックスも作る人によって対応がまちまちだったりしてブラウザ感で差異が出たり、対応ブラウザを明確にどこまでというのが難しかった。
そこでpleeeaseというのを導入して、autoprefixerで自動的にベンダープレフィックスを付けてそのへんのクオリティの差を均しつつ、1ファイルにまとめてhtmlでは1ファイルだけよめばいい形にする。

```gulp.js
// css系処理
// css連結 -> autoprefixer
gulp.task('css', function() {
  return gulp.src(['./app/css/*.css'])
    .pipe($.plumber({
      errorHandler: function(error) {
        // えらー通知
      }
    }))
    .pipe($.concat('style.css'))
    .pipe($.pleeease({
      autoprefixer: {
        browsers: [
          'last 1 versions', // とりあえず最新は対応
          'ie >= 10', // IE10はサポートしないとなのでサポート
          'safari >= 8',
          'ios >= 8',
          'android >= 4'
        ]
      },
      minifier: false // minifyはそんなに好きじゃないのでとりあえずfalse
    }))
    .pipe($.plumber.stop())
    .pipe(gulp.dest('/app'));
});
```

# bowerでjs入れた後にわざわざhtmlいじるの辛い
[gulp-inject](https://www.npmjs.com/package/gulp-inject)
[main-bower-files](https://www.npmjs.com/package/main-bower-files)

利用するjsのライブラリ系は基本的にbowerで管理したいんだけど、bowerで入れた後どのファイルが必要なファイルでそいつをどのhtmlにscriptタグで埋め込んでと考えていく作業は人間じゃなくても自動でできるだろ、という訳で自動化。
gulp-injectで指定したファイルに指定した内容を自動で書き込み。
main-bower-filesでbowerから利用するファイルを抽出。
この2つを組み合わせてbowerで入れたファイルは自動でhtmlに組み込まれる機構ができます。
人間がやらなくても良い作業はどんどん自動化しちゃいましょう。

```gulp.js
// bowerで取得したファイルをindex.htmlに挿入
gulp.task('inject', function() {
  return gulp.src('./app/index.html')
    .pipe($.inject(gulp.src(mainBowerFiles()), {
      name: 'inject',
      relative: true
    }))
    .pipe(gulp.dest('./app'));
});
```
inject:jsとendinjectをhtmlファイルに書くと勝手にscriptタグで挿入してくれる。
今回は使ってないけど、inject:cssでcssようにlinkタグも挿入してくれる。


```index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>min js app</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <div>hello world</div>
  <!-- inject:js -->
  ここの間に勝手にgulp-injectが挿入する
  <script src="components/jquery/dist/jquery.js"></script>
  <!-- endinject -->
  <script src="app.js"></script>
</body>
</html>
```