GruntからMakeへ、Makeからnpm-scriptsへ

## 経緯

- [Gruntすごい、Grunt最高！](http://qiita.com/sasaplus1/items/fd07e41a0c80ce6296ec)
  - Gruntfileの行数が長くなってつらい
  - プラグイン数多いとモジュールのインストールに意外と時間がかかる
- [やっぱりMakeは良い](http://qiita.com/sasaplus1/items/b926da167c49be73f6ed)
  - 数日後に読み返すと何がなんだかわからなくなっててつらい
- npm-scriptsが良い感じ
  - [オレ的Gruntに対する最新の気持ち](http://yosuke-furukawa.hatenablog.com/entry/2014/02/19/112931)
  - [task automation with npm run](http://substack.net/task_automation_with_npm_run)
  - いまここ

## npm-scripts

```json:package.json
{
  "private": true,
  "scripts": {
    "command": "echo Hello!"
  }
}
```

```console:Terminal
$ npm run # or npm run-script
Available scripts in the  package:
  command
    echo Hello!
$ npm run command

> @ command /path/to/dir
> echo Hello!

Hello!
```

## npm install後にbower installをする

```json:package.json
{
  "private": true,
  "scripts": {
    "postinstall": "bower install"
  },
  "dependencies": {
    "bower": "^1.3.12"
  }
}
```

## jadeでjadeをコンパイルする

```sh:npm-script
jade -o ./public ./sources
```

## node-sassでsass, scssをコンパイルする

```sh:npm-script
node-sass --output-style compressed -o ./public -r ./sources
```

## bowerでインストールしたライブラリをconcatする

```:Grepfile
jquery.min.js
vue.min.js
```

```sh:npm-script
find ./bower_components -type f | grep -f Grepfile | xargs awk '{ print }' > ./libs.js
```

`xargs awk '{ print }'`を`xargs cat`にすると

```
//# sourceMappingURL=jquery.min.map/**
 * Vue.js v0.11.0
```

になって残念な感じになる。

## grunt-contrib-concatのseparator, banner, footerっぽいことをして複数のスクリプトを即時関数で囲む

```sh:npm-script
find ./sources -type f -name '*.js ' -exec awk -f concat.awk {} + > index.min.js
```

```js:1.js
/**
 * 1.js
 */
console.log(1);
```

```awk:concat.awk
BEGIN {
  print "(function() {"
}

{
  print
}

END {
  print "}());"
}
```

```js:index.min.js
(function() {
/**
 * 1.js
 */
console.log(1);
/**
 * 2.js
 */
console.log(2);
}());
```

ついでに圧縮する。

```sh:npm-script
find ./sources -type f -name '*.js' -exec awk -f concat.awk {} + | uglifyjs -cm > index.min.js
```

```js:index.min.js
!function(){console.log(1),console.log(2)}();
```

## grunt-contrib-concatのseparator, banner, footerっぽいことをしてJSONを連結する

```sh:npm-script
awk -f join.awk 1.json 2.json 3.json > common.json
```

```json:1.json
{
  "name":"data1"
}
```

```awk:join.awk
BEGIN {
  RS=""
  ORS=""

  print "{\"items\":["
}

{
  if (NR == 1) {
    print
  } else {
    print "," $0
  }
}

END {
  print "]}"
}
```

```json:common.json
{"items":[{
    "name": "data1"
},{
    "name": "data2"
},{
    "name": "data3"
}]}
```

余計なスペースとかを削除したい。

```sh:npm-script
node -pe 'JSON.stringify(process.argv.splice(1).map(function(json) { return require(json); }));' ./1.json ./2.json ./3.json | awk -f join.awk > common.json
```

うーん。横に長い。

```sh:npm-script
find . -type f -name '[123].json' | xargs node -pe 'JSON.stringify(process.argv.splice(1).map(function(a) { return require(a); }));' | awk -f join.awk > common.json
```

うーむ。横に長い。

```json:common.json
{"items":[{"name":"data1"},{"name":"data2"},{"name":"data3"}]}
```

余計なスペースとかが削除された。

## scssとjsをwatchして保存したらビルドしたい

[fswatch](https://github.com/emcrisostomo/fswatch)が必要なので入れておく。

fswatch以外だと

- [watch-then](https://github.com/hirokidaichi/watch-then)
- [wt](https://github.com/yosuke-furukawa/wt)

とか。

```json:package.json
{
  "private": true,
  "scripts": {
    "watch": "npm run watch-css & npm run watch-js",
    "watch-css": "node-sass -w -o ./public -r ./sources",
    "watch-js": "fswatch -E -e '\\.(jade|scss)$' -i '\\.js$' -r ./sources | xargs -I{} -n 1 npm run build-js"
    "build-js": "find . -type f -name '*.js' -exec awk '{ print }' + > ./index.min.js"
  }
}
```

```console:Terminal
$ brew install fswatch
$ npm run watch
```

## ローカルで動くLiveReload付きのサーバが欲しい

サーバだけはGruntを使う。

```coffee:Gruntfile.coffee
module.exports = (grunt) ->

  grunt.initConfig

    connect:
      server:
        options:
          base: 'public'
          livereload: true

    esteWatch:
      livereload:
        enabled: true

  grunt.loadNpmTasks 'grunt-contrib-connect'
  grunt.loadNpmTasks 'grunt-este-watch'

  grunt.registerTask 'default', ['connect', 'esteWatch']

  return
```

```json:package.json
{
  "private": true,
  "scripts": {
    "watch": "grunt & npm run watch-css & npm run watch-file",
    "watch-css": "node-sass -w -o ./public -r ./sources",
    "watch-file": "fswatch -r ./public | xargs -I{} -n 1 sh -c 'curl -fs -o /dev/null http://localhost:35729/changed?files=$(basename {})'"
  },
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-connect": "^0.9.0",
    "grunt-este-watch": "^0.1.18",
    "node-sass": "^1.2.3"
  }
}
```

うーむ。横に長い。

```console:Terminal
$ npm i
$ npm run watch
```

## npm-scriptsが横に長くてつらい

[代表は言っている――](https://twitter.com/yosuke_furukawa/status/473001593307025408)~~ここで死ぬ運命ではないと~~bin以下にシェルを用意して実行させよと

```json:package.json
{
  "private": true,
  "scripts": {
    "build-js": "./bin/build-js.sh"
  }
}
```

```console:Terminal
$ mkdir -p bin
$ cat <<'EOB' > bin/build-js.sh
> #!/bin/bash
>
> find . -type f -name '*.js' -exec awk '{ print }' + > ./index.min.js
> EOB
$ chmod +x bin/build-js.sh
$ npm run build-js
```

---

npm-scriptsというよりかはgruntを使わないでfindとかawkでがんばるスクリプト集みたいになった。

自分一人だけの時なら複雑なスクリプトを書いてもいいかもしれないけど、協調が必要なときは普通にGrunt使った方が良いと思う。（自分以外の人もそれなりにわかるなら別だけど）