フロントエンドの設計から実装まで〜タスクランナー編〜

フロントエンドの設計から実装までした時に考えたことやコードをまとめる

今回はタスクランナー編

デザインパターンは[Flux](https://github.com/facebook/flux)、javascriptのlibraryは[riot.js](http://riotjs.com/)を使っていますが、基本的な思想部分はどのデザインパターンでも変わらないと思います。

GitHub
- [riot_flux_study](https://github.com/takeokunn/riot_flux_study)(まだ研究中、こっちを解説)
- [angular_flux_study](https://github.com/takeokunn/angular_flux_study)(angularjs版、yeomanで生成したのでgulpfileは書いてない・・・・）

前提知識として読んでおいて欲しい記事→[エンジニア視点から見るWebサービスの全体像](http://qiita.com/takeokunnn/items/9b0bcdde411b94775a5f)

全部を説明するのは~~めんどくさい~~尺的に難しいので詳しくはGitHubのソースコードをみてください

[gulpfile.js](https://github.com/takeokunn/riot_flux_study/blob/master/gulpfile.js)

[gulp/*.js](https://github.com/takeokunn/riot_flux_study/tree/master/gulp)

## 目標

タスクランナー`gulp`を使ってコンパイルやコード生成、各種サーバーの起動を全部自動化する。

公開用ファイルを生成する。

## そもそもなんでタスクランナーを使うの？

結論から言うと、**めんどくさい**からだ。

いちいち`ES6`を`javascript`に、`scss`を`css`に、`riot.js`の`tag`を`javascript`にコンパイルするコマンド打ってサーバーをリロードして変更を確認するのが非常にめんどくさい。

また、それぞれのソースコードを圧縮したり難読化するのも手動でやると非常にめんどくさい。

めんどくさいことは人間がやるべきことじゃない

## ファイル構造

ファイル構造は図のとおりだ

![スクリーンショット 2016-07-11 10.13.43.png](https://qiita-image-store.s3.amazonaws.com/0/125325/da9a9f44-0cb7-db37-c60d-6aa09cb769fe.png)

```js:gulpfile.js
'use strict';

const
	gulp = require('gulp'),
	read = require('fs-readdir-recursive')
;

/**
 * read files in ./gulp/*.js
 */
read('./gulp')
	.filter((file_) => {
		return (/\.js$/i).test(file_);
	})
	.map((file_) => {
		require('./gulp/' + file_);
	});

/**
 * for develop
 */
gulp.task('default', () => {
	gulp.start('build:tmp');
});

/**
 * for distribution
 */
gulp.task('dist', () => {
	gulp.start('build:dist');
});

```

gulpfile.jsは上のように書いた。`fs-readdir-recursive`で`gulp/*.js`のソースコードを読み込み、それぞれのタスクを実行するような感じだ。

## タスクフロー（開発用）

開発用タスクのフローは以下のようになっている。

```js:gulp/build.tmp.js
/**
 * build .tmp folder and create some server
 */
gulp.task('build:tmp', () => {

	return runSequence(
		'clean:tmp',
		'copy:html:tmp',
		['copy:assets:tmp', 'tag', 'style:tmp'],
		'script:tmp',
		'inject:tmp',
		['server:start:tmp', 'watch:tmp', 'easymock:tmp']
	);
});

```

1. `.tmp`のディレクトリを全て削除する
2. `src/index.html`を`.tmp/server/index.html`にコピーする
3. `src/assets`を`.tmp/server/assets`にコピーする
4. `src/app/components/**/*.tag`を`src/app/compoents/**/*.js`に変換する
5. `src/app/**/*.scss`を`css`に変換して結合、`./tmp/server/app/index.css`に置く
6. `src/app/**/*.js`を`babel`でコンパイルして結合、`.tmp/server/app/index.module.js`に置く
7. `.tmp/server/index.html`に`bower`でインストールしたライブラリと先ほどコンパイルして設置した`js`と`css`を挿入する
8. 開発用サーバーを起動する
9. `src/**/*.(js|scss|tag|html)`を監視して、変更が感知されたら反映する
10. 擬似APIサーバーを起動する

3,4,5と8,9,10は並列処理をしています。

## タスクフロー（公開用）

```js:gulp/build.dist.js
/**
 * build dist folder
 */
gulp.task('build:dist', () => {

	return runSequence(
		'clean:dist',
		['copy:html:dist', 'copy:assets:dist', 'tag', 'style:dist', 'bower:dist'],
		'script:dist',
		'inject:dist',
		'server:dist'
	);
});
```

1. `dist`のディレクトリを削除する
2. `src/index.html`を`dist/index.html`にコピーする
3. `src/assets/`を`dist/assets`にコピーする
4. `src/app/components/**/*.tag`を`src/app/compoents/**/*.js`に変換する
5. `src/app/**/*.scss`を`css`に変換して最小化して結合、`dist/css/index.min.css`に置く
6. `bower`でインストールしたものを難読化し結合、`dist/js/bower.js`に置く
7. `src/app/**/*.js`を`babel`でコンパイルして難読化し結合、`dist/index.min.js`に置く
8. `dist/index.html`に`dist/(js|css)/*.(js|css)`を読み込む
9. 公開用サーバーを起動する

## 苦労したところ解説

* tagをjsにコンパイル

同じファイルにtagからjsにコンパイルしたものを置きたいときの処理

```js:gulp/tag.js
/**
 * concat my tag file and compile tag into javascript and put in .tmp/server/app/index.tag.js
 */
gulp.task('tag', () => {

	return gulp
		.src(path.join(conf.paths.src, '/app/components/**/*.tag'))
		.pipe(plumber(conf.plumber))
		.pipe(riot({type: 'babel'}))
		.pipe(babel({presets: ['es2015-riot']}))
		.pipe(gulp.dest((file_) => {
			return file_.base;
		}))
	;
});
```

の

```js

.pipe(gulp.dest((file_) => {
	return file_.base;
}))

```
が悩んだ

## 最後に

試行錯誤して作ったものなので必ずしも正しい知識だとは限りません。指摘批判をコメントでしていただけると幸い

## p.s.

gulp超楽しい