Expressの最小構成(ES2015 + Browserify + Watchify + Stylus + Jade)

Expressで簡単なプロダクトを作る時のオレオレ最小構成です。


## 要件
* サーバーサイドもクライアントサイドもES2015(ES6)で書く
  * ~~サーバーサイドはNode.jsがv4以上だとES2015で書けるはず~~  
    `import`などが使えないと知ったので、`babel-node`を使います
  * クライアントサイドJSはBrowserify + Babelifyでコンパイルする
  * 開発時はWatchifyする
  * ビルド時はUglifyする
* CSSはStylusで書く
  * StylusはExpressのMiddlewareで都度コンパイルする
  * ライブラリはkouto-swissを使う
  * ベンダープレフィックスはautoprefixerでつける
* ViewはJadeで書く
  * サーバーからJadeに変数を渡したりする
* Gulpなどのタスクランナーは使わない
  * `$ npm run xxx`でやる
  * 画像のSprite化とかするならGulp使ったほうがいいでしょう


## ディレクトリ構成
```
.
├── .gitignore
├── README.md
├── app.js
├── assets
│   ├── javascripts
│   │   ├── index.js
│   │   ├── index_dev.js
│   └── stylesheets
│       └── style.styl
├── package.json
└── views
    └── index.jade
```

ディレクトリ構成はこんな感じです。


## npm install
`$ npm init`してからインストールするNode package一覧です。
Browserifyとかは`$ npm install -g`しないと動かないかも。

```
$ npm install --save autoprefixer-stylus express jade jquery kouto-swiss path stylus
$ npm install --save-dev babel-cli babel-preset-es2015 babelify browserify uglifyjs watchify
$ npm install --global babel-cli babel-preset-es2015 babelify browserify uglifyjs watchify
```


## ファイルのテンプレート
### app.js
サーバーのJavaScrptです。
`/`へのアクセスで`index`を返します。あとは`res.render()`の中でViewに値を渡しています。
他はStylusを使う設定とか。

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

import express from 'express';
const app = express();
import path from 'path';
import stylus from 'stylus';
import koutoSwiss from 'kouto-swiss';
import autoprefixer from 'autoprefixer-stylus';

app.set('port', 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(stylus.middleware({
  src: path.join(__dirname, 'assets'),
  compile: (str, path) => {
    return stylus(str)
      .set('filename', path)
      .set('compress', true)
      .use(koutoSwiss())
      .use(autoprefixer({ browsers: ['last 2 versions'] }))
    ;
  }
}));
app.use(express.static(path.join(__dirname, 'assets')));

app.get('/', (req, res) => {
  res.render('index', {
    title: 'App Title',
    description: 'App Description'
  });
});

app.listen(app.get('port'), (error) => {
  if (error) {
    console.error(error);
  } else {
    console.info('listen:', app.get('port'));
  }
});
```

### assets/javascripts/index_dev.js
クライアントサイドのJavaScriptです。命名は自由だけど、コンパイル後のファイルが`index.js`なので、`index_dev.js`にしました。同じファイル名でフォルダを分けたほうがいいかも。
jQueryを読みこんだり、ES2015の記法を使ったり。

```js:assets/javascripts/index_dev.js
'use strict';

window.jQuery = window.$ = require('jquery');

(() => {
  const hello = () => {
    const _message = 'Hello World!';
    console.log(_message);
  };

  $(() => {
    hello();
  });
})();
```

### views/index.jade
viewです。サーバーから送られた`title`と`description`を受け取り、表示しています。
テンプレートにOGPの`meta`タグも入れておくと、後でラク。

```jade:views/index.jade
<!DOCTYPE html>
html(lang='ja', prefix='og: http://ogp.me/ns#')
  head
    meta(charset='UTF-8')
    title!= title

    meta(name='description', content='#{description}')
    meta(name='robots', content='index,follow')
    meta(name='viewport', content='width=device-width')
    meta(property='og:title', content='#{title}')
    meta(property='og:type', content='website')
    meta(property='og:url', content='#{url}')
    meta(property='og:description', content='#{description}')
    meta(property='og:site_name', content='#{title}')
    //- meta(property='og:image', content='#{url}/images/ogp.jpg')
    meta(property='og:locale', content='ja_JP')
    meta(name='twitter:card', content='summary')

    link(rel='stylesheet', href='stylesheets/style.css')
    script(src='javascripts/index.js')

  body
    h1 #{title}
    p #{description}
```

### .babelrc
Babelifyの設定ファイルです。ES2015のモジュールを読み込みます。

```json:.babelrc
{
  "presets": ["es2015"]
}
```


## npm run-script
`$ npm run xxx`で実行するコマンドです。`package.json`に書きます。  


```json:package.json
{
  "scripts": {
    "build": "browserify -t babelify assets/javascripts/index_dev.js -o assets/javascripts/index.js",
    "watch": "npm run build && watchify -d -t babelify assets/javascripts/index_dev.js -o assets/javascripts/index.js -v",
    "build:prod": "npm run build && uglifyjs assets/javascripts/index.js --stats -o assets/javascripts/index.js",
    "start": "babel-node app.js"
  }
}
```

* 開発時は`$ npm start`でサーバーを起動して、別プロセスで`$ npm run watch`をします。`index_dev.js`の変更が監視され、都度`index.js`が出力されます。
  * `$ npm start`では`node app.js`ではなく`babel-node app.js`を実行します。`babel-node`だと、ES2015の`import`などが使えるようになります(Node.js v4以降でも`let`や`const`、アロー関数は使えますが)。
* 本番用のビルドは`$ npm run build:prod`を実行します。BrowserifyとUglifyが実行されます。
* ~~Sourcemap忘れてた…。余力があれば書き出すようにする(CSS/JSどっちも)~~
  * JSだけSourcemap出力します(`npm run watch時`)
  * StylusのSourcemapがなぜか出力できない、謎
* ESLintも忘れてるので、おいおい


## まとめ
以下の最小構成のようにもっとコンパクトにできるけど、なにか作るかとなると結局こんな感じになるかな、と。JSをES5で書いてStylusを使わずに生CSSで書けばもっとコードは減らせますが、実際辛い。

ここから、ルーティングやエラー処理を加えたり、処理が増えたらJSを分割するなどをしていけばいいかなと思います。

### 参考
[expressを最小構成で試す - Qiita](http://qiita.com/akameco/items/3ebf2e9014b13ba53f70)