Yeoman(ヨーマン)は、モダンなWebアプリのためのフロントエンド統合開発ツールです。Webアプリをスクラッチから作る際のややこしいワークフローを楽チンにしてくれる便利なツールと考えればよいでしょう。
Yeoman は、以下の3つのツールで構成されています。単体でも使うことができるのですが、それぞれのツールの親和性が高いので、3つをまとめて使いこなすと非常に効率がよいと思います。
Yo | 雛形作成ツール |
Bower | パッケージマネージャ |
Grunt | タスクランナ |
利用イメージは、公式ページの↓の図が非常に分かりやすいです。
図でも説明されているように、まずは 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/」が開かれます。
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>
- http://qiita.com/GenTamura84/items/39193a7d2867af2994f1
- http://stackoverflow.com/questions/12231846/npm-will-not-install-express
- http://apple.stackexchange.com/questions/1393/are-my-permissions-for-usr-local-correct
- http://stackoverflow.com/questions/20330273/error-installing-yeoman
によると、
$ 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 を再実行するとうまくいくことが多いです。
(参考)
- https://github.com/gruntjs/grunt-contrib-imagemin/issues/183
- http://stackoverflow.com/questions/20764881/why-does-npm-install-say-i-have-unmet-dependencies
ハマりポイント⑤
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
参考