grunt自体がNode.jsで動いてるんでややこしいですが、Node.jsでアプリケーションを開発している時、foreverとかnode-devを使って、更新ファイルがあれば再起動してブラウザをリロードして確認すると思います。gruntがあるならサーバーの再起動もブラウザのオートリロードもできるやろ!と思って作りました。
Qiitaにも投稿しました。

はじめに

Node.jsをExpressで作る際に、app.js, routes, jade, stylus, publicの中にあるファイルなどを更新した際に、serverがrestartしてブラウザもreloadされる。ようなGruntfileを作りました。まぁまぁ開発が早くなるんちゃうかと思います。

環境

  • Node.js v0.10.26
  • Express v3.0.0
  • node-supervisor

テンプレートはJadeを使ってます。
CSSは、stylusからCSSを書き出してます。その際に、ベンダープレフィックスを追加するautoprefixerが動いてます。
JavaScriptファイルは全てcoffeeで書きます。
vendorsフォルダーは、distにコピーしているだけです。

Nodeプロセス

node-supervisorを使って起動しますので、npm install -g node-supervisor してください。

ファイル構成

.
├── Gruntfile.coffee
├── app
│   ├── app.coffee
│   ├── bin
│   │   └── www
│   ├── public
│   │   ├── images
│   │   ├── javascripts
│   │   └── stylesheets
│   ├── routes
│   │   ├── index.coffee
│   │   └── user.coffee
│   └── views
│       ├── error.jade
│       ├── index.jade
│       └── layout.jade
├── node_modules
└── package.json

Getting Started

package.jsonのあるディレクトリでnpm installします。 必要なnode_modulesがインストールされます。

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
{
  "name": "application-name",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "express": "^3.4.8",
    "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"
  },
  "devDependencies": {
    "grunt": "^0.4.5",
    "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",
    "matchdep": "^0.3.0"
  }
}

npm install してパッケージをインストールします。

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
'use strict'
 
proxySnippet = require('grunt-connect-proxy/lib/utils').proxyRequest
mountFolder = folderMount = (connect, base) ->
  connect['static'] require('path').resolve(base)
 
listen = 8000
server = 3000
 
module.exports = (grunt) ->
  require('matchdep').filterDev('grunt-*').forEach grunt.loadNpmTasks
  grunt.initConfig
 
    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'
 
    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 %>/<%= dir.vendors %>'
        dest: '<%= dir.dist %>/<%= dir.vendors %>'
        src: ['**']
      build:
        expand: true
        dot: false
        cwd: '<%= dir.dist %>/'
        dest: '<%= dir.build %>/'
        src: ['**']
 
    connect:
      front:
        options:
          host: 'localhost'
          port: listen
          middleware: (connect) ->
            [
              mountFolder(connect, '.')
              proxySnippet
            ]
          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:
      jade:
        files: '<%= dir.src %>/**/*.jade',
        tasks: 'newer:copy:jade'
      stylus:
        files: '<%= dir.src %>/**/*.styl'
        tasks: [
          'newer:stylus:dist',
          'newer:autoprefixer:dist',
          'newer:styleguide: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 %>/<%= dir.vendors %>/**']
        tasks: ['copy:vendors']
      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 %>'
        ]
      docs:
        src: '<%= dir.docs %>'
 
  grunt.registerTask 'server', [
    'configureProxies'
    'express:dev'
    'connect:front'
    'watch'
  ]
 
  grunt.registerTask 'default', [
    'clean'
    'stylus:dist'
    'autoprefixer'
    'coffee:dist'
    'copy:jade'
    'copy:img'
    'copy:bin'
    'copy:vendors'
    'imagemin'
  ]
 
  grunt.registerTask 'dev', [
    'default'
    'configureProxies'
    'express:dev'
    'connect:front'
    'watch'
  ]

簡単な使い方

デフォルトのコマンドは、coffeeファイルがJavaScriptファイルに、stylusファイルがCSSファイルに書きだされ、jadeはコピーされて、画像ファイルは圧縮されます。

$ grunt

grunt devで、ブラウザが立ち上がりページが表示されます。ファイル監視をし、ファイルに変更があれば、サーバーが再起動してブラウザもリロードします。

$ grunt dev