package.jsonをどう書けばいいのかよくわからない・・・。jade, coffee-script, stylusだけをgitで管理し、gruntでビルドしたjs, css, bower_componentsとかは、gitで管理する必要がない。流れ的には下記の通り。

  1. jade, stylus, coffee-scriptで開発。
  2. gruntで更新ファイルを監視し、jadeはcopy、stylusはcssへ、coffee-scriptはjsへ。
  3. gitで管理するのは、1.のファイルたち。
  4. Herokuへdeploy。
  5. gruntが実行して、1.のファイルたちが2.になってサーバーが動く。

この流れに行き着くまでに色々理由がある。

  1. coffeeファイルでNode.jsを動かしてもいいが、syntaxerrorが事前にわかりにくい。
    → grunt-coffeelintで文法チェックをする
    → 文法チェックしても動くかどうかはわからない。
    → やっぱりjsファイルにしてから動作確認したい。
  2. grunt-autoprefixerを使えば、-webkit, -mozなどを書かなくても保管してくれる。
    → stylusには効かない。
    → stylusからcssに変換してからautoprefixerを掛ける。
  3. Herokuへdeployするにはgitで管理しているファイルに限る。 → gruntで書きだしたファイルをgitで管理する?いやそれはない。
    → gruntで書きだす前のファイルをgitで管理しないと意味がない。

  4. Herokuへdeployする時にgruntを動かしたい。
    → 方法としては、package.jsonのscriptにpostinstallを追記する。
    → または、Herokuが提供しているbuildpackを使ってgruntを叩くようにheroku configで設定をする。

4.の項目に関して、具体的なpackage.jsonは下記の通り。

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
{
  "name": "test",
  "version": "0.0.1",
  "engines": {
    "node": "0.10.x"
  },
  "private": false,
  "scripts": {
    "start": "node ./dist/bin/www",
    "postinstall": "./node_modules/grunt-cli/bin/grunt build"
  },
  "dependencies": {
    "express": "~4.2.0",
    "static-favicon": "~1.0.0",
    "morgan": "~1.0.0",
    "cookie-parser": "~1.0.1",
    "body-parser": "~1.0.0",
    "debug": "~0.7.4",
    "jade": "~1.3.0",
    "stylus": "0.42.3",
    "grunt": "^0.4.5",
    "grunt-cli": "^0.1.13"
  },
  "devDependencies": {
    "grunt-autoprefixer": "^0.8.1",
    "grunt-connect-proxy": "^0.1.10",
    "grunt-contrib-clean": "^0.5.0",
    "grunt-contrib-coffee": "^0.10.1",
    "grunt-contrib-connect": "^0.8.0",
    "grunt-contrib-copy": "^0.5.0",
    "grunt-contrib-imagemin": "^0.7.1",
    "grunt-contrib-stylus": "^0.18.0",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-express-server": "^0.4.17",
    "grunt-newer": "^0.7.0",
    "grunt-bower-task": "^0.3.4",
    "load-grunt-tasks": "^0.6.0"
  }
}

9,10行目に書かれている項目がいつもと少し違います。express v3からwwwというファイルをstartの項目に書いて、Node.jsを起動する方法になりました。そのパスの最初にdistを指定しているのは、gruntがビルドしたディレクトリを指していますので、distというディレクトリにしています。ここは任意になりますね。

10行目は、Herokuにdeployした時に実行するgruntコマンドが書いてあります。deploy時に実行したいコマンドをGruntfileに書いてあれば、ここの項目が実行されます。

さて、コレを実際にデプロイするとどうなるか。

command
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
> test@0.0.1 postinstall /tmp/build_63e5029d-35ad-4574-8b0b-60718f56b4c8
> ./node_modules/grunt-cli/bin/grunt build
 
sh: ./node_modules/grunt-cli/bin/grunt: not found
 
npm ERR! test@0.0.1 postinstall: `./node_modules/grunt-cli/bin/grunt build`
npm ERR! Exit status 127
npm ERR!
npm ERR! Failed at the test@0.0.1 postinstall script.
npm ERR! This is most likely a problem with the grunt-express-coffee package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     ./node_modules/grunt-cli/bin/grunt build
npm ERR! You can get their info via:
npm ERR!     npm owner ls grunt-express-coffee
npm ERR! There is likely additional logging output above.
npm ERR! System Linux 3.8.11-ec2
npm ERR! command "/tmp/build_63e5029d-35ad-4574-8b0b-60718f56b4c8/vendor/node/bin/node" "/tmp/build_63e5029d-35ad-4574-8b0b-60718f56b4c8/vendor/node/bin/npm" "install" "--userconfig" "/tmp/build_63e5029d-35ad-4574-8b0b-60718f56b4c8/.npmrc"
npm ERR! cwd /tmp/build_63e5029d-35ad-4574-8b0b-60718f56b4c8
npm ERR! node -v v0.10.30
npm ERR! npm -v 1.4.21
npm ERR! code ELIFECYCLE
npm ERR!
npm ERR! Additional logging details can be found in:
npm ERR!     /tmp/build_63e5029d-35ad-4574-8b0b-60718f56b4c8/npm-debug.log
npm ERR! not ok code 0

こんな感じでエラーになっちゃうんですよね。要はgruntが実行できないですよと怒られているわけです。これの原因は、packega.jsonにあります。

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"dependencies": {
  "express": "~4.2.0",
  "static-favicon": "~1.0.0",
  "morgan": "~1.0.0",
  "cookie-parser": "~1.0.1",
  "body-parser": "~1.0.0",
  "debug": "~0.7.4",
  "jade": "~1.3.0",
  "stylus": "0.42.3"
},
"devDependencies": {
  "grunt": "^0.4.5",
  "grunt-cli": "^0.1.13",
  ....
}

dependenciesがdeploy時に必要なmoduleをインストールする項目なので、ここにgruntを書いてないので、gruntが実行できない。ということです。そしたらここに書いてまえ!と、

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"dependencies": {
  "express": "~4.2.0",
  "static-favicon": "~1.0.0",
  "morgan": "~1.0.0",
  "cookie-parser": "~1.0.1",
  "body-parser": "~1.0.0",
  "debug": "~0.7.4",
  "jade": "~1.3.0",
  "stylus": "0.42.3",
  "grunt": "^0.4.5",
  "grunt-cli": "^0.1.13"
},
"devDependencies": {
  ....
}

devDependenciesの中にあるgrunt, grunt-cliをdependenciesに移しました。これでHerokuへdeployすると、激しくログが出て、結果こけます。そのログの途中でERRORが出てました。

command
1
2
3
Loading "Gruntfile.coffee" tasks...ERROR
       >> Error: Cannot find module 'load-grunt-tasks'
       Warning: Task "build" not found. Use --force to continue.

gruntタスクを読み込むためのload-grunt-tasksがないですよーと怒られました。んではまた、dependenciesに書いてみます。

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
"dependencies": {
  "express": "~4.2.0",
  "static-favicon": "~1.0.0",
  "morgan": "~1.0.0",
  "cookie-parser": "~1.0.1",
  "body-parser": "~1.0.0",
  "debug": "~0.7.4",
  "jade": "~1.3.0",
  "stylus": "0.42.3",
  "grunt": "^0.4.5",
  "grunt-cli": "^0.1.13",
  "load-grunt-tasks": "^0.6.0"
},
"devDependencies": {
  ....
}

さて、これでdeployしてどうなるかな・・・ってまた激しくログが出て、途中でERRORが出ていました。

command
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>> Local Npm module "grunt-autoprefixer" not found. Is it installed?
>> Local Npm module "grunt-connect-proxy" not found. Is it installed?
>> Local Npm module "grunt-contrib-clean" not found. Is it installed?
>> Local Npm module "grunt-contrib-coffee" not found. Is it installed?
>> Local Npm module "grunt-contrib-connect" not found. Is it installed?
>> Local Npm module "grunt-contrib-copy" not found. Is it installed?
>> Local Npm module "grunt-contrib-imagemin" not found. Is it installed?
>> Local Npm module "grunt-contrib-stylus" not found. Is it installed?
>> Local Npm module "grunt-contrib-watch" not found. Is it installed?
>> Local Npm module "grunt-express-server" not found. Is it installed?
>> Local Npm module "grunt-newer" not found. Is it installed?
>> Local Npm module "grunt-bower-task" not found. Is it installed?
Warning: Task "clean" not found. Use --force to continue.
 
Aborted due to warnings.

もう全然moduleが足りないってことね笑。ってことは、どうなるかというと、package.jsonの中身はこうなります。

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
  "name": "test",
  "version": "0.0.1",
  "engines": {
    "node": "0.10.x"
  },
  "private": false,
  "scripts": {
    "start": "node ./dist/bin/www",
    "postinstall": "./node_modules/grunt-cli/bin/grunt build"
  },
  "dependencies": {
    "express": "~4.2.0",
    "static-favicon": "~1.0.0",
    "morgan": "~1.0.0",
    "cookie-parser": "~1.0.1",
    "body-parser": "~1.0.0",
    "debug": "~0.7.4",
    "jade": "~1.3.0",
    "stylus": "0.42.3",
    "grunt": "^0.4.5",
    "grunt-cli": "^0.1.13",
    "grunt-autoprefixer": "^0.8.1",
    "grunt-connect-proxy": "^0.1.10",
    "grunt-contrib-clean": "^0.5.0",
    "grunt-contrib-coffee": "^0.10.1",
    "grunt-contrib-connect": "^0.8.0",
    "grunt-contrib-copy": "^0.5.0",
    "grunt-contrib-imagemin": "^0.7.1",
    "grunt-contrib-stylus": "^0.18.0",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-express-server": "^0.4.17",
    "grunt-newer": "^0.7.0",
    "grunt-bower-task": "^0.3.4",
    "load-grunt-tasks": "^0.6.0"
  }
}

devDependenciesの項目が全然なくなってしまうんですよね!これでdeployすると、無事にdeploy完了しました!ワーイヽ(゚∀゚)メ(゚∀゚)メ(゚∀゚)ノワーイ

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
> ./node_modules/grunt-cli/bin/grunt build
 
Running "clean:dist" (clean) task
 
Running "bower:install" (bower) task
>> Installed bower packages
>> Copied packages to /tmp/build_dbb97ba9-1171-42e5-aa62-d7acea76c359/lib
 
Running "stylus:dist" (stylus) task
File dist/public/stylesheets/style.css created.
 
Running "autoprefixer:dist" (autoprefixer) task
File dist/public/stylesheets/style.css created.
 
Running "coffee:dist" (coffee) task
 
Running "copy:jade" (copy) task
Copied 3 files
 
Running "copy:img" (copy) task
 
 
Running "copy:bin" (copy) task
Created 1 directories, copied 1 files
 
Running "copy:vendors" (copy) task
 
 
Running "copy:bower_components" (copy) task
 
 
Running "imagemin:dist" (imagemin) task
Minified 0 images (saved 0 B)
 
Done, without errors.

deployログの途中にも、ちゃんとgruntのログがでて、Welcome to Express画面も確認できました。

まとめますと、今回しようしたGruntfileはこちらです。

Gruntfile.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
'use strict'
 
mountFolder = folderMount = (connect, base) ->
  connect['static'] require('path').resolve(base)
 
listen = 8000
server = 3000
 
module.exports = (grunt) ->
  require('load-grunt-tasks')(grunt)
  grunt.initConfig
    pkg: grunt.file.readJSON('package.json')
    # Metadata.
    banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + '<%= grunt.template.today(\'yyyy-mm-dd\') %>\n' + '<%= pkg.homepage ? \'* \' + pkg.homepage + \'\\n\' : \'\' %>' + '* Copyright (c) <%= grunt.template.today(\'yyyy\') %> <%= pkg.author.name %>;' + ' Licensed <%= _.pluck(pkg.licenses, \'type\').join(\', \') %> */\n'
 
    dir:
      src: 'app'
      styl: 'stylesheets'
      coffee: 'javascripts'
      images: 'images'
      js: 'javascripts'
      css: 'stylesheets'
      img: 'images'
      vendors: 'vendors'
      dist: 'dist'
      build: 'build'
      docs: 'docs'
      test: 'test'
 
    bower:
      install:
        options:
          install: true
          cleanTargetDir: false
          cleanBowerDir: false
 
    stylus:
      dist:
        options:
          compress: false
        expand: true
        cwd: '<%= dir.src %>'
        src: '**/*.styl'
        dest: '<%= dir.dist %>'
        ext: '.css'
 
    coffee:
      dist:
        option:
          pretty: true
        expand: true
        cwd: '<%= dir.src %>'
        src: '**/*.coffee'
        dest: '<%= dir.dist %>'
        ext: '.js'
 
    # coffeeの文法チェック
    coffeelint:
      app: '<%= dir.src %>/**/*.coffee'
 
    # CSSのprefiexを補完
    autoprefixer:
      options:
        browsers: ['last 2 version', 'ie 8', 'ie 7', 'ie 6']
      dist:
        expand: true
        cwd: '<%= dir.dist %>'
        src: '**/*.css'
        dest: '<%= dir.dist %>'
        ext: '.css'
 
    # 画像の圧縮
    imagemin:
      options:
        optimizationLevel: 7
        pngquant: false
      dist:
        expand: true
        cwd: '<%= dir.dist %>/<%= dir.images %>'
        src: '**/*.{jpg,jpeg,gif}'
        dest: '<%= dir.dist %>/<%= dir.img %>'
 
    copy:
      jade:
        expand: true
        dot: true
        cwd: '<%= dir.src %>'
        dest: '<%= dir.dist %>'
        src: '**/*.jade'
      bin:
        expand: true
        cwd: '<%= dir.src %>/bin'
        dest: '<%= dir.dist %>/bin'
        src: '**'
      img:
        expand: true
        dot: true
        cwd: '<%= dir.src %>'
        dest: '<%= dir.dist %>'
        src: [
          '**/*.{gif,jpeg,jpg,png,svg,webp}',
        ]
      vendors:
        expand: true
        dot: true
        cwd: '<%= dir.src %>/public/vendors'
        dest: '<%= dir.dist %>/public/vendors'
        src: '**'      
      bower_components:
        expand: true
        dot: true
        cwd: '<%= dir.src %>/bower_components'
        dest: '<%= dir.dist %>/bower_components'
        src: ['**']
 
    connect:
      front:
        options:
          host: 'localhost'
          port: listen
          middleware: (connect) ->
            [
              mountFolder(connect, '.')
              proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest
            ]
 
          open:
            target: 'http://localhost:' + listen
 
          livereload: true
 
      proxies: [
        context: '/'
        host: 'localhost'
        port: server + ''
        https: false
        changeOrigin: false
      ]
 
    express:
      dev:
        options:
          background: true
          port: server
          cmd: 'supervisor'
          args: []
          script: '<%= dir.dist %>/bin/www'
          delay: 0
 
    watch:
      options:
        livereload: true
      jade:
        files: '<%= dir.src %>/**/*.jade',
        tasks: 'newer:copy:jade'
      stylus:
        files: '<%= dir.src %>/**/*.styl'
        tasks: [
          'newer:stylus:dist',
          'newer:autoprefixer:dist',
        ]
      coffee:
        files: '<%= dir.src %>/**/*.coffee'
        tasks: 'newer:coffee:dist'
      images:
        files: ['<%= dir.src %>/**/*.{gif,jpeg,jpg,png,svg,webp}']
        tasks: ['newer:imagemin:dist', 'copy', 'imagemin']
      vendors:
        files: ['<%= dir.src %>/public/vendors']
        tasks: ['copy:vendors']
      bower_components:
        files: ['<%= dir.src %>/bower_components']
        tasks: ['copy:bower_components']
 
      express:
        files: [
          '<%= dir.dist %>/app.js'
          '<%= dir.dist %>/routes/**/*.js'
          '<%= dir.dist %>/views/**/*.jade'
        ]
        tasks: ['express:dev']
        options:
          livereload: true
 
      veiw:
        files: ['<%= dir.dist %>/public/**/*.{js,css}']
 
 
    clean:
      dist:
        src: [
          '<%= dir.dist %>'
        ]
 
  grunt.registerTask 'server', [
    'configureProxies'
    'express:dev'
    'connect:front'
    'watch'
  ]
 
  grunt.registerTask 'default', [
    'clean'
    'bower:install'
    'stylus:dist'
    'autoprefixer'
    'coffee:dist'
    'copy:jade'
    'copy:img'
    'copy:bin'
    'copy:vendors'
    'copy:bower_components'
    'imagemin'
  ]
 
  grunt.registerTask 'dev', [
    'default'
    'configureProxies'
    'express:dev'
    'connect:front'
    'watch'
  ]
  
  grunt.registerTask 'build', [
    'default'
  ]

package.jsonはこちら。

package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
  "name": "grunt-express-coffee",
  "version": "0.0.1",
  "engines": {
    "node": "0.10.x"
  },
  "private": false,
  "scripts": {
    "start": "node ./dist/bin/www",
    "postinstall": "./node_modules/grunt-cli/bin/grunt build"
  },
  "dependencies": {
    "express": "~4.2.0",
    "static-favicon": "~1.0.0",
    "morgan": "~1.0.0",
    "cookie-parser": "~1.0.1",
    "body-parser": "~1.0.0",
    "debug": "~0.7.4",
    "jade": "~1.3.0",
    "stylus": "0.42.3",
    "grunt": "^0.4.5",
    "grunt-cli": "^0.1.13",
    "grunt-autoprefixer": "^0.8.1",
    "grunt-connect-proxy": "^0.1.10",
    "grunt-contrib-clean": "^0.5.0",
    "grunt-contrib-coffee": "^0.10.1",
    "grunt-contrib-connect": "^0.8.0",
    "grunt-contrib-copy": "^0.5.0",
    "grunt-contrib-imagemin": "^0.7.1",
    "grunt-contrib-stylus": "^0.18.0",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-express-server": "^0.4.17",
    "grunt-newer": "^0.7.0",
    "grunt-bower-task": "^0.3.4",
    "load-grunt-tasks": "^0.6.0"
  }
}

ちょっと待てよ・・・。ホンマにこのpackage.jsonでええんやろか・・・。gruntは基本、devDependenciesに書いてあることが多いので、違和感があるだけなんやろうか・・・。実際にこれがベストプラクティスなんかはわからないので、どなたか賢くてエロい方、ご教授いただければ幸いです。