akiyoko blog

akiyoko の IT技術系ブログです

Yeoman を使って Webアプリの雛形を作ってみる

Yeoman(ヨーマン)は、モダンなWebアプリのためのフロントエンド統合開発ツールです。Webアプリをスクラッチから作る際のややこしいワークフローを楽チンにしてくれる便利なツールと考えればよいでしょう。


Yeoman は、以下の3つのツールで構成されています。単体でも使うことができるのですが、それぞれのツールの親和性が高いので、3つをまとめて使いこなすと非常に効率がよいと思います。

Yo 雛形作成ツール
Bower パッケージマネージャ
Grunt スクラン



利用イメージは、公式ページの↓の図が非常に分かりやすいです。

f:id:akiyoko:20140731011453p:plain
http://yeoman.io/learning/index.html より引用)


図でも説明されているように、まずは Yo から使い始めることが多いわけなのですが、Yo を使って雛形アプリを作ることで、Bower(バウワー)を利用するための bower.json、Grunt を利用するための package.json と Gruntfile.js までもがひと通り揃った状態になるため、後は必要に応じて任意のタスクを実行すればよいだけ、という環境を整えることができるのです。



環境

  • Mac OSX 10.9.4
  • Node.js インストール済み(バージョンが古かったので苦労した。。後述)

やりたいこと

  • Yeoman を使って、雛形アプリを作り、任意のライブラリを追加し、テストサーバにデプロイする。

具体的には、http://yeoman.io/learning/ に書かれているように、

# First, you'll need to install yo and other required tools:
$ npm install -g yo
$ npm install -g grunt-cli bower

# To scaffold a web application, you'll need to install the generator-webapp generator
$ npm install -g generator-webapp

# create a directory for your new project
$ mkdir my-yo-project
$ cd my-yo-project

# and then run:
$ yo webapp

を実行後に、Foundation のライブラリを Bower で追加。
最後に、Grunt でテストサーバにデプロイしてブラウザで確認して、プロダクションのソースコードを出力するところまでを実施してみたいと思います。



 

0. Node.js のインストール

Yeoman は Node.js で動作するので、まずは Node.js をインストールします。

$ brew install node

とやるそうですが、私の場合は、すでにインストール済みでした。
(おそらく、Titanium Studio からインストールしていたのでしょう。)


$ node -v
v0.10.13
$ npm -v
1.3.2


 

1. Node.js のアップグレード

Node.js のバージョンが古い場合は、アップグレードしておきます。
(バージョンが古いと、あとでエラーに泣く羽目になります。。後述)

$ npm cache clean
$ npm install -g n
npm http GET https://registry.npmjs.org/n
npm http 200 https://registry.npmjs.org/n
npm http GET https://registry.npmjs.org/n/-/n-1.2.9.tgz
npm http 200 https://registry.npmjs.org/n/-/n-1.2.9.tgz
unbuild n@1.2.9
/usr/local/bin/n -> /usr/local/lib/node_modules/n/bin/n
n@1.2.9 /usr/local/lib/node_modules/n
$ n stable

     install : v0.10.29
       mkdir : /usr/local/n/versions/0.10.29
       fetch : http://nodejs.org/dist/v0.10.29/node-v0.10.29-darwin-x64.tar.gz
   installed : v0.10.29

$ node -v
v0.10.29
$ npm -v
1.4.14

⇒ ハマりポイント①

(参考)

 

2. yo, bower, grunt-cli をインストール

まとめてインストールしてしまいます。
「-g」オプションでインストールすることで、どこからでもコマンドを実行できるようにしておきます。(「/usr/local/lib/node_modules/」以下にインストールされるようです。)

$ npm install -g yo bower grunt-cli
  ・
  ・
[Yeoman Doctor] Everything looks alright!
-
grunt-cli@0.1.13 /usr/local/lib/node_modules/grunt-cli
├── resolve@0.3.1
├── nopt@1.0.10 (abbrev@1.0.5)
└── findup-sync@0.1.3 (glob@3.2.11, lodash@2.4.1)

bower@1.3.8 /usr/local/lib/node_modules/bower
├── is-root@0.1.0
├── junk@0.3.0
├── stringify-object@0.2.1
├── abbrev@1.0.5
├── which@1.0.5
├── chmodr@0.1.0
├── opn@0.1.2
├── osenv@0.1.0
├── archy@0.0.2
├── graceful-fs@3.0.2
├── lockfile@0.4.2
├── rimraf@2.2.8
├── bower-logger@0.2.2
├── lru-cache@2.5.0
├── bower-endpoint-parser@0.2.2
├── retry@0.6.1
├── nopt@3.0.1
├── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)
├── tmp@0.0.23
├── q@1.0.1
├── request-progress@0.3.1 (throttleit@0.0.2)
├── semver@2.3.2
├── shell-quote@1.4.2 (array-filter@0.0.1, array-map@0.0.0, array-reduce@0.0.0, jsonify@0.0.0)
├── p-throttler@0.0.1 (q@0.9.7)
├── mkdirp@0.5.0 (minimist@0.0.8)
├── bower-json@0.4.0 (intersect@0.0.3, deep-extend@0.2.11, graceful-fs@2.0.3)
├── promptly@0.2.0 (read@1.0.5)
├── fstream-ignore@0.0.10 (inherits@2.0.1, minimatch@0.3.0)
├── fstream@0.1.29 (inherits@2.0.1, mkdirp@0.3.5)
├── tar@0.1.20 (inherits@2.0.1, block-stream@0.0.7)
├── glob@4.0.5 (once@1.3.0, inherits@2.0.1, minimatch@1.0.0)
├── bower-config@0.5.2 (osenv@0.0.3, graceful-fs@2.0.3, optimist@0.6.1)
├── decompress-zip@0.0.8 (nopt@2.2.1, mkpath@0.1.0, touch@0.0.2, readable-stream@1.1.13-1, binary@0.3.0)
├── request@2.36.0 (json-stringify-safe@5.0.0, forever-agent@0.5.2, aws-sign2@0.5.0, qs@0.6.6, oauth-sign@0.3.0, tunnel-agent@0.4.0, node-uuid@1.4.1, mime@1.2.11, form-data@0.1.4, tough-cookie@0.12.1, http-signature@0.10.0, hawk@1.0.0)
├── bower-registry-client@0.2.1 (graceful-fs@2.0.3, request-replay@0.2.0, lru-cache@2.3.1, async@0.2.10, mkdirp@0.3.5, request@2.27.0)
├── handlebars@1.3.0 (optimist@0.3.7, uglify-js@2.3.6)
├── cardinal@0.4.4 (ansicolors@0.2.1, redeyed@0.4.4)
├── mout@0.9.1
├── update-notifier@0.2.0 (semver-diff@0.1.0, string-length@0.1.2, chalk@0.5.1, latest-version@0.2.0, configstore@0.3.1)
├── insight@0.3.1 (object-assign@0.1.2, async@0.2.10, lodash.debounce@2.4.1, request@2.27.0, inquirer@0.4.1, configstore@0.2.3)
└── inquirer@0.5.1 (readline2@0.1.0, mute-stream@0.0.4, through@2.3.4, async@0.8.0, lodash@2.4.1, cli-color@0.3.2)

yo@1.2.1 /usr/local/lib/node_modules/yo
├── is-root@0.1.0
├── fullname@0.1.1
├── opn@0.1.2
├── async@0.9.0
├── multiline@0.3.4 (strip-indent@0.1.3)
├── sudo-block@0.4.0 (chalk@0.4.0)
├── nopt@3.0.1 (abbrev@1.0.5)
├── findup@0.1.5 (commander@2.1.0, colors@0.6.2)
├── chalk@0.5.1 (escape-string-regexp@1.0.1, ansi-styles@1.1.0, supports-color@0.2.0, strip-ansi@0.3.0, has-ansi@0.1.0)
├── string-length@0.1.2 (strip-ansi@0.2.2)
├── shelljs@0.3.0
├── yosay@0.3.0 (ansi-regex@0.2.1, ansi-styles@1.1.0, pad-component@0.0.1, word-wrap@0.1.3, strip-ansi@0.2.2, minimist@0.2.0, chalk@0.4.0, taketalk@0.1.1)
├── lodash@2.4.1
├── insight@0.3.1 (object-assign@0.1.2, chalk@0.4.0, async@0.2.10, lodash.debounce@2.4.1, request@2.27.0, configstore@0.2.3, inquirer@0.4.1)
├── update-notifier@0.2.0 (semver-diff@0.1.0, latest-version@0.2.0, configstore@0.3.1)
└── yeoman-generator@0.17.1 (dargs@0.1.0, github-username@0.1.1, diff@1.0.8, class-extend@0.1.1, rimraf@2.2.8, text-table@0.2.0, mime@1.2.11, chalk@0.4.0, debug@1.0.4, isbinaryfile@2.0.1, grouped-queue@0.3.0, mkdirp@0.5.0, glob@4.0.5, findup-sync@0.1.3, iconv-lite@0.2.11, file-utils@0.2.0, download@0.1.18, request@2.39.0, gruntfile-editor@0.1.1, cheerio@0.17.0, underscore.string@2.3.3, inquirer@0.5.1)||<

⇒ ハマりポイント②
⇒ ハマりポイント③

yo 1.2.1
bower 1.3.8
grunt-cli 0.1.13

がインストールされました。


 

3. generator-webapp をインストール

「yo webapp」したい場合は「generator-webapp」という名前のジェネレータを npm でインストールします(ジェネレータ名は「generator-」を付けることになっているようです)。この場合も「-g」オプションを付けます。

$ npm install -g generator-webapp
  ・
  ・
generator-mocha@0.1.5 /usr/local/lib/node_modules/generator-mocha
└── yeoman-generator@0.17.1 (dargs@0.1.0, github-username@0.1.1, diff@1.0.8, class-extend@0.1.1, rimraf@2.2.8, chalk@0.4.0, text-table@0.2.0, mime@1.2.11, async@0.9.0, isbinaryfile@2.0.1, nopt@3.0.1, debug@1.0.4, grouped-queue@0.3.0, shelljs@0.3.0, mkdirp@0.5.0, glob@4.0.5, findup-sync@0.1.3, underscore.string@2.3.3, download@0.1.18, iconv-lite@0.2.11, request@2.39.0, file-utils@0.2.0, lodash@2.4.1, cheerio@0.17.0, gruntfile-editor@0.1.1, inquirer@0.5.1)

generator-webapp@0.4.9 /usr/local/lib/node_modules/generator-webapp
├── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)
├── cheerio@0.13.1 (underscore@1.5.2, entities@0.5.0, CSSselect@0.4.1, htmlparser2@3.4.0)
└── yeoman-generator@0.16.0 (dargs@0.1.0, debug@0.7.4, diff@1.0.8, class-extend@0.1.1, rimraf@2.2.8, findup-sync@0.1.3, text-table@0.2.0, mime@1.2.11, async@0.2.10, mkdirp@0.3.5, isbinaryfile@2.0.1, shelljs@0.2.6, underscore.string@2.3.3, glob@3.2.11, iconv-lite@0.2.11, lodash@2.4.1, request@2.30.0, file-utils@0.1.5, download@0.1.18, inquirer@0.4.1)

generator-webapp は、以下のような機能を提供てくれます(公式ページより)。

  • CSS Autoprefixing
  • Built-in preview server with LiveReload
  • Automagically compile CoffeeScript & Sass
  • Automagically lint your scripts
  • Automagically wire up your Bower components with grunt-wiredep.
  • Awesome Image Optimization (via OptiPNG, pngquant, jpegtran and gifsicle)
  • Mocha Unit Testing with PhantomJS
  • Bootstrap for Sass (Optional)
  • Leaner Modernizr builds (Optional)


なお、yo コマンドから「Install a generator」を選択して、任意の generator を検索してインストールすることも可能です。

$ yo
[?] What would you like to do? Install a generator
[?] Search NPM for generators: backbone
[?] Here's what I found. Install one? (Use arrow keys)
❯ generator-backbone 
  generator-backbone-amd 
  generator-backbone-browserify 
  generator-backbone-list-view 
  generator-backbone-marionette 
  generator-backbone-mocha 
  generator-backbone-module 
(Move up and down to reveal more choices)

 

4. プロジェクト生成

$ cd ~
$ mkdir yo_test
$ cd yo_test/

$ yo webapp

     _-----_
    |       |
    |--(o)--|   .--------------------------.
   `---------´  |    Welcome to Yeoman,    |
    ( _´U`_ )   |   ladies and gentlemen!  |
    /___A___\   '__________________________'
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

Out of the box I include HTML5 Boilerplate, jQuery, and a Gruntfile.js to build your app.
[?] What more would you like? 
 ⬢ Bootstrap
 ⬢ Sass
❯⬢ Modernizr
[?] Would you like to use libsass? Read up more at 
https://github.com/andrew/node-sass#reporting-sass-compilation-and-syntax-issues: (y/N) y
   create Gruntfile.js
   create package.json
   create .gitignore
   create .gitattributes
   create bower.json
   create .jshintrc
   create .editorconfig
   create app/favicon.ico
   create app/404.html
   create app/robots.txt
   create app/.htaccess
   create app/styles/main.scss
   create app/index.html
   create app/scripts/main.js


I'm all done. Running bower install & npm install for you to install the required dependencies. If this fails, try running the command yourself.


npm WARN package.json yo-test@0.0.0 No description
npm WARN package.json yo-test@0.0.0 No repository field.
npm WARN package.json yo-test@0.0.0 No README data
bower jquery#~1.11.0            cached git://github.com/jquery/jquery.git#1.11.1
bower jquery#~1.11.0          validate 1.11.1 against git://github.com/jquery/jquery.git#~1.11.0
bower modernizr#~2.6.2          cached git://github.com/Modernizr/Modernizr.git#2.6.3
bower modernizr#~2.6.2        validate 2.6.3 against git://github.com/Modernizr/Modernizr.git#~2.6.2
bower bootstrap-sass-official#~3.1.0           cached git://github.com/twbs/bootstrap-sass.git#3.1.1+2
bower bootstrap-sass-official#~3.1.0         validate 3.1.1+2 against git://github.com/twbs/bootstrap-sass.git#~3.1.0
bower bootstrap-sass-official#~3.1.0          install bootstrap-sass-official#3.1.1+2
bower modernizr#~2.6.2                        install modernizr#2.6.3
bower jquery#~1.11.0                          install jquery#1.11.1
 
> phantomjs@1.9.7-15 install /Users/akiyoko/yo_test/node_modules/grunt-mocha/node_modules/grunt-lib-phantomjs/node_modules/phantomjs
> node install.js

\
bootstrap-sass-official#3.1.1+2 bower_components/bootstrap-sass-official

modernizr#2.6.3 bower_components/modernizr

jquery#1.11.1 bower_components/jquery
Download already available at /var/folders/rc/_0bgj5gd2d3_1_r87wq7x4fh0000gn/T/phantomjs/phantomjs-1.9.7-macosx.zip
Extracting zip contents
Copying extracted folder /var/folders/rc/_0bgj5gd2d3_1_r87wq7x4fh0000gn/T/phantomjs/phantomjs-1.9.7-macosx.zip-extract-1406805214289/phantomjs-1.9.7-macosx -> /Users/akiyoko/yo_test/node_modules/grunt-mocha/node_modules/grunt-lib-phantomjs/node_modules/phantomjs/lib/phantom
Writing location.js file
Done. Phantomjs binary available at /Users/akiyoko/yo_test/node_modules/grunt-mocha/node_modules/grunt-lib-phantomjs/node_modules/phantomjs/lib/phantom/bin/phantomjs
 
> optipng-bin@0.3.9 postinstall /Users/akiyoko/yo_test/node_modules/grunt-contrib-imagemin/node_modules/image-min/node_modules/optipng-bin
> node index.js

✓ pre-build test passed successfully
 
> jpegtran-bin@0.2.7 postinstall /Users/akiyoko/yo_test/node_modules/grunt-contrib-imagemin/node_modules/image-min/node_modules/jpegtran-bin
> node index.js

✓ pre-build test passed successfully
 
> gifsicle@0.1.6 postinstall /Users/akiyoko/yo_test/node_modules/grunt-contrib-imagemin/node_modules/image-min/node_modules/gifsicle
> node index.js

✓ pre-build test passed successfully
 
> pngquant-bin@0.1.7 postinstall /Users/akiyoko/yo_test/node_modules/grunt-contrib-imagemin/node_modules/image-min/node_modules/pngquant-bin
> node index.js

✓ pre-build test passed successfully
 
> node-sass@0.8.6 install /Users/akiyoko/yo_test/node_modules/grunt-sass/node_modules/node-sass
> node build.js

`darwin-x64-v8-3.14` exists; testing
-
․․․․․․․․․․․․․․․․․․․․․․

  22 passing (35ms)

Binary is fine; exiting
grunt-contrib-copy@0.5.0 node_modules/grunt-contrib-copy

grunt-rev@0.1.0 node_modules/grunt-rev

grunt-contrib-concat@0.3.0 node_modules/grunt-contrib-concat

jshint-stylish@0.1.5 node_modules/jshint-stylish
├── text-table@0.2.0
└── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)

grunt-contrib-clean@0.5.0 node_modules/grunt-contrib-clean
└── rimraf@2.2.8

grunt-concurrent@0.5.0 node_modules/grunt-concurrent
├── async@0.2.10
└── pad-stdio@0.1.1 (lpad@0.2.1)

time-grunt@0.3.2 node_modules/time-grunt
├── date-time@0.1.1
├── pretty-ms@0.1.0
├── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)
├── hooker@0.2.3
└── text-table@0.2.0

grunt-contrib-htmlmin@0.2.0 node_modules/grunt-contrib-htmlmin
├── each-async@0.1.3
├── pretty-bytes@0.1.2
├── html-minifier@0.5.6
└── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)

grunt-newer@0.7.0 node_modules/grunt-newer
├── rimraf@2.2.6
└── async@0.2.10

grunt-usemin@2.1.1 node_modules/grunt-usemin
├── debug@0.7.4
└── lodash@1.0.1

grunt-contrib-cssmin@0.9.0 node_modules/grunt-contrib-cssmin
├── clean-css@2.1.8 (commander@2.1.0)
├── chalk@0.4.0 (strip-ansi@0.1.1, ansi-styles@1.0.0, has-color@0.1.7)
└── maxmin@0.1.0 (pretty-bytes@0.1.2, gzip-size@0.1.1)

grunt-contrib-uglify@0.4.1 node_modules/grunt-contrib-uglify
├── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)
├── maxmin@0.1.0 (pretty-bytes@0.1.2, gzip-size@0.1.1)
└── uglify-js@2.4.15 (uglify-to-browserify@1.0.2, async@0.2.10, optimist@0.3.7, source-map@0.1.34)

grunt-contrib-watch@0.6.1 node_modules/grunt-contrib-watch
├── async@0.2.10
├── tiny-lr-fork@0.0.5 (debug@0.7.4, faye-websocket@0.4.4, noptify@0.0.3, qs@0.5.6)
├── lodash@2.4.1
└── gaze@0.5.1 (globule@0.1.0)

grunt-mocha@0.4.11 node_modules/grunt-mocha
├── lodash@2.3.0
├── mocha@1.18.2 (diff@1.0.7, growl@1.7.0, commander@2.0.0, mkdirp@0.3.5, debug@1.0.4, glob@3.2.3, jade@0.26.3)
└── grunt-lib-phantomjs@0.4.0 (eventemitter2@0.4.14, semver@1.0.14, temporary@0.0.8, phantomjs@1.9.7-15)

grunt-contrib-jshint@0.9.2 node_modules/grunt-contrib-jshint
├── hooker@0.2.3
└── jshint@2.4.4 (console-browserify@0.1.6, exit@0.1.2, minimatch@0.4.0, underscore@1.4.4, shelljs@0.1.4, cli@0.4.5, htmlparser2@3.3.0)

grunt-modernizr@0.5.2 node_modules/grunt-modernizr
├── colors@0.6.2
├── promised-io@0.3.4
├── uglify-js@1.3.3
└── request@2.27.0 (json-stringify-safe@5.0.0, forever-agent@0.5.2, qs@0.6.6, aws-sign@0.3.0, oauth-sign@0.3.0, tunnel-agent@0.3.0, cookie-jar@0.3.0, node-uuid@1.4.1, mime@1.2.11, form-data@0.1.4, hawk@1.0.0, http-signature@0.10.0)

grunt-svgmin@0.4.0 node_modules/grunt-svgmin
├── each-async@0.1.3
├── pretty-bytes@0.1.2
├── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)
└── svgo@0.4.4 (colors@0.6.2, whet.extend@0.9.9, coa@0.4.1, sax@0.6.0, js-yaml@2.1.3)

grunt-bower-install@1.4.1 node_modules/grunt-bower-install
├── wiredep@1.4.4 (chalk@0.1.1, through2@0.4.2, glob@3.2.11, lodash@1.3.1)
└── bower-config@0.5.2 (osenv@0.0.3, graceful-fs@2.0.3, optimist@0.6.1, mout@0.9.1)

grunt-autoprefixer@0.7.6 node_modules/grunt-autoprefixer
├── diff@1.0.8
├── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)
└── autoprefixer@1.3.1 (fs-extra@0.9.1, postcss@0.3.5, caniuse-db@1.0.20140730)

grunt-contrib-connect@0.7.1 node_modules/grunt-contrib-connect
├── connect-livereload@0.3.2
├── open@0.0.4
├── async@0.2.10
├── portscanner@0.2.2 (async@0.1.15)
└── connect@2.13.1 (uid2@0.0.3, methods@0.1.0, debug@0.8.1, pause@0.0.1, cookie-signature@1.0.1, qs@0.6.6, fresh@0.2.0, bytes@0.2.1, raw-body@1.1.3, buffer-crc32@0.2.1, batch@0.5.0, cookie@0.1.0, compressible@1.0.0, negotiator@0.3.0, send@0.1.4, multiparty@2.2.0)

load-grunt-tasks@0.4.0 node_modules/load-grunt-tasks
├── multimatch@0.1.0 (minimatch@0.2.14, lodash@2.4.1)
└── findup-sync@0.1.3 (glob@3.2.11, lodash@2.4.1)

grunt@0.4.5 node_modules/grunt
├── dateformat@1.0.2-1.2.3
├── which@1.0.5
├── eventemitter2@0.4.14
├── getobject@0.1.0
├── colors@0.6.2
├── async@0.1.22
├── rimraf@2.2.8
├── grunt-legacy-util@0.2.0
├── exit@0.1.2
├── nopt@1.0.10 (abbrev@1.0.5)
├── hooker@0.2.3
├── glob@3.1.21 (inherits@1.0.0, graceful-fs@1.2.3)
├── minimatch@0.2.14 (sigmund@1.0.0, lru-cache@2.5.0)
├── lodash@0.9.2
├── coffee-script@1.3.3
├── underscore.string@2.2.1
├── iconv-lite@0.2.11
├── js-yaml@2.0.5 (esprima@1.0.4, argparse@0.1.15)
├── findup-sync@0.1.3 (glob@3.2.11, lodash@2.4.1)
└── grunt-legacy-log@0.1.1 (lodash@2.4.1, underscore.string@2.3.3)

grunt-contrib-imagemin@0.6.1 node_modules/grunt-contrib-imagemin
├── pretty-bytes@0.1.2
├── mkdirp@0.3.5
├── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)
├── async@0.2.10
└── image-min@0.2.3 (win-spawn@2.0.0, filesize@2.0.3, stream-combiner@0.0.4, multipipe@0.0.2, through2@0.4.2, concat-stream@1.4.6, map-key@0.1.4, mout@0.9.1, optipng-bin@0.3.9, jpegtran-bin@0.2.7, gifsicle@0.1.6, pngquant-bin@0.1.7)

grunt-sass@0.11.0 node_modules/grunt-sass
├── each-async@0.1.3
├── chalk@0.4.0 (has-color@0.1.7, ansi-styles@1.0.0, strip-ansi@0.1.1)
└── node-sass@0.8.6 (node-watch@0.3.4, mkdirp@0.3.5, nan@0.8.0, optimist@0.6.1, sinon@1.10.3, mocha@1.18.2)
   invoke   mocha:app
   create     test/bower.json
   create     test/.bowerrc
   create     test/spec/test.js
   create     test/index.html

⇒ ハマりポイント④


なお、yo で generator をインストール後、generator によっては、

$ npm install

が必要な場合もあるようです。



パッケージ構成はこうなりました。

$ tree -L 2
.
├── Gruntfile.js
├── app
│   ├── 404.html
│   ├── favicon.ico
│   ├── images
│   ├── index.html
│   ├── robots.txt
│   ├── scripts
│   └── styles
├── bower.json
├── bower_components
│   ├── bootstrap-sass-official
│   ├── jquery
│   └── modernizr
├── node_modules
│   ├── grunt
│   ├── grunt-autoprefixer
│   ├── grunt-bower-install
│   ├── grunt-concurrent
│   ├── grunt-contrib-clean
│   ├── grunt-contrib-concat
│   ├── grunt-contrib-connect
│   ├── grunt-contrib-copy
│   ├── grunt-contrib-cssmin
│   ├── grunt-contrib-htmlmin
│   ├── grunt-contrib-imagemin
│   ├── grunt-contrib-jshint
│   ├── grunt-contrib-uglify
│   ├── grunt-contrib-watch
│   ├── grunt-mocha
│   ├── grunt-modernizr
│   ├── grunt-newer
│   ├── grunt-rev
│   ├── grunt-sass
│   ├── grunt-svgmin
│   ├── grunt-usemin
│   ├── jshint-stylish
│   ├── load-grunt-tasks
│   └── time-grunt
├── package.json
└── test
    ├── bower.json
    ├── index.html
    └── spec

35 directories, 9 files


bower.json, package.json, Gruntfile.js の中身はこうなっています。

bower.json

{
  "name": "yo-test",
  "private": true,
  "dependencies": {
    "bootstrap-sass-official": "~3.1.0",
    "modernizr": "~2.6.2",
    "jquery": "~1.11.0"
  },
  "devDependencies": {}
}

package.json

{
  "name": "yo-test",
  "version": "0.0.0",
  "dependencies": {},
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-copy": "~0.5.0",
    "grunt-contrib-concat": "~0.3.0",
    "grunt-contrib-uglify": "~0.4.0",
    "grunt-sass": "~0.11.0",
    "grunt-contrib-jshint": "~0.9.2",
    "grunt-contrib-cssmin": "~0.9.0",
    "grunt-contrib-connect": "~0.7.1",
    "grunt-contrib-clean": "~0.5.0",
    "grunt-contrib-htmlmin": "~0.2.0",
    "grunt-bower-install": "~1.4.0",
    "grunt-contrib-imagemin": "~0.6.0",
    "grunt-contrib-watch": "~0.6.1",
    "grunt-rev": "~0.1.0",
    "grunt-autoprefixer": "~0.7.2",
    "grunt-usemin": "~2.1.0",
    "grunt-mocha": "~0.4.10",
    "grunt-modernizr": "~0.5.2",
    "grunt-newer": "~0.7.0",
    "grunt-svgmin": "~0.4.0",
    "grunt-concurrent": "~0.5.0",
    "load-grunt-tasks": "~0.4.0",
    "time-grunt": "~0.3.1",
    "jshint-stylish": "~0.1.5"
  },
  "engines": {
    "node": ">=0.10.0"
  }
}

Gruntfile.js

// Generated on 2014-07-31 using generator-webapp 0.4.9
'use strict';

// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'

module.exports = function (grunt) {

    // Load grunt tasks automatically
    require('load-grunt-tasks')(grunt);

    // Time how long tasks take. Can help when optimizing build times
    require('time-grunt')(grunt);

    // Configurable paths
    var config = {
        app: 'app',
        dist: 'dist'
    };

    // Define the configuration for all the tasks
    grunt.initConfig({

        // Project settings
        config: config,

        // Watches files for changes and runs tasks based on the changed files
        watch: {
            bower: {
                files: ['bower.json'],
                tasks: ['bowerInstall']
            },
            js: {
                files: ['<%= config.app %>/scripts/{,*/}*.js'],
                tasks: ['jshint'],
                options: {
                    livereload: true
                }
            },
            jstest: {
                files: ['test/spec/{,*/}*.js'],
                tasks: ['test:watch']
            },
            gruntfile: {
                files: ['Gruntfile.js']
            },
            sass: {
                files: ['<%= config.app %>/styles/{,*/}*.{scss,sass}'],
                tasks: ['sass:server', 'autoprefixer']
            },
            styles: {
                files: ['<%= config.app %>/styles/{,*/}*.css'],
                tasks: ['newer:copy:styles', 'autoprefixer']
            },
            livereload: {
                options: {
                    livereload: '<%= connect.options.livereload %>'
                },
                files: [
                    '<%= config.app %>/{,*/}*.html',
                    '.tmp/styles/{,*/}*.css',
                    '<%= config.app %>/images/{,*/}*'
                ]
            }
        },

        // The actual grunt server settings
        connect: {
            options: {
                port: 9000,
                open: true,
                livereload: 35729,
                // Change this to '0.0.0.0' to access the server from outside
                hostname: 'localhost'
            },
            livereload: {
                options: {
                    middleware: function(connect) {
                        return [
                            connect.static('.tmp'),
                            connect().use('/bower_components', connect.static('./bower_components')),
                            connect.static(config.app)
                        ];
                    }
                }
            },
            test: {
                options: {
                    open: false,
                    port: 9001,
                    middleware: function(connect) {
                        return [
                            connect.static('.tmp'),
                            connect.static('test'),
                            connect().use('/bower_components', connect.static('./bower_components')),
                            connect.static(config.app)
                        ];
                    }
                }
            },
            dist: {
                options: {
                    base: '<%= config.dist %>',
                    livereload: false
                }
            }
        },

        // Empties folders to start fresh
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '<%= config.dist %>/*',
                        '!<%= config.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },

        // Make sure code styles are up to par and there are no obvious mistakes
        jshint: {
            options: {
                jshintrc: '.jshintrc',
                reporter: require('jshint-stylish')
            },
            all: [
                'Gruntfile.js',
                '<%= config.app %>/scripts/{,*/}*.js',
                '!<%= config.app %>/scripts/vendor/*',
                'test/spec/{,*/}*.js'
            ]
        },

        // Mocha testing framework configuration options
        mocha: {
            all: {
                options: {
                    run: true,
                    urls: ['http://<%= connect.test.options.hostname %>:<%= connect.test.options.port %>/index.html']
                }
            }
        },

        // Compiles Sass to CSS and generates necessary files if requested
        sass: {
            options: {
                includePaths: [
                    'bower_components'
                ]
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/styles',
                    src: ['*.scss'],
                    dest: '.tmp/styles',
                    ext: '.css'
                }]
            },
            server: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/styles',
                    src: ['*.scss'],
                    dest: '.tmp/styles',
                    ext: '.css'
                }]
            }
        },

        // Add vendor prefixed styles
        autoprefixer: {
            options: {
                browsers: ['last 1 version']
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/styles/',
                    src: '{,*/}*.css',
                    dest: '.tmp/styles/'
                }]
            }
        },

        // Automatically inject Bower components into the HTML file
        bowerInstall: {
            app: {
                src: ['<%= config.app %>/index.html'],
                exclude: ['bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap.js']
            },
            sass: {
                src: ['<%= config.app %>/styles/{,*/}*.{scss,sass}']
            }
        },

        // Renames files for browser caching purposes
        rev: {
            dist: {
                files: {
                    src: [
                        '<%= config.dist %>/scripts/{,*/}*.js',
                        '<%= config.dist %>/styles/{,*/}*.css',
                        '<%= config.dist %>/images/{,*/}*.*',
                        '<%= config.dist %>/styles/fonts/{,*/}*.*',
                        '<%= config.dist %>/*.{ico,png}'
                    ]
                }
            }
        },

        // Reads HTML for usemin blocks to enable smart builds that automatically
        // concat, minify and revision files. Creates configurations in memory so
        // additional tasks can operate on them
        useminPrepare: {
            options: {
                dest: '<%= config.dist %>'
            },
            html: '<%= config.app %>/index.html'
        },

        // Performs rewrites based on rev and the useminPrepare configuration
        usemin: {
            options: {
                assetsDirs: ['<%= config.dist %>', '<%= config.dist %>/images']
            },
            html: ['<%= config.dist %>/{,*/}*.html'],
            css: ['<%= config.dist %>/styles/{,*/}*.css']
        },

        // The following *-min tasks produce minified files in the dist folder
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/images',
                    src: '{,*/}*.{gif,jpeg,jpg,png}',
                    dest: '<%= config.dist %>/images'
                }]
            }
        },

        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/images',
                    src: '{,*/}*.svg',
                    dest: '<%= config.dist %>/images'
                }]
            }
        },

        htmlmin: {
            dist: {
                options: {
                    collapseBooleanAttributes: true,
                    collapseWhitespace: true,
                    removeAttributeQuotes: true,
                    removeCommentsFromCDATA: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true,
                    removeRedundantAttributes: true,
                    useShortDoctype: true
                },
                files: [{
                    expand: true,
                    cwd: '<%= config.dist %>',
                    src: '{,*/}*.html',
                    dest: '<%= config.dist %>'
                }]
            }
        },

        // By default, your `index.html`'s <!-- Usemin block --> will take care of
        // minification. These next options are pre-configured if you do not wish
        // to use the Usemin blocks.
        // cssmin: {
        //     dist: {
        //         files: {
        //             '<%= config.dist %>/styles/main.css': [
        //                 '.tmp/styles/{,*/}*.css',
        //                 '<%= config.app %>/styles/{,*/}*.css'
        //             ]
        //         }
        //     }
        // },
        // uglify: {
        //     dist: {
        //         files: {
        //             '<%= config.dist %>/scripts/scripts.js': [
        //                 '<%= config.dist %>/scripts/scripts.js'
        //             ]
        //         }
        //     }
        // },
        // concat: {
        //     dist: {}
        // },

        // Copies remaining files to places other tasks can use
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= config.app %>',
                    dest: '<%= config.dist %>',
                    src: [
                        '*.{ico,png,txt}',
                        '.htaccess',
                        'images/{,*/}*.webp',
                        '{,*/}*.html',
                        'styles/fonts/{,*/}*.*'
                    ]
                }, {
                    expand: true,
                    dot: true,
                    cwd: '.',
                    src: ['bower_components/bootstrap-sass-official/vendor/assets/fonts/bootstrap/*.*'],
                    dest: '<%= config.dist %>'
                }]
            },
            styles: {
                expand: true,
                dot: true,
                cwd: '<%= config.app %>/styles',
                dest: '.tmp/styles/',
                src: '{,*/}*.css'
            }
        },

        // Generates a custom Modernizr build that includes only the tests you
        // reference in your app
        modernizr: {
            dist: {
                devFile: 'bower_components/modernizr/modernizr.js',
                outputFile: '<%= config.dist %>/scripts/vendor/modernizr.js',
                files: {
                    src: [
                        '<%= config.dist %>/scripts/{,*/}*.js',
                        '<%= config.dist %>/styles/{,*/}*.css',
                        '!<%= config.dist %>/scripts/vendor/*'
                    ]
                },
                uglify: true
            }
        },

        // Run some tasks in parallel to speed up build process
        concurrent: {
            server: [
                'sass:server',
                'copy:styles'
            ],
            test: [
                'copy:styles'
            ],
            dist: [
                'sass',
                'copy:styles',
                'imagemin',
                'svgmin'
            ]
        }
    });


    grunt.registerTask('serve', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'connect:dist:keepalive']);
        }

        grunt.task.run([
            'clean:server',
            'concurrent:server',
            'autoprefixer',
            'connect:livereload',
            'watch'
        ]);
    });

    grunt.registerTask('server', function (target) {
        grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
        grunt.task.run([target ? ('serve:' + target) : 'serve']);
    });

    grunt.registerTask('test', function (target) {
        if (target !== 'watch') {
            grunt.task.run([
                'clean:server',
                'concurrent:test',
                'autoprefixer'
            ]);
        }

        grunt.task.run([
            'connect:test',
            'mocha'
        ]);
    });

    grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'concat',
        'cssmin',
        'uglify',
        'copy:dist',
        'modernizr',
        'rev',
        'usemin',
        'htmlmin'
    ]);

    grunt.registerTask('default', [
        'newer:jshint',
        'test',
        'build'
    ]);
};


 

5. Bower で Foundation を追加インストール

「--save」オプションを付けることで、bower.json の「dependencies」に、「--save-dev」オプションを付けることで「devDependencies」に依存関係を保存することができます。

ここで、Foundation のライブラリは製品版にもパッケージとして入れたいので、「--save」オプションを付けておきます。

$ bower install foundation --save
bower foundation#*              cached git://github.com/zurb/bower-foundation.git#5.3.1
bower foundation#*            validate 5.3.1 against git://github.com/zurb/bower-foundation.git#*
bower jquery-placeholder#~2.0.7 cached git://github.com/mathiasbynens/jquery-placeholder.git#2.0.8
bower jquery-placeholder#~2.0.7         validate 2.0.8 against git://github.com/mathiasbynens/jquery-placeholder.git#~2.0.7
bower jquery#>= 2.1.0                     cached git://github.com/jquery/jquery.git#2.1.1
bower jquery#>= 2.1.0                   validate 2.1.1 against git://github.com/jquery/jquery.git#>= 2.1.0
bower jquery.cookie#~1.4.0                cached git://github.com/carhartl/jquery-cookie.git#1.4.1
bower jquery.cookie#~1.4.0              validate 1.4.1 against git://github.com/carhartl/jquery-cookie.git#~1.4.0
bower modernizr#>= 2.7.2                  cached git://github.com/Modernizr/Modernizr.git#2.8.3
bower modernizr#>= 2.7.2                validate 2.8.3 against git://github.com/Modernizr/Modernizr.git#>= 2.7.2
bower fastclick#>=0.6.11                  cached git://github.com/ftlabs/fastclick.git#1.0.2
bower fastclick#>=0.6.11                validate 1.0.2 against git://github.com/ftlabs/fastclick.git#>=0.6.11
bower fastclick#>=0.6.11                     new version for git://github.com/ftlabs/fastclick.git#>=0.6.11
bower fastclick#>=0.6.11                 resolve git://github.com/ftlabs/fastclick.git#>=0.6.11
bower fastclick#>=0.6.11                download https://github.com/ftlabs/fastclick/archive/v1.0.3.tar.gz
bower fastclick#>=0.6.11                 extract archive.tar.gz
bower fastclick#>=0.6.11                resolved git://github.com/ftlabs/fastclick.git#1.0.3

Unable to find a suitable version for modernizr, please choose one:
    1) modernizr#~2.6.2 which resolved to 2.6.3 and is required by yo-test 
    2) modernizr#>= 2.7.2 which resolved to 2.8.3 and is required by foundation#5.3.1

Prefix the choice with ! to persist it to bower.json


[?] Answer: 2

Unable to find a suitable version for jquery, please choose one:
    1) jquery#~1.11.0 which resolved to 1.11.1 and is required by yo-test 
    2) jquery#>=1.2 which resolved to 1.11.1 and is required by jquery.cookie#1.4.1 
    3) jquery#>= 2.1.0 which resolved to 2.1.1 and is required by foundation#5.3.1

Prefix the choice with ! to persist it to bower.json


[?] Answer: 3
bower modernizr#>= 2.7.2                 install modernizr#2.8.3
bower jquery#>= 2.1.0                    install jquery#2.1.1
bower foundation#~5.3.1                  install foundation#5.3.1
bower jquery.cookie#~1.4.0               install jquery.cookie#1.4.1
bower jquery-placeholder#~2.0.7          install jquery-placeholder#2.0.8
bower fastclick#>=0.6.11                 install fastclick#1.0.3

modernizr#2.8.3 bower_components/modernizr

jquery#2.1.1 bower_components/jquery

foundation#5.3.1 bower_components/foundation
├── fastclick#1.0.3
├── jquery#2.1.1
├── jquery-placeholder#2.0.8
├── jquery.cookie#1.4.1
└── modernizr#2.8.3

jquery.cookie#1.4.1 bower_components/jquery.cookie
└── jquery#2.1.1

jquery-placeholder#2.0.8 bower_components/jquery-placeholder

fastclick#1.0.3 bower_components/fastclick
$ tree -L 2
.
├── Gruntfile.js
├── app
│   ├── 404.html
│   ├── favicon.ico
│   ├── images
│   ├── index.html
│   ├── robots.txt
│   ├── scripts
│   └── styles
├── bower.json
├── bower_components
│   ├── bootstrap-sass-official
│   ├── fastclick
│   ├── foundation
│   ├── jquery
│   ├── jquery-placeholder
│   ├── jquery.cookie
│   └── modernizr
├── node_modules
│   ├── grunt
│   ├── grunt-autoprefixer
│   ├── grunt-bower-install
│   ├── grunt-concurrent
│   ├── grunt-contrib-clean
│   ├── grunt-contrib-concat
│   ├── grunt-contrib-connect
│   ├── grunt-contrib-copy
│   ├── grunt-contrib-cssmin
│   ├── grunt-contrib-htmlmin
│   ├── grunt-contrib-imagemin
│   ├── grunt-contrib-jshint
│   ├── grunt-contrib-uglify
│   ├── grunt-contrib-watch
│   ├── grunt-mocha
│   ├── grunt-modernizr
│   ├── grunt-newer
│   ├── grunt-rev
│   ├── grunt-sass
│   ├── grunt-svgmin
│   ├── grunt-usemin
│   ├── jshint-stylish
│   ├── load-grunt-tasks
│   └── time-grunt
├── package.json
└── test
    ├── bower.json
    ├── index.html
    └── spec

39 directories, 9 files


bower.json に「foundation」が追加されました。

bower.json

{
  "name": "yo-test",
  "private": true,
  "dependencies": {
    "bootstrap-sass-official": "~3.1.0",
    "modernizr": "~2.6.2",
    "jquery": "~1.11.0",
    "foundation": "~5.3.1"
  },
  "devDependencies": {}
}


ここで、Foundation系の依存ファイル(foundation/css, foundation/js など)の参照を index.html に追記するには、

$ grunt bowerInstall

とすればよいのですが、「bower install」を実行しても Gruntfile.js のタスクまでは自動で書き換えられないため、このままでは index.html には何も追記されません。
同様に、「grunt build」を実行しても、Foundation系の sassファイルをコンパイルしたり、jsファイルを圧縮したり、dist/ ディレクトリにコピーしたりといったことが行われません。これをするためには、
https://github.com/zurb/foundation/blob/master/Gruntfile.js
を参考にして、cssmin (sass), uglify, copy を自前で修正しなければいけません(今回それは割愛しています)。


あるいは、Foundation を使うのがメインであれば、
$ foundation new! Foundation 5がリリースされました | notnil creation weblog
を参考にして、

$ foundation new

とした方が早いかもしれません。


または、Foundation 4系でよければ、

$ gem list
$ sudo gem install compass
$ compass -v
Compass 0.12.7 (Alnilam)

$ npm install -g generator-zurb-foundation
$ yo zurb-foundation
$ npm install
$ grunt server

とか。


Foundation 5系なら、公式の generator ではないようですが、
https://www.npmjs.org/package/generator-zf5
とか。



 

6. Gruntタスクを実行して、ブラウザで確認

Gruntfile.js に「grunt serve」タスクがすでに定義されているので、「grunt serve」タスクを実行してみます。

$ grunt serve
Running "serve" task

Running "clean:server" (clean) task

Running "concurrent:server" (concurrent) task
    
    Running "copy:styles" (copy) task
    
    
    Done, without errors.
    
    
    Execution Time (2014-07-31 11:19:21 UTC)
    loading tasks  14ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 56%
    copy:styles    10ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 40%
    Total 25ms
        
    Running "sass:server" (sass) task
    File .tmp/styles/main.css created.
    
    Done, without errors.
    
    
    Execution Time (2014-07-31 11:19:21 UTC)
    loading tasks   14ms  ▇▇ 3%
    sass:server    390ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 97%
    Total 404ms
    
Running "autoprefixer:dist" (autoprefixer) task
File .tmp/styles/main.css created.

Running "connect:livereload" (connect) task
Started connect web server on http://localhost:9000

Running "watch" task
Waiting...

ブラウザが自動的に起動して、「http://localhost:9000/」が開かれます。
f:id:akiyoko:20140731204945p:plain



 

7. grunt test してみる

「grunt test」タスクを実行して、テストを流してみます。

$ grunt test
Running "test" task

Running "clean:server" (clean) task

Running "concurrent:test" (concurrent) task
    
    Running "copy:styles" (copy) task
    
    
    Done, without errors.
    
    
    Execution Time (2014-07-31 11:27:54 UTC)
    loading tasks   3ms  ▇▇▇▇▇▇ 11%
    copy:styles    25ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 89%
    Total 28ms
    
Running "autoprefixer:dist" (autoprefixer) task

Running "connect:test" (connect) task
Started connect web server on http://localhost:9001

Running "mocha:all" (mocha) task
Testing: http://localhost:9001/index.html

  ․

  1 passing (408ms)

>> 1 passed! (0.41s)

Done, without errors.


Execution Time (2014-07-31 11:27:53 UTC)
concurrent:test   1.3s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 28%
connect:test     198ms  ▇▇▇▇ 4%
mocha:all           3s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 67%
Total 4.5s

⇒ ハマりポイント⑤



 

8. プロダクション用のファイル一式を出力

最後に、プロダクション用のファイル一式を「dist」ディレクトリに出力します。
7. のテストが成功しないと、プロダクション用のコードが出力がされないので注意。

$ grunt
Running "newer:jshint" (newer) task

Running "newer:jshint:all" (newer) task

Running "jshint:all" (jshint) task

✔ No problems


Running "newer-postrun:jshint:all:1:/Users/akiyoko/yo_test/node_modules/grunt-newer/.cache" (newer-postrun) task

Running "test" task

Running "clean:server" (clean) task

Running "concurrent:test" (concurrent) task
    
    Running "copy:styles" (copy) task
    
    
    Done, without errors.
    
    
    Execution Time (2014-07-31 11:28:58 UTC)
    loading tasks  3ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 30%
    copy:styles    7ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 70%
    Total 10ms
    
Running "autoprefixer:dist" (autoprefixer) task

Running "connect:test" (connect) task
Started connect web server on http://localhost:9001

Running "mocha:all" (mocha) task
Testing: http://localhost:9001/index.html

  ․

  1 passing (105ms)

>> 1 passed! (0.10s)

Running "clean:dist" (clean) task

Running "useminPrepare:html" (useminPrepare) task
Going through app/index.html to update the config
Looking for build script HTML comment blocks

Configuration is now:

  concat:
  { generated: 
   { files: 
      [ { dest: '.tmp/concat/styles/vendor.css', src: [] },
        { dest: '.tmp/concat/styles/main.css',
          src: [ '.tmp/styles/main.css' ] },
        { dest: '.tmp/concat/scripts/vendor/modernizr.js',
          src: [ 'bower_components/modernizr/modernizr.js' ] },
        { dest: '.tmp/concat/scripts/vendor.js',
          src: [ 'bower_components/jquery/dist/jquery.js' ] },
        { dest: '.tmp/concat/scripts/plugins.js',
          src: 
           [ 'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/affix.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/alert.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/dropdown.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/tooltip.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/modal.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/transition.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/button.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/popover.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/carousel.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/scrollspy.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/collapse.js',
             'bower_components/bootstrap-sass-official/vendor/assets/javascripts/bootstrap/tab.js' ] },
        { dest: '.tmp/concat/scripts/main.js',
          src: [ '{app,.tmp}/scripts/main.js' ] } ] } }

  uglify:
  { generated: 
   { files: 
      [ { dest: 'dist/scripts/vendor/modernizr.js',
          src: [ '.tmp/concat/scripts/vendor/modernizr.js' ] },
        { dest: 'dist/scripts/vendor.js',
          src: [ '.tmp/concat/scripts/vendor.js' ] },
        { dest: 'dist/scripts/plugins.js',
          src: [ '.tmp/concat/scripts/plugins.js' ] },
        { dest: 'dist/scripts/main.js',
          src: [ '.tmp/concat/scripts/main.js' ] } ] } }

  cssmin:
  { generated: 
   { files: 
      [ { dest: 'dist/styles/vendor.css',
          src: [ '.tmp/concat/styles/vendor.css' ] },
        { dest: 'dist/styles/main.css',
          src: [ '.tmp/concat/styles/main.css' ] } ] } }

Running "concurrent:dist" (concurrent) task
    
    Running "copy:styles" (copy) task
    
    
    Done, without errors.
    
    
    Execution Time (2014-07-31 11:29:02 UTC)
    loading tasks   5ms  ▇▇▇▇▇▇▇▇▇▇ 20%
    copy:styles    19ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 76%
    Total 25ms
        
    Running "imagemin:dist" (imagemin) task
    Minified 0 images (saved 0 B)
    
    Done, without errors.
    
    
    Execution Time (2014-07-31 11:29:02 UTC)
    loading tasks  25ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 53%
    imagemin:dist  22ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 47%
    Total 47ms
        
    Running "svgmin:dist" (svgmin) task
    Total saved: 0 B
    
    Done, without errors.
    
    
    Execution Time (2014-07-31 11:29:02 UTC)
    svgmin:dist  717ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 99%
    Total 724ms
        
    Running "sass:dist" (sass) task
    File .tmp/styles/main.css created.
    
    Running "sass:server" (sass) task
    File .tmp/styles/main.css created.
    
    Done, without errors.
    
    
    Execution Time (2014-07-31 11:29:02 UTC)
    sass:dist    791ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 85%
    sass:server  135ms  ▇▇▇▇▇▇▇▇ 14%
    Total 934ms
    
Running "autoprefixer:dist" (autoprefixer) task
File .tmp/styles/main.css created.

Running "concat:generated" (concat) task
File ".tmp/concat/styles/vendor.css" created.
File ".tmp/concat/styles/main.css" created.
File ".tmp/concat/scripts/vendor/modernizr.js" created.
File ".tmp/concat/scripts/vendor.js" created.
File ".tmp/concat/scripts/plugins.js" created.
File ".tmp/concat/scripts/main.js" created.

Running "cssmin:generated" (cssmin) task
>> Destination not written because minified CSS was empty.
File dist/styles/main.css created: 130.33 kB → 103.92 kB

Running "uglify:generated" (uglify) task
File dist/scripts/vendor/modernizr.js created: 51.35 kB → 11.08 kB
File dist/scripts/vendor.js created: 247.35 kB → 84.11 kB
File dist/scripts/plugins.js created: 58.16 kB → 30.41 kB
File dist/scripts/main.js created: 30 B → 28 B

Running "copy:dist" (copy) task
Copied 9 files

Running "modernizr:dist" (modernizr) task

Enabled Extras
>> shiv
>> load
>> cssclasses

Looking for Modernizr references

in dist/styles/main.css
>> svg

Downloading source files
200 modernizr.load.1.5.4.js
200 modernizr-latest.js

>> Generating a custom Modernizr build
>> Uglifying

>> Wrote file to dist/scripts/vendor/modernizr.js

Running "rev:dist" (rev) task
dist/scripts/main.js >> b6c3df09.main.js
dist/scripts/plugins.js >> 6b5da0e8.plugins.js
dist/scripts/vendor.js >> db02b173.vendor.js
dist/scripts/vendor/modernizr.js >> fbe20327.modernizr.js
dist/styles/main.css >> 444ed944.main.css
dist/favicon.ico >> 6df2b309.favicon.ico

Running "usemin:html" (usemin) task

Processing as HTML - dist/404.html
Update the HTML to reference our concat/min/revved script files
Update the HTML with the new css filenames
Update the HTML with the new img filenames
Update the HTML with data-main tags
Update the HTML with data-* tags
Update the HTML with background imgs, case there is some inline style
Update the HTML with anchors images
Update the HTML with reference in input

Processing as HTML - dist/index.html
Update the HTML to reference our concat/min/revved script files
<script src="scripts/vendor/modernizr.js" changed to <script src="scripts/vendor/fbe20327.modernizr.js"
<script src="scripts/vendor.js" changed to <script src="scripts/db02b173.vendor.js"
<script src="scripts/plugins.js" changed to <script src="scripts/6b5da0e8.plugins.js"
<script src="scripts/main.js" changed to <script src="scripts/b6c3df09.main.js"
Update the HTML with the new css filenames
<link rel="shortcut icon" href="/favicon.ico" changed to <link rel="shortcut icon" href="/6df2b309.favicon.ico"
<link rel="stylesheet" href="styles/main.css" changed to <link rel="stylesheet" href="styles/444ed944.main.css"
Update the HTML with the new img filenames
Update the HTML with data-main tags
Update the HTML with data-* tags
Update the HTML with background imgs, case there is some inline style
Update the HTML with anchors images
Update the HTML with reference in input

Running "usemin:css" (usemin) task

Processing as CSS - dist/styles/444ed944.main.css
Update the CSS to reference our revved images

Running "htmlmin:dist" (htmlmin) task
Minified dist/404.html 4.46 kB → 4.18 kB
Minified dist/index.html 3.14 kB → 2.38 kB

Done, without errors.


Execution Time (2014-07-31 11:28:57 UTC)
jshint:all         268ms  ▇▇ 2%
concurrent:test     1.2s  ▇▇▇▇▇▇▇▇▇ 10%
mocha:all           1.1s  ▇▇▇▇▇▇▇▇ 9%
concurrent:dist     3.4s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 28%
autoprefixer:dist  335ms  ▇▇▇ 3%
concat:generated   184ms  ▇▇ 1%
uglify:generated    1.8s  ▇▇▇▇▇▇▇▇▇▇▇▇▇ 14%
modernizr:dist      3.7s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 30%
Total 12.3s


プロダクション用のアプリがビルドされ、dist配下に出力されます。

$ tree dist/
dist/
├── 404.html
├── 6df2b309.favicon.ico
├── bower_components
│   └── bootstrap-sass-official
│       └── vendor
│           └── assets
│               └── fonts
│                   └── bootstrap
│                       ├── glyphicons-halflings-regular.eot
│                       ├── glyphicons-halflings-regular.svg
│                       ├── glyphicons-halflings-regular.ttf
│                       └── glyphicons-halflings-regular.woff
├── index.html
├── robots.txt
├── scripts
│   ├── 6b5da0e8.plugins.js
│   ├── b6c3df09.main.js
│   ├── db02b173.vendor.js
│   └── vendor
│       └── fbe20327.modernizr.js
└── styles
    └── 444ed944.main.css

9 directories, 13 files


これで、ひと通りのワークフローが完了です。


ここからは、上記の手順の中でハマったポイントをいくつか紹介。


 

ハマりポイント

Node.js のバージョンが古かったのと npm のキャッシュの消し方の問題というのがほとんどの原因だったのですが、それに気付くのに時間が掛かってしまいました。。


ハマりポイント①

「yo webapp」実行時に、以下のようなエラーが。
Node.js のバージョンが「v0.10.13」だと古くてダメだったようです。

npm ERR! Error: EMFILE, open '/var/folders/rc/_0bgj5gd2d3_1_r87wq7x4fh0000gn/T/npm-17294/1406648702666-0.9199138130061328/package/package.json'
npm ERR! If you need help, you may report this log at:
npm ERR!     <http://github.com/isaacs/npm/issues>
npm ERR! or email it to:
npm ERR!     <npm-@googlegroups.com>

npm ERR! System Darwin 13.3.0
npm ERR! command "node" "/usr/local/bin/npm" "install"

/usr/local/lib/node_modules/npm/lib/utils/error-handler.js:266
  log.error("cwd", process.cwd())
                           ^
Error: EMFILE, too many open files
    at process.errorHandler (/usr/local/lib/node_modules/npm/lib/utils/error-handler.js:266:28)
    at process.EventEmitter.emit (events.js:117:20)
    at process._fatalException (node.js:272:26)


-----------------------------------------
Update available: 1.2.1 (current: 1.1.2)
Run npm update -g yo to update
-----------------------------------------

events.js:74
        throw TypeError('Uncaught, unspecified "error" event.');
              ^
TypeError: Uncaught, unspecified "error" event.
    at TypeError (<anonymous>)
    at Appgenerator.EventEmitter.emit (events.js:74:15)
    at done (/usr/local/lib/node_modules/generator-webapp/node_modules/yeoman-generator/lib/base.js:318:16)
    at /usr/local/lib/node_modules/generator-webapp/node_modules/yeoman-generator/node_modules/async/lib/async.js:232:13
    at /usr/local/lib/node_modules/generator-webapp/node_modules/yeoman-generator/node_modules/async/lib/async.js:113:21
    at /usr/local/lib/node_modules/generator-webapp/node_modules/yeoman-generator/node_modules/async/lib/async.js:24:16
    at /usr/local/lib/node_modules/generator-webapp/node_modules/yeoman-generator/node_modules/async/lib/async.js:229:17
    at /usr/local/lib/node_modules/generator-webapp/node_modules/yeoman-generator/node_modules/async/lib/async.js:516:34
    at Appgenerator.<anonymous> (/usr/local/lib/node_modules/generator-webapp/node_modules/yeoman-generator/lib/actions/install.js:43:7)
    at ChildProcess.EventEmitter.emit (events.js:117:20)

 

ハマりポイント②

「sudo npm install -g yo bower grunt-cli」実行時、以下のようなエラーが。

npm ERR! tar.unpack error reading /Users/akiyoko/.npm/mout/0.9.1/package.tgz
npm http 200 https://registry.npmjs.org/is-tar/-/is-tar-0.1.1.tgz
npm http 200 https://registry.npmjs.org/is-7zip
unbuild bower@1.3.8
npm http GET https://registry.npmjs.org/is-7zip/-/is-7zip-0.1.0.tgz
npm ERR! Error: ENOENT, chown '/usr/local/lib/node_modules/bower/node_modules/stringify-object/stringify-object.js'
npm ERR! If you need help, you may report this log at:
npm ERR!     <http://github.com/isaacs/npm/issues>
npm ERR! or email it to:
npm ERR!     <npm-@googlegroups.com>

によると、

$ sudo chown -R `whoami` ~/.npm
$ sudo chown -R `whoami` /usr/local

をしろと。
というかそもそも、sudo で npm install yo とかしてたのがダメだったっぽい。
sudo を付けずに npm install できるようになっていないとダメなのでした。



ハマりポイント③

Node.js のバージョンが「v0.10.13」のままだと、「npm install -g yo bower grunt-cli」で、

  • yo: 1.1.2
  • bower: 1.3.8
  • grunt-cli: 0.1.13

が入るのですが、yoのバージョンが「1.1.2」だと、やはり「yo webapp」実行時に次のようなエラーで止まってしまいます。
一旦、「npm uninstall -g yo bower grunt-cli」でアンインストールしてから、再度入れ直しましょう。

> pngquant-bin@0.1.7 postinstall /Users/akiyoko/yo_test/node_modules/grunt-contrib-imagemin/node_modules/image-min/node_modules/pngquant-bin
> node index.js

/
module.js:340
    throw err;
          ^
Error: Cannot find module 'mout/object/merge'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (/Users/akiyoko/yo_test/node_modules/grunt-contrib-imagemin/node_modules/image-min/node_modules/pngquant-bin/node_modules/bin-wrapper/index.js:8:13)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)
npm WARN optional dep failed, continuing pngquant-bin@0.1.7

 

ハマりポイント④

いろいろやってると、たまに(というか頻繁に?)

module.js:340
    throw err;
          ^
Error: Cannot find module '../lang/deepClone'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (/Users/akiyoko/yo_test/node_modules/grunt-contrib-imagemin/node_modules/image-min/node_modules/mout/object/merge.js:2:17)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)
npm WARN optional dep failed, continuing pngquant-bin@0.1.7

などというエラーが出てしまうことが。

その場合は、

$ rm -rf ~/.npm
$ npm cache clean

と実行してから、npm install を再実行するとうまくいくことが多いです。

(参考)


 

ハマりポイント⑤

Grunt のテストが何故か失敗してしまう問題。

$ grunt test
Running "test" task

Running "clean:server" (clean) task
Cleaning .tmp...OK

Running "concurrent:test" (concurrent) task
    
    Running "copy:styles" (copy) task
    
    
    Done, without errors.
    
    
    Execution Time (2014-07-31 11:21:05 UTC)
    loading tasks  15ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 33%
    copy:styles    29ms  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 64%
    Total 45ms
    
Running "autoprefixer:dist" (autoprefixer) task

Running "connect:test" (connect) task
Started connect web server on http://localhost:9001

Running "mocha:all" (mocha) task
Testing: http://localhost:9001/index.html

Warning: PhantomJS timed out, possibly due to a missing Mocha run() call. Use --force to continue.

Aborted due to warnings.


Execution Time (2014-07-31 11:20:58 UTC)
concurrent:test   7.4s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 37%
mocha:all        12.5s  ▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇ 62%
Total 20s

どうやら、testディレクトリで「bower install」してあげる必要があるようです。
詳しい理由は分かりませんでしたが。。

$ cd test/
$ bower install
bower chai#~1.8.0               cached git://github.com/chaijs/chai.git#1.8.1
bower chai#~1.8.0             validate 1.8.1 against git://github.com/chaijs/chai.git#~1.8.0
bower mocha#~1.14.0             cached git://github.com/visionmedia/mocha.git#1.14.0
bower mocha#~1.14.0           validate 1.14.0 against git://github.com/visionmedia/mocha.git#~1.14.0
bower chai#~1.8.0              install chai#1.8.1
bower mocha#~1.14.0            install mocha#1.14.0

chai#1.8.1 bower_components/chai

mocha#1.14.0 bower_components/mocha

参考




 

おまけ

なお、bower.json のバージョン等を直接修正した場合は、

$ bower update

して、パッケージを更新すればよいようです。