Takazudolog
2013-02-27T17:14:17+09:00
http://takazudo.github.com/blog
"Takazudo" Takeshi Takatsudo
takazudo@gmail.com
http://takazudo.github.com/blog/entry/2013-02-23-grunt04.html
Grunt v0.4.0 での変更点
2013-02-23T00:00:00+09:00
"Takazudo" Takeshi Takatsudo
http://takazudo.github.com/blog
<p><a href='http://gruntjs.com/'>Grunt</a> がめでたく version 0.4.0 になりました。version 0.3.xからの変更点をざっくり。自分で昔書いたタスクを書きなおす上で必要だったことのみですけど。</p>
<!--more-->
<p>箇条書きで書くとこんなかんじでした。</p>
<ul>
<li>ドキュメントがめっちゃ親切になった</li>
<li>Grunt本体はグローバルにインストールされなくなった</li>
<li>grunt-cliのみをグローバルにインストールすればOK</li>
<li>ビルトインのタスクは全てnpm moduleになった</li>
<li>Grunt本家がビルトインで存在していたタスクをnpm moduleとして用意した</li>
<li>コンフィグのファイルはgrunt.jsではなく、Gruntfile.js及びGruntfile.coffeeになった</li>
<li>コンフィグのファイル及びタスクはCoffeeScriptで書いても良くなった</li>
<li>タスク内でイベントが使えるようになった</li>
<li>テンプレート機能が改善</li>
<li>registerHelperが無くなった</li>
</ul>
<h2 id='id118'>ドキュメントが整備された件</h2>
<p>今までは頑張ってソースを読んだりなどもしなければ細かくはわかりませんでしたけど、かなり親切にドキュメントが用意されました。順に読んでいけば分かる感じになってます。cool</p>
<ul>
<li><a href='http://gruntjs.com/'>Grunt: The JavaScript Task Runner</a></li>
</ul>
<h2 id='grunt'>Grunt本体はグローバルにインストールされなくなった件</h2>
<p>今まで、Grunt本体は、<code>npm install -g grunt</code>で、グローバルにインストールしなければなりませんでした。しかし、これには問題がありました。その大きな理由は、異なるGruntのバージョンがインストールされている環境では、動かない可能性があるということです。私のマシンには v0.3.2 だけど あなたのマシンには v0.3.4 が入っていて、このプロジェクト、こっちの環境ではは動くけどあなたの環境では動かない。あーしかし複数のプロジェクトでGrunt使ってるのでアップデートも難しいなー…。ということがありましたが、これからはこれが無くなります。</p>
<p>Gruntは、<code>grunt-cli</code> というものを用意しました。これは、<code>npm install -g grunt-cli</code>でグローバルにインストールします。これをやらないと、Gruntは使えません。<code>grunt-cli</code>をインストールすると、<code>grunt</code>コマンドが使えるようになります。</p>
<p>ありゃ?それって変わってなくない?と思いきやそうではないのです。<code>grunt-cli</code>自体は、ローカルにインストールされたGruntを実行するということをだけをやる、非常に小さいコマンドです。Grunt本体はどうするかというと、そのプロジェクトのディレクトリに、ローカルにインストールします。</p>
<p>今まで、ローカルにインストールするのは、npm taskだけでした。しかし、v0.4からは、Grunt自身も<code>package.json</code>に<code>devDependencies</code>としてバージョンとともに指定し、ローカルにインストールする必要があります。で、<code>grunt</code>コマンドである、<code>grunt-cli</code>は、そのローカルのGruntを実行するだけのコマンドになので、プロジェクトごとに、異なるバージョンのGruntを使い分けることができるようになった - ってことみたいです。</p>
<h2 id='npm_module'>ビルトインのタスクは全てnpm moduleになった件</h2>
<p>今まで、watchとかconcatとかminとか、最初っから存在していたタスクは、全て無くなりました。その代わりに、これらは、独立したnpm moduleとして存在することとなりました。これらのタスクは、以下のプラグインのページより探すことができます。</p>
<ul>
<li><a href='http://gruntjs.com/plugins'>Plugins - Grunt: The JavaScript Task Runner</a></li>
</ul>
<p>contrib-で始まるものは全て、Grunt Teamが用意したタスクのようです。今までビルトインで存在していたタスクは、全て存在しています。今までのGruntプロジェクトをアップデートする場合は、使っていたビルトインのタスクを、<code>loadNpmTasks</code>で読み込む必要があります。コンフィグファイルの書き方は、ほとんど変わっていないので、比較的スムーズに移行できる感じです。</p>
<p>このようにビルトインのタスクが独立したため、Grunt自体が担う機能は、Gruntのベースとなる部分に絞られました。こうしないと、ビルトインのタスクが無限に増えてGrunt自体がどんどんでかくなってしまうため、将来性を考慮した設計と言えそうです。</p>
<p>ついでに、npm taskは、どこの誰が作ったか分からない、動作するか、将来的にもメンテされていくのか不安があるものでした(個人の感想)。Grunt Teamが作った公式のnpm taskが用意されることで、これらのnpm taskの動作は、かなり信頼性のあるものであると思っていいんじゃないでしょうか。</p>
<h2 id='gruntfilejs__gruntfilecoffee'>Gruntfile.js / Gruntfile.coffee</h2>
<p>今までコンフィグのファイルは<code>grunt.js</code>でしたが、これからは<code>Gruntfile.js</code>か<code>Gruntfile.coffee</code>です。<code>RakeFile</code>や<code>MakeFile</code>と合わせたんじゃないでしょうか多分。</p>
<h2 id='coffeescript'>CoffeeScript</h2>
<p><code>Gruntfile.coffee</code>の拡張子から分かるように、CoffeeScriptも利用可能になりました。タスクのファイルも同様です。こういうコンフィグを書く分には、CoffeeScriptはシンプルに書けるというメリットしかなさそうなので、いい感じなんじゃないでしょうか。</p>
<h2 id='id119'>タスク内でイベントが使えるようになった</h2>
<p>今まで、タスクをまたいだ処理は書きづらいものとなっていました。ですがv0.4.0からは、イベントが実装されたので、かなり自由に処理を書くことができます。</p>
<ul>
<li><a href='http://gruntjs.com/api/grunt.event'>grunt.event - Grunt: The JavaScript Task Runner</a></li>
</ul>
<h2 id='id120'>テンプレート機能が改善</h2>
<p>今まで、コンフィグ内の他のプロパティを読む場合、<code>'<config:taskName.src>'</code>とかいう書き方でしたが、これは<code>'<%= taskName.src %>'</code>と、Gruntのテンプレート機能を使うようになりました。よりテンプレート機能が汎用的なものとなったようです。</p>
<h2 id='registerhelper'>registerHelperが無くなった</h2>
<p>今まで、タスク間をまたぐ処理は、<code>grunt.registerHelper</code>を経由して行なっていました。ですが、この<code>registerHelper</code>は、Gruntの名前空間に自前のヘルパー関数を登録するような感じです。これを使うと、タスク同士でヘルパー名が競合する可能性がありました。よって、この機能は無くなりました。</p>
<p>複数のタスクで共有される機能を使う場合は、その共通機能自体をタスクとして別立てにするか、普通に<code>require</code>して読み込ませるかのどちらかの方法を取る必要があります。</p>
<p>…</p>
<p>という感じで、Grunt v0.4.0はイケてるなーと思いました。<br />今まで使ってた人は、「アップデートせなーーー!」と思うでしょうが、そこまで大変じゃないかと思います。自分でタスク書いてた人は諸々変える必要がありますけど。</p>
<p>私自身は、ちょっとしたタスクを自分で書いていたため、これを機に、v0.4.0に合わせて書き直しました。自分でタスク書きたいけどどう書くのか分からんという人は覗いてみると参考になる部分があるかもしれないです。</p>
<ul>
<li><a href='https://github.com/Takazudo/gruntExamples'>Takazudo/gruntExamples · GitHub</a></li>
</ul>
<p>自分のタスクを整理していて思いましたが、Gruntがリリースされて日がそんなに経っていない頃と比べると、npm taskの数と品質がずっと上がっており、たぶん、普通のことをしたいのであれば、自分でタスクを書く必要はもうあんまりないかと思います。いやーGruntすごいなー。</p>
http://takazudo.github.com/blog/entry/2013-02-06-videoaudio.html
地獄のvideo/audio要素
2013-02-06T00:00:00+09:00
"Takazudo" Takeshi Takatsudo
http://takazudo.github.com/blog
<p>video/audioにハマりまくったメモ。作ってたのは、Flashでやってたような、複数の動画ファイルを、途中の選択肢によって色々変えながら見せるというようなインタラクティブムービーみたいなの。なので、ハードにvideoを使いまくるという意味で、普通に一本動画を見せるという用途よりももっと色々する必要があったわけだけれども、そうでない場合でも、videoが色々厄介であるということは知っておく必要があると思う。</p>
<!--more-->
<p>とりあえず、videoやaudioを使いたい人は、以下の2ページを熟読せよ。</p>
<ul>
<li><a href='http://thinkit.co.jp/story/2013/01/25/3935'>プラグインは要らない!音声/動画対応したHTML5 - audio/video要素 | Think IT</a></li>
<li><a href='http://diveintohtml5.info/video.html'>Video - Dive Into HTML5</a></li>
</ul>
<p>videoのイベントは <a href='http://www.w3.org/2010/05/video/mediaevents.html'>HTML5 Video</a> を眺めてるとなんとなく分かる。</p>
<p>audioライブラリ試してうまくいったやつはこれ。</p>
<ul>
<li><a href='http://www.createjs.com/#!/SoundJS'>SoundJS</a></li>
</ul>
<p>videoのライブラリ、使ってないけど良さげなのはこれ。自分の場合、こまいことしたかったので、これじゃなくてもろもろ自分で書いた。</p>
<ul>
<li><a href='http://mediaelementjs.com/'>MediaElement.js</a></li>
</ul>
<p>以下メモ。</p>
<hr />
<p>Q: mp4やwebmが再生されない</p>
<p>AddType video/ogg .ogv<br />AddType video/mp4 .mp4<br />AddType video/webm .webm<br />AddType audio/mpeg .mp3<br />AddType audio/ogg .oga .ogg</p>
<p>をapacheに設定。設定しないとtextとかで返しちゃうから。(超重要)</p>
<hr />
<p>Q: <code>preload="auto"</code>にして、コントロールバー見ると、途中までしかダウンロードされてないんだけど</p>
<p>videoは全部をダウンロードしない。それが正常。</p>
<hr />
<p>Q: プリロードはできるの?</p>
<p>たぶん完全にはできない。その代わり、このネットワーク速度なら再生がダウンロードを追い越さないというぐらいまでダウンロードが完了したら、<code>canplaythrough</code>イベントが発火する。実質これがプリロードで、上記の途中までしかダウンロードされないのもこのため。さっぱりダウンロードさせたくなかったらvideo要素にprealod=”none”を指定しておき、<code>element.load()</code>で読み込み開始。画像のプリロードみたいな風に単純には行かない。</p>
<hr />
<p>Q: 時間をすごい先にしたら全部プリロードできちゃったりするんじゃないの?</p>
<p>できない。videoのレスポンスはPartial Contentで、よくわからんけどXHRみたいなもので、断片的に送られて来るらしい。なので、先まで時間すっ飛ばしたらその辺からダウンロードが開始される。</p>
<p>ちなみに、XHRで全部とってきておけばキャッシュするだろとか、とってきてdataURIとしてsrcに指定すれば全部プリロードできるだろとか思ってやってみたらブラウザが落ちたのでそういうアプローチはだめげ</p>
<hr />
<p>Q: <code>canplaythrough</code>が発火しない</p>
<p>FirefoxとOperaは<code>element.load()</code>しても読み込まない風。一度ビデオを再生させなければならない。<code>element.play()</code> <code>element.muted = true</code>して、50ms後ぐらいに<code>element.pause()</code> <code>element.muted = false</code>すれば読み込みを開始する。ひどい。あと、<code>element.readyState</code>が4以上になるのも監視したほうがいい。(よくわかってない。canplaythroughはreadyStateのラッパ?)</p>
<p>そして、上記をやり、再生し、すぐストップさせるっていうのをやるわけだけど、ここでまたOperaが<code>pause()</code>、<code>muted=true</code>を失敗し、プリロード中にvideoの音が垂れ流されるという現象が発生する。これはスペックにも依存するのだけれども、videoのプリロードは1つずつ行ったほうが良さそう。こんなこともあるため、ビデオの初め2秒ぐらいは無音状態にしておいたほうがいい。このへんがうまくいくか行かないかは、ビデオのクオリティにもよりそう。</p>
<p>また、このプレイポーズのプリロードキックは、非常に処理負荷の高い動作。そして、FirefoxとOperaでしか必要のないことなので、ブラウザ判別して、この2つのブラウザだけにこれを行ったほうがいい。具体的にはこれを全ブラウザに対してやると、IE9やら10が落ちる可能性がある。</p>
<hr />
<p>Q: <code>element.pause()</code>とか<code>element.muted = true</code>とか<code>element.currentTime=0</code>とか失敗する。</p>
<p><code>try</code>/<code>catch</code>せよ。これらは失敗する可能性がある。IE9では<code>element.currentTime</code>をいじれるのは再生した後。そして、可能な限り必要のない時にvideoのメソッドを呼ばないほうがいい。たぶん、ビデオ関連の操作は、ブラウザにとって特殊な操作であると考えられるのかなと感じた。外部のAPIを呼んだり、ハードウェアに近い部分を操作するという意味で。</p>
<hr />
<p>Q: <code>canplaythrough</code>って信じられるの?</p>
<p>なぜか特定のネットワークだとバッファで止まったりしまくったことがあった。また、マシンスペックがへぼいと、回線速度が良好でも処理が追いつかなくて止まる事がある。ブラウザの実装次第。そんなこんなで、重すぎるコンテンツは低スペックPCだと無理になる。そしてマシンスペックを知る方法とかたぶん無い。しかしまぁ、大抵の場合は<code>canplaythrough</code>を信じてやるしか無いし、概ね問題ない。</p>
<hr />
<p>Q: フォーマットって何用意すればいいの?</p>
<p>video: webmとh264。audio: mp3とogg。Firefox、Opera、Chromeがwebm/ogg、IE9、IE10、iOS、Chromeがh264/mp3。(2013年2月時点)</p>
<hr />
<p>Q: エンコードって何ですればいいの?</p>
<p>これが楽。<a href='http://www.mirovideoconverter.com/'>Miro Video Converter</a></p>
<hr />
<p>Q: webmの最後のフレームが静止状態で1秒ぐらい続いちゃうんだけど</p>
<p>多くのエンコーダーではそのようになってしまう。気になるならこれでエンコードする。<a href='http://www.xmedia-recode.de/'>XMedia Recode</a></p>
<hr />
<p>Q: video要素をそのままHTMLに書いた方がいいの?オンザフライで作った方がいいの?</p>
<p>状況によるけど、沢山のvideo要素を扱う場合は、必要な物だけオンザフライで作ったほうが良かった。videoに<code>preload="none"</code>を指定していても、IE9, 10ではそれを全部、はじめの部分だけ読みに行き、メモリ不足がどうとか言い出してエラー出しまくって最悪フリーズした。オンザフライで作る場合は、<code>Modernizr.video.webm</code>、<code>Modernizr.video.h264</code>で判別して対応するファイルへのパスをvideoのsrcに指定する。っていうのをやった。</p>
<hr />
<p>Q: 読込中にブラウザがフリーズします</p>
<p>自前でキュー作って順番にロードせよ。</p>
<hr />
<p>Q: <code>element.play()</code>しても0.5秒ぐらい再生が始まらない (IE10)</p>
<p>マシンスペックによる。低スペックなマシンだと起こる。回避無理そう。</p>
<hr />
<p>Q: videoのプレイ進捗が知りたい</p>
<p><code>element.currentTime</code>に現在の再生位置時間が入ってる。<code>timeupdate</code>イベントがプレイ中に起こり続ける。ただし、<code>timeupdate</code>は1秒間に1〜4回ぐらいしか発火しない。もっと細かく取りたかったらtimeupdate間でタイマー回して取るのが吉か。</p>
<hr />
<p>Q: <code>ended</code>イベントが発火しない(IE10)</p>
<p>低スペックPCで発生する。<code>element.play()</code>時に<code>new Date</code>して、タイマー回して、<code>element.duration</code>分経ったかを合わせてチェックするっていうのをやった。</p>
<hr />
<p>Q: Safari5で<code>element.load()</code>が効かない</p>
<p>Safari5のvideoの実装は不十分すぎでめちゃくちゃ微妙。<code>element.preload = 'auto'</code>で読み込みが開始される。</p>
<hr />
<p>Q: Safari5/6でブラウザがフリーズする</p>
<p>Mac OSX 10.8(Moutain Lion)未満では、複数のvideo要素を扱う時、Safari5でvideoのロード等の処理負荷が一定以上になると、ブラウザがフリーズする。まずは、video要素自体は<code>preload="none"</code>で作っておき、一定の間隔を置いて一つずつ<code>preload="auto"</code>にしていくとかして、慎重に扱わないとフリーズする。作るものによっては、Mac OS X 10.8未満のSafari(5/6)は非サポートとしたほうが良さそう。ちなみに、HTMLにvideo要素を直接書いた方が安定した気がしたかも。Mac OSX 10.8のSafari6ではこの現象は起こったことがない。</p>
<p>そして、この、10.7 (Lion)、10.6 (Snow Leopard) のSafari5/6では、マシンによってこの現象が起こるものと起こらないものがあるみたい。要するにスペックやらCPUやらグラボに起因する問題なのかなと思われた。</p>
<hr />
<p>Q: IE10が落ちる</p>
<p>IE10はChromeの次ぐらいに優秀なんだけれども、処理性能はマシンスペック依存。でかすぎるvideoはIE10を落としたり不安定にするんで、控えめにvideo使わないとダメかもしれない。低スペックPCでも見れるコンテンツを安全に作りたかったら重いコンテンツはNGかと思う。</p>
<hr />
<p>Q: 読み込みに失敗するんだけど</p>
<p><code>error</code>イベントをしっかり取ってエラーを出しましょう。あと、同時にいっぱい読むとエラーが起こりやすい。ビデオ読み込みのエラーはどこでもいずこかで発生するものと思ったほうがいい。</p>
<hr />
<p>Q: 複数タブで同じページを表示すると片方でvideoを読み込まない</p>
<p>たぶんブラウザが悪いけどなぜかそうなる。</p>
<hr />
<p>Q: IE10/9で「この操作を完了するための十分な記憶域がありません」</p>
<p>IE10ではvideoを18個作って、ひと通り再生してリロードしたら、このエラーが出て、関係ないところのJSがエラー出しまくりました。これを14個にしたらこのエラーが出なくなった。これは、動画のクオリティには関係ない話らしい。高画質でも低画質でも18個だとエラー出たし、14個だとエラー出なかった。videoはいっぱい作っちゃダメっぽいですな。ちなみにこれもマシンによって出たり出なかったりするんで…。</p>
<hr />
<p>Q: 連続してSEみたいな短いaudioを鳴らせない</p>
<p>5個ぐらい作って代わる代わるplayさせるといいんじゃない… 僕はそうしました…</p>
<hr />
<p>Q: Safariで短いaudioが鳴らない</p>
<p>videoだのaudioだのと言っても、Safariにとってそれは、QuickTimeのAPIを内部的に呼んでいる風。QuickTime自体が短すぎるmp3を鳴らせない。無音部分を後ろに追加せよ…</p>
<hr />
<p>Q: canvasにvideoを取り込みたい</p>
<p>context.drawImageすればいい</p>
<hr />
<p>Q: <code>muted</code>属性がきかない</p>
<p>たぶんブラウザの実装が不十分で効かないのがある。playさせてから<code>element.muted = true</code>なりなんなりしてやると効いたりする?</p>
<hr />
<p>Q: Safariで<code>display:none</code>にしたvideoの読み込みが開始されない</p>
<p><code>display:block</code>にしたら読み込んだ気がした確か。</p>
<hr />
<p>Q: Operaでやたら重い</p>
<p>videoを<code>opacity:0</code>で隠しておくとOperaでやたら重くなる。<code>display:none</code>の方がいい。</p>
<hr />
<p>Q: Safariでなんかしらんけど真っ暗</p>
<p>displayを<code>block</code>にしたり<code>none</code>にしたりしてるとなった。だから隠すには<code>opacity:0</code>のほうがいいのかも。えーっとOperaは諦めた。</p>
<hr />
<p>Q: windows XPでやたら変</p>
<p>同じバージョンのブラウザであっても、windows XPでは音や映像が飛びまくったり止まったりなどして、何か色々おかしい。Vista以上だと大丈夫。要するにXPだとかなり無理。</p>
<hr />
<p>Q: ちょっと前のブラウザで何かおかしい</p>
<p><code>Modernizr.video</code>が<code>true</code>を返したとしても、ブラウザの実装が完璧なわけではない。動かない可能性があるとかなんとか、常に出したほうがいいかも。</p>
<hr />
<p>Q: iOSでvideoを<code>visibility:hidden</code>にしてるけどなんか表示されちゃう</p>
<p>videoがHTML内にある時点で、ブラウザは何かしら処理を始めちゃうって思ったほうがいい。<code>document.createElement('video')</code>からオンザフライで色々してください</p>
<hr />
<p>Q: ビデオの再生がダウンロードに追っつかなくなった時に何かしたい</p>
<p><code>stalled</code>というイベントがあるようだけど試してない。対応していないブラウザに対しては<code>timeupdate</code>毎に、3000ms以内にまた発生しないか等をチェックしたりする必要があるかもしれない。</p>
<hr />
<p>Q: サーバーが落ちます/負荷が高すぎて耐えられない</p>
<p>普通のサーバーでアクセス数が多いなら死にます。フロントだけでなく、バックエンドも色々と検討する必要があるでしょう。</p>
<hr />
<p>Q: Chromeでビデオの読み込みに失敗する</p>
<p>stackoverflowによれば、Chromeでは7本以上のビデオを一つのページに読み込むとエラーになるらしい。</p>
<ul>
<li><a href='http://stackoverflow.com/questions/10658654/chrome-html5-videos-stop-working-if-too-many-tabs-are-open-memory-issue'>Chrome HTML5 Videos stop working if too many tabs are open - Memory issue? - Stack Overflow</a></li>
</ul>
<p>この現象は、自分の環境では、Windows端末にて確認できた。しかし、全てのWindows端末で起こるわけではないようだ。この現象が起こるChromeを判別するのは、多分ムリだろう。アップデートを待つしか無い。</p>
<hr />
<p>Q: IE10を閉じるとIEが強制終了されましたと出る</p>
<p>window onunload時に全てのvideoをremoveしたら直ったかもしれない</p>
<hr />
<p>Q: ユーザーにvideoやmp3をダウンロードさせたくないんですけど</p>
<p>諦めろ(よく聞かれる)<br />もしくはすっごい予算かけてなにかすごいサーバーの仕組みを作る</p>
<hr />
<p>Q: モバイルでvideoやaudioを使いたい</p>
<p>ただの動画再生ならYouTubeを使いましょう。もしくはかなり工数がかかることを覚悟して望まないとダメかも。iOSはそもそもvideoを再生すると全画面表示なってしまうので、それ以外の用途では無理。audioは最初に紹介したSoundJSがそこそこなんとかしてくれるっぽいけど、これもまた大変そうな感じ。よく聞かれるのが、スマホ向けにも動画を見せたいとか言うやつなんだけれども、その問いに対する自分の答えはだいたい</p>
<ol>
<li>YouTube使え</li>
<li>h264とwebm用意して見れないやつはあきらめたほうがいい</li>
<li>無限に工数と予算が増えることを許可してくれるならやりますけど動作は保証できないんでほとんどコストに見合わないと思う</li>
</ol>
<p>PCでは、いくつかのフォーマットを用意すれば、モダンブラウザで動作させることが可能だろう。しかし、iOSやAndroidは、バージョンや端末により、高ビットレート、フレームだと再生できないことがあるらしい(私自身は試してない。聞いた話、ググった内容等から)。Youtubeの中の人によると、Youtubeでは、ひとつの動画に対し、20本近くのトランスコードを行なっているそうだ。端末の解像度やUAなどにより、出す動画を選択しているとのこと。それをおおよそ普通の案件で行うのは無理だろう…。おおよそほとんどの端末で見れるようにするためには、低ビットレートの動画になってしまうのかなと思う(それでも再生されるかは分からない)</p>
<hr />
<p>Q: videoを使うのは大変なんでしょうか</p>
<p>ただの再生ならそこまでではないけども、APIやイベントを使って色々したいのであれば、かなり大変という印象。まだまだこれからっていう感じ。Chromeだけならとても楽しく利用できる…。canvasと組み合わせたりするとかなり夢広がる。2012年2月時点での自分の感想は、一番マシなのはChromeで、Chrome以外は全部微妙で不具合がある(それでも動かないChromeもある)。特に多数のvideoを使うとどれでもかなりダメ。</p>
<p>そんでもって、video非対応なブラウザ、videoが微妙なブラウザに対しても色々メッセージ出したりなんのしないといけないんでそのへんも地味に大変。</p>
<hr />
<p>そんなvideoに苦労したサイトはこちら。</p>
<ul>
<li><a href='http://www.itsumokawaii.jp/'>Interactive Film “itsumo kawaii” - KIRINJI</a></li>
</ul>
http://takazudo.github.com/blog/entry/2012-12-10-oocsssass.html
OOCSSとSass
2012-12-10T00:00:00+09:00
"Takazudo" Takeshi Takatsudo
http://takazudo.github.com/blog
<p><a href='http://www.adventar.org/calendars/1'>CSS Preprocessor Advent Calendar 2012</a>の10日目、<a href='http://twitter.com/Takazudo'>@Takazudo</a>です。</p>
<p>僕はSassが好きです。なぜならSassには<code>extend</code>があるからです。その理由を、社内勉強会で発表した、以下のスライドで話したことを補足しつつ、書きます。</p>
<ul>
<li><a class='apply-nolazy' href='/presentation-oocss-sass/#/'>OOCSS + Sass</a></li>
</ul><!--more-->
<h2 id='oocss'>OOCSSってなんぞ</h2>
<p>まず、Sassの<code>extend</code>が素敵な点を紹介する前に、OOCSSについて簡単に紹介します。</p>
<p>OOCSSというのは、Nicole Sullivanという人が言い出した考え方です。そのプレゼン資料やビデオなどは、本人が発表したものが、slideshare等にアップされています。</p>
<ul>
<li><a href='http://oocss.org/'>Object-oriented CSS</a></li>
<li><a href='http://fronteers.nl/congres/2009/sessions/object-oriented-css'>Object Oriented CSS by Nicole Sullivan · Fronteers</a></li>
<li><a href='http://www.slideshare.net/stubbornella/the-cascade-grids-headings-and-selectors-from-an-oocss-perspective-ajax-experience-2009'>The Cascade, Grids, Headings, and Selectors from an OOCSS Perspecti…</a></li>
</ul>
<p>OOCSSのサイトの例えば<a href='http://oocss.org/grids_docs.html'>このページ</a>を見ると、divのclass属性に<code>size1of3</code>だと<code>size2of3</code>だとかいうのが使われているのがわかります。さっとだけOOCSSのことを見た人の中には、「あーOOCSSって、そういう便利系クラスをいっぱい付けてやるスタイルのことなのね。知ってるわーそれ2年前からやってわー知ってたわー」という人が、なぜか多いのですが、OOCSSというのはそういうことじゃーないんです。</p>
<p>OOCSSとは、ページの部品を「モジュール」という単位で分けて考えることで、サイトのメンテナンス性とパフォーマンスを上げる考え方の一つです。そして、複雑な部品については、このモジュールについて、他のプログラミング言語における、クラス(HTMLのclass属性とは異なる、プログラムにおける雛形の意)の継承の考え方を利用し、効率的に拡張していく…というのが、OOCSSのキモとなる部分であると、僕は捉えています。その実現の方法として、class属性に複数のクラスを当てる手法をとる… ということになります。</p>
<p>それを、簡単なモジュールを作りつつ、見ていくことにします。</p>
<h2 id='id115'>黒か白か</h2>
<p>まず、TwitterとFacebookのボタンを作ってみます。<br />Twitterボタンは赤、Facebookボタンは青…としましょう。そんなコードを、おれならこう書くぜ!ドーーン</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/381bdd5da7799866ab3d7557e1ba8e450b4e55b2/Picture%2010.png' /></p>
<p>m9(^Д^)プギャー と言われてしまいそうですが、これは、よく非難されるような、典型的なダメなクラス名の付け方…あの時のオレはどうかしちまってたんだ… とも言えるようなクラス名です。これは、柔軟性はありますよね。うん… でもCSSって、一体何なんだろうね…</p>
<p>ちなみに、このサンプルでは、ただの赤青のボタンですが、これは、同じような見栄えを持つ、めちゃくちゃリッチなボタン… しかし、その2つの装飾は異なる…であると、応用をきかせて、想像してください。イマジネーションの側面が重要です。</p>
<p>そんなダメダメなコードをdisりまくる、セマンティック野郎の書いたコードがこれです。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/0f23e9c2eda9bdfe9d0cef44e63badc5abd36594/Picture%204.png' /></p>
<p>オーセマンティックデスネー<br />ではそのCSSを見てみましょう。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/ce1c88b8b3b8b89c8a1d3c044922b89f4ac9be56/Picture%2011.png' /></p>
<p>ハハハ・・・セマンティック最高だよね・・・<br />同じCSSをコピペして使うなんてさ・・・<br />おいおい、でもセマンティックは重要だろ?<br />クラス名には意味をもたせるべきだろ?<br />いやいやしかしこのCSSは無いでしょ。<br />ほかにも似たようなgoogle plusボタンができたらまたあんた同じCSSコピペするつもりでしょ?<br />じゃあどうすればいいっていうのさ!</p>
<p>ということになってしまいますよね。<br />この2つのコードは、言ってみれば、黒か白かという両極端なコードです。柔軟性を追求すれば、前者はまぁそりゃ柔軟であることには違いないですが、それ、CSSの意味ってなんなんですか?ってなりますし、後者はHTMLとしては理想ですが、CSSの中は大変なことになってしまいます。</p>
<p>あーほんとHTMLとCSSってなんでこう面倒くさいんでしょうね。</p>
<p>…こえますか?…聞こえますか?…僕はいま、あなたの脳内に直接語りかけています!OOCSSを使うのです!YES!</p>
<h2 id='oocss'>OOCSSっぽい書き方</h2>
<p>と、そんなコードを、OOCSSでは以下のように書きます。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/c55635236d2e963416aad56e4cd07e6ac5d6f4ca/Picture%201.png' /></p>
<p>OOCSSではーベースのモジュールに、スキンを適用する…というふうに書くのです。複数のクラス名を利用して。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/105c2b7dfb373d89e02b3a38093585c519c782cd/Picture%202.png' /></p>
<p>ここでのベースのモジュールというのは、クラス、<code>btnbase</code>が表現するもの。ここには、ボタンの共通スタイルが適用されています。その上で更に具体的なバリエーションは、<code>twitter</code>、<code>facebook</code>といったような、スキンとしてのクラスで適用するという形です。</p>
<p>これが、プログラミング言語における、「クラスの継承」っぽいでしょ?っていう感じでObjected Oriented CSSと名付けられたようです。まぁその呼び方はどうなのよっていう意見もありますが、要するにこういうのがOOCSSの考え方(の一部)です。これなら、CSSに無駄がありませんね。同じようなgoogle plusボタンが現れたとしても、<code>btnbase googleplus</code>っていうクラスをあててスタイルが適用されるように作ればいいんですから…!</p>
<h2 id='id116'>オイオイその冗談みたいなクラス名は何だ</h2>
<p>でもちょっとまってください。</p>
<p>「このクラス名のどこらへんがセマンティックっていうんだよ?え?<code>btnbase</code>だって?ふざけてんのかね。そんなのは<code>blackborder</code>がちょっとマシになったようなもんじゃないんですか?」</p>
<p>とセマンティック野郎が言いそうです。そのストリクトで真っ直ぐな精神は買いましょう。しかし、クラスをよりセマンティックにすれば、CSSはどんどん複雑に…。どうすれば…!という問に対しては、OOCSSは、「そんへんはある程度妥協して諦めろ」と答えます。OOCSSの考え方を用いれば、この<code>btnbase</code>のようなクラス名を作り、それをHTMLのclass属性に書くことになります。これはセマンティックなクラス名とは言いがたい…しかし!これがパフォーマンスとメンテナンス性を考慮した、最適解である…というのが、OOCSSの考え方です。なので、ただ単に便利なクラス名をポコポコ作って組み合わせれば素敵でしょ?っていうわけじゃないのです。</p>
<p>それにしたってそんな変なクラスを付けるなんて、中途半端な感じがしませんか?そうかも知れません。しかし、「CSS is too fragile」。CSSはあまりにも壊れやすい。CSSの機能は、はっきり言ってショボイ。プログラミング言語であれば、複雑さに対処する方法は色々と用意されています。しかし、CSSにはそのようなものがないと言ってしまっていいでしょう。とてつもなく長いCSSファイル - 赤の他人が書いたものを、すらすらと解読することができるでしょうか? それは、やっている人なら分かると思いますが、なかなかにしんどい作業です。</p>
<p>なんどでも言いたいこと、「CSSはショボイ」。しかしそれゆえに、その壊れやすいCSSを、丁寧に考えて書く必要があるのです。「そんな変なクラス」と呼んだもの、それは、具体的すぎず、なおかつ抽象的過ぎない、ちょうどいい具合の、そのUIを表現するクラス名ということになるでしょうね。</p>
<h2 id='__'>枠 + モジュール</h2>
<p>OOCSSは、このようなモジュール+スキンの考えに加え、枠組みを作って、その中にモジュールを突っ込んで使えるようにしろというような考え方もあります。このアンチパターンが、例えば以下の様なコードです。</p>
<div class='highlight'><pre><code class='css'><span class='nc'>.sidebar</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebar</span> <span class='nt'>h1</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebar</span> <span class='nt'>ul</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebar</span> <span class='nc'>.banners</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebar</span> <span class='nc'>.primarynav</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebar</span> <span class='nc'>.secondarynav</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
</code></pre>
</div>
<p>サイドバーというのは、色々なモジュールが入る可能性がある、「大きなもの」です。上のコードは、OOCSS的に言えば、そのサイドバーを「大きなモジュール」として捉えてしまっている書き方であると考えることができます。実際、上のコードでは<code>...</code>で済ませていますが、その中には<code>span</code>なり<code>li</code>なり別の要素がボンボン入り、ネストは深く深くなっていくはずです。そうなれば、サイドバーという大きなモジュールに対応するCSSは、どんどん長く、深くなっていってしまいます。</p>
<p>これは、「粒度が大きすぎる」状態です。もっと細かく、再利用可能な適切な単位のHTML+CSSの塊を「モジュール」として考えましょう。そうすれば、メンテナンス性もアップでしょ?この例だったら、例えば以下のように細かく、一つずつを小さな「モジュール」として捉えるのです。</p>
<div class='highlight'><pre><code class='css'><span class='nc'>.sidebar</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebarHeading</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebarList</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebarBanners</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebarPrimarynav</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
<span class='nc'>.sidebarSecondarynav</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
</code></pre>
</div>
<p>そうすれば、一つのモジュールが複雑になった時でも、見ればいいのはそのモジュールの部分だけですので、後からの変更も比較的容易です。ページを構成するUIを、適切な大きさの、小さなモジュールの集まりと考えることで、コードを整理するのです。</p>
<p>ついでに言うと、弊社<a href='http://www.pxgrid.com/'>ピクセルグリッド</a>では、コーディングする際、そのようなモジュール一覧を作ってから具体的なページを起こすようにしていますよ。例えば<a href='http://cdn.dropmark.com/2008/08dc7cea3fdde7aea49c8ffc17e82b8c6d06da63/modules1.png'>こんなふう</a>に。そして、モジュールの一番外側の要素には、クラス名として<code>mod-モジュール名</code>をつけているんです。そうすれば、クラス名が被るっていうこともかなり少なくなるでしょう?ついでに<a href='http://www.codegrid.net/'>CodeGrid</a>というサービスで記事を書いていたりもするので気になる方はチェックしてみてね…!</p>
<p>…と話がそれました。<br />そんな風に、モジュールで考えるのがOOCSSの基本的な考え方なのです。そのために、ある程度セマンティックでないクラス名は致し方なし。だって「The world is not always perfect」だからです。ある程度の抽象さをもったクラス名を使うという感じですね。繰り返しになりますが、<code>borderred</code>とかいうクラス名を付けることがOOCSSじゃないんですよ。</p>
<p>ページを構成するUIを、モジュール単位で整理して考えることが重要なんです。</p>
<h2 id='mixin__include'>mixin + include</h2>
<p>そんなこんなで、要素のclass属性を利用し、モジュールモジュールした組み方がOOCSSなのですが、Sassを使えば、そんなことをしなくても、セマンティック野郎の野望を実現します。それも、CSSの管理性も確保した上で…!そんな素敵なSassでこのボタンを書いた例がこれです。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/29804bd5211aa79748eaf5f849a5a114c199b1d8/Picture%205.png' /></p>
<p>HTMLには<code>twitterbtn</code>、<code>facebookbtn</code>という、コンテンツそのものを表現するようなクラス名を、そして、Sassファイルの中では、これらのクラスセレクタに対し、ベースとなる、今OOCSSで<code>.btnbase</code>として定義してたスタイル - これを<code>mixin</code>として定義しておき、これをまるっと<code>include</code>します。</p>
<p>こうすれば、先ほど問題になっていた問題が解決されているのが分かるでしょうか? クラス名はセマンティックで、CSSの管理性も確保されているのです…!ドヤーーというのがSassの素晴らしいところです。</p>
<h2 id='extend'>extend</h2>
<p>ですが、実は、このようにOOCSS的なことをSassでやるのには、<code>extend</code>の方が優れています。<code>extend</code>を使うと、以下の様なコードになります。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/77abda73288a4561c0fde5e086a9c0f4a25b10f7/Picture%206.png' /></p>
<p><code>.btnbase</code>に当てられたスタイル群をもってきますよーという感じです。</p>
<p>なぜ<code>extend</code>のほうがいいかというのは、出力結果のCSSを見てみるとわかります。左が、先の<code>mixin</code>の例が出力したCSS、右が今回の<code>extend</code>の例が出力したCSSです。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/3da06708835cba80f1759f7ad658b3cb07148d5a/Picture%207.png' /></p>
<p><code>mixin</code>を<code>include</code>した場合は、同じコードが2箇所に出力されてしまいます。それに対し、<code>extend</code>の場合は、同じスタイルらを、セレクタをまとめて出力してくれるのです。</p>
<p>HTML+CSSではできないことを平然とやってのけた…!痺れるし憧れますね。個人的には、これこそがSassのもたらす最大のメリットだなーと感じます。<code>extend</code>は、そんな風に素晴らしい構造性をCSSにもたらしてくれます。</p>
<p>このような書き方を、OOSassと呼ぶ人もいるようです。</p>
<h2 id='placeholder_selector'>placeholder selector</h2>
<p>ついでに言っておくと、Sass3.2からは、<code>placeholder selector</code>というものが追加されました。これを使うと、今出した<code>extend</code>の例が出力する<code>.btnbase</code>というモジュールのベースとなる、抽象的なクラスを、出力させないようにすることができます。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/9eeb09a81c43f0e04926590396b28560c59c2b7e/Picture%208.png' /></p>
<p>これの出力結果が、以下の右。左は、先のように、普通に<code>.btnbase</code>で<code>extend</code>した場合の出力です。<code>.btnbase</code>で<code>extend</code>した時と比べると、<code>%btnbase</code>で<code>extend</code>した方には、赤文字で書かれたクラスセレクタが存在していないことがわかります。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/efdd48766c34b140183a8e388d30459687ba6c64/Picture%209.png' /></p>
<p>我々は、Sassを利用することで、他のプログラミング言語に用意されている、「抽象クラス」のようなことを、実現できることとなります。おお、ブラボー・・・</p>
<h2 id='sassextend'>Sassのextendが素敵まとめ</h2>
<p>そんなSassの<code>extend</code>が素晴らしすぎるよねということを図にまとめると、以下のようになります。</p>
<p><img alt='' src='http://cdn.dropmark.com/2008/35da381d8cad4caec755596c54a466810d602cba/extending.png' /></p>
<p>OOCSSでは、CSSでベースとなるモジュールを作り、スキンを作り、そのスキン適用を、HTMLのclass属性上出やるしかありませんでした。しかし、Sassを使えば、それは全てSCSSファイルの中で完結します。モジュールにスキン適用…ここまでを全てSass任せにできるのです。</p>
<p>これで、はじめに出たセマンティックな人も柔軟性重視な人もニッコリというわけですね。いやーSassって素晴らしいなー。</p>
<h2 id='oocss'>でもやっぱOOCSSか?</h2>
<p>なー、Sassって素晴らしいなー… 完全無欠だなー… と思ってたんですよ。以下の記事を読むまでは。</p>
<ul>
<li><a href='http://oliverash.me/2012/09/07/methods-for-modifying-objects-in-oocss.html'>Methods for Modifying Objects in OOCSS (Extends: A Fool’s Gold) – Oliver Joseph Ash</a></li>
</ul>
<p>この記事が言っているのは、「OOCSS的なことをSassでやるのは確かに素晴らしいさ。でも、そんな風に<code>extend</code>しまくると、出力結果のCSSで、超大量のセレクタが並ぶことになり、結果的にCSSの容量がふえまくっちゃうことになるんだよ?」ということです。</p>
<p>これは、先程の<code>extend</code>の結果に並ぶ、</p>
<div class='highlight'><pre><code class='css'><span class='nc'>.btnbase</span><span class='o'>,</span> <span class='nc'>.twitterbtn</span><span class='o'>,</span> <span class='nc'>.facebookbtn</span> <span class='p'>{</span>
<span class='o'>...</span>
<span class='p'>}</span>
</code></pre>
</div>
<p>の部分ことを言っています。この例ですと、具体的なボタンのスキンは2つだけだからまだ良いです。しかし、この「具体的なボタン」というものが、100個ぐらいあったらどうでしょう。100このクラスセレクタがここに並ぶことになるんですよ。さらに、この例だと単純だからいいものの、実際には中にspanだの別の要素が並ぶことになります。そうなると、もう大量のセレクタがCSSに書きだされてしまう…!ということになってしまうんですよね。</p>
<p>これは確かに盲点です。それだったらHTMLのclass属性でOOCSSした方がいいかもしれません。しかしそうするとHTMLにスタイル指定の一部を担わせることになり… と、悩みはつきません。</p>
<h2 id='id117'>まとめ</h2>
<p>HTMLもCSSも完璧なものではありません。その完璧じゃない部分を、Sass等のCSSプリプロセッサは、大いに補強してくれるものですが、それで万事OKだというわけではない事も念頭に入れておく必要があるでしょう。</p>
<p>複雑な<code>extend</code>が容量の大きいCSSを作ってしまう… これは確かに問題ではありますが、かなりモジュールの数が増え、複雑になりすぎてしまった時にしか問題にならなそうではあります。</p>
<p>結局のところ、CSSプリプロセッサを使ったところで、「CSS is too fragile」の根本的な問題が解決されたわけではありません。その部分を解決するのは、やはりモジュール感を意識した無駄のない設計と言えるのではないでしょうか。</p>
<p>あなたはどのようにCSSを書いていますか? この記事を読むことで、少しでもCSSの設計に対する思慮が深まっていただけたら幸いです。</p>
<p><a href='http://www.adventar.org/calendars/1'>CSS Preprocessor Advent Calendar 2012</a>、次はコバさんです。</p>
<p>お読みいただき、ありがとうございました。</p>
http://takazudo.github.com/blog/entry/2012-12-03-imgloader.html
jQuery.ImgLoader has XHR2 feature now
2012-12-03T00:00:00+09:00
"Takazudo" Takeshi Takatsudo
http://takazudo.github.com/blog
<p>I just updated my library <a href='https://github.com/Takazudo/jQuery.ImgLoader'>jQuery.ImgLoader</a>.<br />It preloads imgs using XHR2, so now you can bind <code>progress</code> event to the loader.</p>
<!--more-->
<p>Here’s the demo.</p>
<ul>
<li><a href='/jQuery.ImgLoader/demo2/example.html'>demo</a></li>
</ul>
<p>It’s easy to use <code>progress</code> event.</p>
<div class='highlight'><pre><code class='javascript'><span class='kd'>var</span> <span class='nx'>loader</span> <span class='o'>=</span> <span class='nx'>$</span><span class='p'>.</span><span class='nx'>ImgLoader</span><span class='p'>({</span>
<span class='nx'>srcs</span><span class='o'>:</span> <span class='p'>[</span> <span class='s1'>'img1.jpg'</span><span class='p'>,</span> <span class='s1'>'img2.jpg'</span><span class='p'>,</span> <span class='s1'>'img3.jpg'</span><span class='p'>,</span> <span class='s1'>'img4.jpg'</span> <span class='p'>]</span>
<span class='p'>});</span>
<span class='nx'>loader</span><span class='p'>.</span><span class='nx'>on</span><span class='p'>(</span><span class='s1'>'progress'</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>progressInfo</span><span class='p'>){</span>
<span class='nx'>console</span><span class='p'>.</span><span class='nx'>log</span><span class='p'>(</span><span class='nx'>progressInfo</span><span class='p'>.</span><span class='nx'>loadedRatio</span><span class='p'>);</span> <span class='c1'>// 0.45 woot!</span>
<span class='p'>});</span>
<span class='nx'>loader</span><span class='p'>.</span><span class='nx'>on</span><span class='p'>(</span><span class='s1'>'itemload'</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>$img</span><span class='p'>){</span>
<span class='nx'>$</span><span class='p'>(</span><span class='s1'>'#somewhere'</span><span class='p'>).</span><span class='nx'>append</span><span class='p'>(</span><span class='nx'>$img</span><span class='p'>);</span>
<span class='p'>});</span>
<span class='nx'>loader</span><span class='p'>.</span><span class='nx'>on</span><span class='p'>(</span><span class='s1'>'allload'</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>$img</span><span class='p'>){</span>
<span class='nx'>alert</span><span class='p'>(</span><span class='s1'>'everything loaded!'</span><span class='p'>);</span>
<span class='p'>});</span>
<span class='nx'>loader</span><span class='p'>.</span><span class='nx'>load</span><span class='p'>();</span>
</code></pre>
</div>
<p>In the lib, it send XHR2 request before it creates img tag if the browser can use XHR2.</p>
<ul>
<li><a href='https://github.com/Takazudo/jQuery.ImgLoader'>jQuery.ImgLoader</a></li>
</ul>
http://takazudo.github.com/blog/entry/2012-07-20-grunt-loadnpmtasks.html
grunt loadNpmTasks
2012-07-20T00:00:00+09:00
"Takazudo" Takeshi Takatsudo
http://takazudo.github.com/blog
<p>Basically, with grunt, you need to code your own tasks.<br />But you can also use tasks on npm. Here’s howto.</p>
<!--more-->
<h2 id='growl'>growl</h2>
<p>Easy growling using <a href='https://github.com/alextucker/grunt-growl'>grunt-growl</a>.</p>
<div class='highlight'><pre><code class='javascript'><span class='nx'>module</span><span class='p'>.</span><span class='nx'>exports</span> <span class='o'>=</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>grunt</span><span class='p'>){</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>loadNpmTasks</span><span class='p'>(</span><span class='s1'>'grunt-growl'</span><span class='p'>);</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>initConfig</span><span class='p'>({</span>
<span class='nx'>growl</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>test1</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>message</span><span class='o'>:</span> <span class='s1'>'OMG!!!'</span><span class='p'>,</span>
<span class='nx'>title</span><span class='o'>:</span> <span class='s1'>'WOOOOT!!'</span>
<span class='p'>}</span>
<span class='p'>}</span>
<span class='p'>});</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerTask</span><span class='p'>(</span><span class='s1'>'default'</span><span class='p'>,</span> <span class='s1'>'growl'</span><span class='p'>);</span>
<span class='p'>};</span>
</code></pre>
</div>
<h2 id='coffeescript'>CoffeeScript</h2>
<p>Easy CoffeeScript compiling using <a href='https://github.com/avalade/grunt-coffee'>grunt-coffee</a>.<br />I want to be notified when the compiling successed, too.</p>
<div class='highlight'><pre><code class='javascript'><span class='nx'>module</span><span class='p'>.</span><span class='nx'>exports</span> <span class='o'>=</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>grunt</span><span class='p'>){</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>loadNpmTasks</span><span class='p'>(</span><span class='s1'>'grunt-growl'</span><span class='p'>);</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>loadNpmTasks</span><span class='p'>(</span><span class='s1'>'grunt-coffee'</span><span class='p'>);</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>initConfig</span><span class='p'>({</span>
<span class='nx'>growl</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>done</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>title</span><span class='o'>:</span> <span class='s1'>'grunt'</span><span class='p'>,</span>
<span class='nx'>message</span><span class='o'>:</span> <span class='s1'>'SUCCESSED!!'</span>
<span class='p'>}</span>
<span class='p'>},</span>
<span class='nx'>coffee</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>app</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>src</span><span class='o'>:</span> <span class='p'>[</span>
<span class='s1'>'coffee/1.coffee'</span><span class='p'>,</span>
<span class='s1'>'coffee/2.coffee'</span>
<span class='p'>],</span>
<span class='nx'>dest</span><span class='o'>:</span> <span class='s1'>'js'</span>
<span class='p'>}</span>
<span class='p'>},</span>
<span class='nx'>watch</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>app</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>files</span><span class='o'>:</span> <span class='s1'>'<config:coffee.app.src>'</span><span class='p'>,</span>
<span class='nx'>tasks</span><span class='o'>:</span> <span class='s1'>'coffee:app growl'</span>
<span class='p'>}</span>
<span class='p'>}</span>
<span class='p'>});</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerTask</span><span class='p'>(</span><span class='s1'>'default'</span><span class='p'>,</span> <span class='s1'>'coffee growl'</span><span class='p'>);</span>
<span class='p'>};</span>
</code></pre>
</div>
<h2 id='sass'>Sass</h2>
<p>Sass compiling using <a href='https://github.com/sindresorhus/grunt-sass'>grunt-sass</a>.</p>
<div class='highlight'><pre><code class='javascript'><span class='nx'>module</span><span class='p'>.</span><span class='nx'>exports</span> <span class='o'>=</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>grunt</span><span class='p'>){</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>loadNpmTasks</span><span class='p'>(</span><span class='s1'>'grunt-growl'</span><span class='p'>);</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>loadNpmTasks</span><span class='p'>(</span><span class='s1'>'grunt-sass'</span><span class='p'>);</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>initConfig</span><span class='p'>({</span>
<span class='nx'>growl</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>done</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>title</span><span class='o'>:</span> <span class='s1'>'grunt'</span><span class='p'>,</span>
<span class='nx'>message</span><span class='o'>:</span> <span class='s1'>'SUCCESSED!!'</span>
<span class='p'>}</span>
<span class='p'>},</span>
<span class='nx'>sass</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>app</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>src</span><span class='o'>:</span> <span class='s1'>'scss/style.scss'</span><span class='p'>,</span>
<span class='nx'>dest</span><span class='o'>:</span> <span class='s1'>'css/style.css'</span>
<span class='p'>}</span>
<span class='p'>},</span>
<span class='nx'>watch</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>app</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>files</span><span class='o'>:</span> <span class='s1'>'scss/*'</span><span class='p'>,</span>
<span class='nx'>tasks</span><span class='o'>:</span> <span class='s1'>'sass:app growl:done'</span>
<span class='p'>}</span>
<span class='p'>}</span>
<span class='p'>});</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerTask</span><span class='p'>(</span><span class='s1'>'default'</span><span class='p'>,</span> <span class='s1'>'sass growl:done'</span><span class='p'>);</span>
<span class='p'>};</span>
</code></pre>
</div>
<h2 id='compass'>Compass</h2>
<p>Compiling compass scss files using <a href='https://github.com/kahlil/grunt-compass'>grunt-compass</a>.</p>
<div class='highlight'><pre><code class='javascript'><span class='nx'>module</span><span class='p'>.</span><span class='nx'>exports</span> <span class='o'>=</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>grunt</span><span class='p'>){</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>loadNpmTasks</span><span class='p'>(</span><span class='s1'>'grunt-growl'</span><span class='p'>);</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>loadNpmTasks</span><span class='p'>(</span><span class='s1'>'grunt-compass'</span><span class='p'>);</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>initConfig</span><span class='p'>({</span>
<span class='nx'>growl</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>done</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>title</span><span class='o'>:</span> <span class='s1'>'grunt'</span><span class='p'>,</span>
<span class='nx'>message</span><span class='o'>:</span> <span class='s1'>'SUCCESSED!!'</span>
<span class='p'>}</span>
<span class='p'>},</span>
<span class='nx'>compass</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>app</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>src</span><span class='o'>:</span> <span class='s1'>'scss/'</span><span class='p'>,</span>
<span class='nx'>dest</span><span class='o'>:</span> <span class='s1'>'css/'</span>
<span class='p'>}</span>
<span class='p'>},</span>
<span class='nx'>watch</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>app</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>files</span><span class='o'>:</span> <span class='s1'>'scss/*'</span><span class='p'>,</span>
<span class='nx'>tasks</span><span class='o'>:</span> <span class='s1'>'compass:app growl:done'</span>
<span class='p'>}</span>
<span class='p'>}</span>
<span class='p'>});</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerTask</span><span class='p'>(</span><span class='s1'>'default'</span><span class='p'>,</span> <span class='s1'>'compass growl:done'</span><span class='p'>);</span>
<span class='p'>};</span>
</code></pre>
</div>
<h2 id='notice'>Notice</h2>
<p>Before using there npm tasks, you need to do <code>npm install grunt-coffee</code> in the directory which <code>grunt.js</code> exists.</p>
<p>Npm tasks are mostly well coded. But you need to know that they are not always perfect. Report bugs or send pull request to authors if they didn’t work. Or you need to code tasks by yourself.</p>
<p>You can check my grunt examples on <a href='https://github.com/Takazudo/gruntExamples'>Takazudo/gruntExamples</a>.<br />All examples here are in this repository too.</p>
http://takazudo.github.com/blog/entry/2012-04-14-grunt-coffee.html
Compiling CoffeeScript with grunt
2012-04-14T00:00:00+09:00
"Takazudo" Takeshi Takatsudo
http://takazudo.github.com/blog
<p><a href='http://coffeescript.org/'>CoffeeScript</a> is sweat.<br />It provides easy watch and has no problem about compiling without any other tools. But in concrete projects, we sometimes need more complicated compiling strategy. <a href='https://github.com/cowboy/grunt'>Grunt</a> provides nice APIs for automated builds for sass, less, stylus, CoffeeScript or something like that. In this article, I’ll introduce my grunt file for CoffeeScript.</p>
<!--more-->
<p>Note: This article is for grunt version 0.3.8. APIs may be changed in the future.</p>
<h2 id='installing_grunt'>Installing grunt</h2>
<p>To use grunt, install node and npm. Grunt needs to be installed as global with <code>-g</code>.</p>
<div class='highlight'><pre><code class='bash'><span class='nv'>$ </span>npm install -g grunt
</code></pre>
</div>
<p>I skip grunt’s basics here. You can see some examples on <a href='https://github.com/cowboy/grunt'>grunt</a>’s repository. Or check <a href='https://github.com/Takazudo/gruntExamples'>my repository</a> for grunt examples. Basically, create <code>grunt.js</code> to your directory. Then write build codes there. Then you can run the builds with the command like below.</p>
<div class='highlight'><pre><code class='bash'><span class='nv'>$ </span>grunt
</code></pre>
</div><div class='highlight'><pre><code class='bash'><span class='nv'>$ </span>grunt watch
</code></pre>
</div>
<p>If you have not used grunt yet. Just try to work something with grunt. It has standard features for JavaScript development. ex: jslint, concat, minify, test.</p>
<h2 id='how_to_compile_coffeescript_files_on_command_line'>How to compile CoffeeScript files on command line</h2>
<p>It’s listed on <a href='http://coffeescript.org/'>CoffeeScript’ website</a>. Here’s what I want to use.</p>
<div class='highlight'><pre><code class='bash'><span class='nv'>$ </span>coffee --compile --output .js/ .coffee/
</code></pre>
</div>
<p>With this, coffee compiles all <code>*.coffee</code> files in <code>coffee</code> directory. The compiled JavaScript files will be put into <code>js</code> directory.</p>
<div class='highlight'><pre><code class='bash'><span class='nv'>$ </span>coffee --join compiled.js --compile 1.coffee 2.coffee
</code></pre>
</div>
<p>With this, coffee concatenate <code>1.coffee</code> and <code>2.coffee</code> first. Then it compiles concanated sources to <code>compiled.js</code>.</p>
<p>CoffeeScript has also <code>--watch</code> option. But it’s better to automate these with build utilities. Rake, Make or something like that also seem good. But if you are familiar to JavaScript, grunt is better for you, I guess.</p>
<h2 id='helper_utility_tasks'>Helper, utility tasks</h2>
<p>Grunt doesn’t have the feature to compile CoffeeScript as default. So you need to write your own task for coffee compiling. First, let’s prepare helper functions / tasks for it. Here’s what I wrote.</p>
<div class='highlight'><pre><code class='javascript'><span class='cm'>/* grunt common utilities */</span>
<span class='nx'>module</span><span class='p'>.</span><span class='nx'>exports</span> <span class='o'>=</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>grunt</span><span class='p'>){</span>
<span class='kd'>var</span> <span class='nx'>exec</span> <span class='o'>=</span> <span class='nx'>require</span><span class='p'>(</span><span class='s1'>'child_process'</span><span class='p'>).</span><span class='nx'>exec</span><span class='p'>;</span>
<span class='c1'>// child_process.exec bridge</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerHelper</span><span class='p'>(</span><span class='s1'>'exec'</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>opts</span><span class='p'>,</span> <span class='nx'>done</span><span class='p'>)</span> <span class='p'>{</span>
<span class='kd'>var</span> <span class='nx'>command</span> <span class='o'>=</span> <span class='nx'>opts</span><span class='p'>.</span><span class='nx'>cmd</span> <span class='o'>+</span> <span class='s1'>' '</span> <span class='o'>+</span> <span class='nx'>opts</span><span class='p'>.</span><span class='nx'>args</span><span class='p'>.</span><span class='nx'>join</span><span class='p'>(</span><span class='s1'>' '</span><span class='p'>);</span>
<span class='nx'>exec</span><span class='p'>(</span><span class='nx'>command</span><span class='p'>,</span> <span class='nx'>opts</span><span class='p'>.</span><span class='nx'>opts</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>code</span><span class='p'>,</span> <span class='nx'>stdout</span><span class='p'>,</span> <span class='nx'>stderr</span><span class='p'>)</span> <span class='p'>{</span>
<span class='k'>if</span><span class='p'>(</span><span class='o'>!</span><span class='nx'>done</span><span class='p'>){</span>
<span class='k'>return</span><span class='p'>;</span>
<span class='p'>}</span>
<span class='k'>if</span><span class='p'>(</span><span class='nx'>code</span> <span class='o'>===</span> <span class='mi'>0</span><span class='p'>)</span> <span class='p'>{</span>
<span class='nx'>done</span><span class='p'>(</span><span class='kc'>null</span><span class='p'>,</span> <span class='nx'>stdout</span><span class='p'>,</span> <span class='nx'>code</span><span class='p'>);</span>
<span class='p'>}</span> <span class='k'>else</span> <span class='p'>{</span>
<span class='nx'>done</span><span class='p'>(</span><span class='nx'>code</span><span class='p'>,</span> <span class='nx'>stderr</span><span class='p'>,</span> <span class='nx'>code</span><span class='p'>);</span>
<span class='p'>}</span>
<span class='p'>});</span>
<span class='p'>});</span>
<span class='c1'>// growl: Ex. grunt.helper('growl', 'foo', 'bar');</span>
<span class='c1'>// http://growl.info/extras.php#growlnotify</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerHelper</span><span class='p'>(</span><span class='s1'>'growl'</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>title</span><span class='p'>,</span> <span class='nx'>msg</span><span class='p'>)</span> <span class='p'>{</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>helper</span><span class='p'>(</span><span class='s1'>'exec'</span><span class='p'>,</span> <span class='p'>{</span>
<span class='nx'>cmd</span><span class='o'>:</span> <span class='s1'>'growlnotify'</span><span class='p'>,</span>
<span class='nx'>args</span><span class='o'>:</span> <span class='p'>[</span>
<span class='s1'>'-t'</span><span class='p'>,</span> <span class='s2'>"'"</span> <span class='o'>+</span> <span class='nx'>title</span> <span class='o'>+</span> <span class='s2'>"'"</span><span class='p'>,</span>
<span class='s1'>'-m'</span><span class='p'>,</span> <span class='s2'>"'"</span> <span class='o'>+</span> <span class='nx'>msg</span> <span class='o'>+</span> <span class='s2'>"'"</span>
<span class='p'>]</span>
<span class='p'>});</span>
<span class='p'>});</span>
<span class='c1'>// ok: use this for notify everything are allright.</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerTask</span><span class='p'>(</span><span class='s1'>'ok'</span><span class='p'>,</span> <span class='s1'>'done!'</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(){</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>helper</span><span class='p'>(</span><span class='s1'>'growl'</span><span class='p'>,</span> <span class='s1'>'grunt.js'</span><span class='p'>,</span> <span class='s1'>'\(^o^)/'</span><span class='p'>);</span>
<span class='p'>});</span>
<span class='p'>};</span>
</code></pre>
</div>
<p>In the code above, first, I created <code>child_process.exec</code> wrapper. With this helper, you can pass the commands to <code>child_process.exec</code> easily. I’ll call this “exec” helper.</p>
<p>Next, I created “growl” helper. <a href='http://growl.info/extras.php#growlnotify'>Growl</a> is a great application for Mac (though if you use Mac, you may already know. And sorry for non-Mac users.). This notifies you something. I’ll use this to notify whether CoffeeScript’s compiling was successed or not. Install growlnotify extension if you’ve not installed it yet. You can see how I used “exec” helper here.</p>
<p>The last part of the code above is “ok” task. I’ll call this task when everythings was done without error. You can see how I used “growl” helper here.</p>
<p>“Helper” is like a function shared in “grunt” namespace. I’ll use “exec” helper more next.</p>
<h2 id='coffeescript_compiling_tasks'>CoffeeScript compiling tasks</h2>
<p>So let’s compile CoffeeScripts with this.</p>
<div class='highlight'><pre><code class='javascript'><span class='cm'>/**</span>
<span class='cm'> * coffee compiling tasks</span>
<span class='cm'> * CoffeeScript: http://coffeescript.org/</span>
<span class='cm'> */</span>
<span class='nx'>module</span><span class='p'>.</span><span class='nx'>exports</span> <span class='o'>=</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>grunt</span><span class='p'>){</span>
<span class='kd'>var</span> <span class='nx'>log</span> <span class='o'>=</span> <span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>log</span><span class='p'>;</span>
<span class='kd'>function</span> <span class='nx'>handleResult</span><span class='p'>(</span><span class='nx'>from</span><span class='p'>,</span> <span class='nx'>dest</span><span class='p'>,</span> <span class='nx'>err</span><span class='p'>,</span> <span class='nx'>stdout</span><span class='p'>,</span> <span class='nx'>code</span><span class='p'>,</span> <span class='nx'>done</span><span class='p'>)</span> <span class='p'>{</span>
<span class='k'>if</span><span class='p'>(</span><span class='nx'>err</span><span class='p'>){</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>helper</span><span class='p'>(</span><span class='s1'>'growl'</span><span class='p'>,</span> <span class='s1'>'COFFEE COMPILING GOT ERROR'</span><span class='p'>,</span> <span class='nx'>stdout</span><span class='p'>);</span>
<span class='nx'>log</span><span class='p'>.</span><span class='nx'>writeln</span><span class='p'>(</span><span class='nx'>from</span> <span class='o'>+</span> <span class='s1'>': failed to compile to '</span> <span class='o'>+</span> <span class='nx'>dest</span> <span class='o'>+</span> <span class='s1'>'.'</span><span class='p'>);</span>
<span class='nx'>log</span><span class='p'>.</span><span class='nx'>writeln</span><span class='p'>(</span><span class='nx'>stdout</span><span class='p'>);</span>
<span class='nx'>done</span><span class='p'>(</span><span class='kc'>false</span><span class='p'>);</span>
<span class='p'>}</span><span class='k'>else</span><span class='p'>{</span>
<span class='nx'>log</span><span class='p'>.</span><span class='nx'>writeln</span><span class='p'>(</span><span class='nx'>from</span> <span class='o'>+</span> <span class='s1'>': compiled to '</span> <span class='o'>+</span> <span class='nx'>dest</span> <span class='o'>+</span> <span class='s1'>'.'</span><span class='p'>);</span>
<span class='nx'>done</span><span class='p'>(</span><span class='kc'>true</span><span class='p'>);</span>
<span class='p'>}</span>
<span class='p'>}</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerHelper</span><span class='p'>(</span><span class='s1'>'coffee_dir_to_dir'</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>fromdir</span><span class='p'>,</span> <span class='nx'>dest</span><span class='p'>,</span> <span class='nx'>done</span><span class='p'>)</span> <span class='p'>{</span>
<span class='kd'>var</span> <span class='nx'>args</span> <span class='o'>=</span> <span class='p'>{</span>
<span class='nx'>cmd</span><span class='o'>:</span> <span class='s1'>'coffee'</span><span class='p'>,</span>
<span class='nx'>args</span><span class='o'>:</span> <span class='p'>[</span> <span class='s1'>'--compile'</span><span class='p'>,</span> <span class='s1'>'--output'</span><span class='p'>,</span> <span class='nx'>dest</span><span class='p'>,</span> <span class='nx'>fromdir</span> <span class='p'>]</span>
<span class='p'>};</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>helper</span><span class='p'>(</span><span class='s1'>'exec'</span><span class='p'>,</span> <span class='nx'>args</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>err</span><span class='p'>,</span> <span class='nx'>stdout</span><span class='p'>,</span> <span class='nx'>code</span><span class='p'>){</span>
<span class='nx'>handleResult</span><span class='p'>(</span><span class='nx'>fromdir</span><span class='p'>,</span> <span class='nx'>dest</span><span class='p'>,</span> <span class='nx'>err</span><span class='p'>,</span> <span class='nx'>stdout</span><span class='p'>,</span> <span class='nx'>code</span><span class='p'>,</span> <span class='nx'>done</span><span class='p'>);</span>
<span class='p'>});</span>
<span class='p'>});</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerHelper</span><span class='p'>(</span><span class='s1'>'coffee_multi_to_one'</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>srcs</span><span class='p'>,</span> <span class='nx'>dest</span><span class='p'>,</span> <span class='nx'>done</span><span class='p'>)</span> <span class='p'>{</span>
<span class='nx'>srcs</span> <span class='o'>=</span> <span class='nx'>srcs</span><span class='p'>.</span><span class='nx'>join</span><span class='p'>(</span><span class='s1'>' '</span><span class='p'>);</span>
<span class='kd'>var</span> <span class='nx'>args</span> <span class='o'>=</span> <span class='p'>{</span>
<span class='nx'>cmd</span><span class='o'>:</span> <span class='s1'>'coffee'</span><span class='p'>,</span>
<span class='nx'>args</span><span class='o'>:</span> <span class='p'>[</span> <span class='s1'>'--join'</span><span class='p'>,</span> <span class='nx'>dest</span><span class='p'>,</span> <span class='s1'>'--compile'</span><span class='p'>,</span> <span class='nx'>srcs</span> <span class='p'>]</span>
<span class='p'>};</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>helper</span><span class='p'>(</span><span class='s1'>'exec'</span><span class='p'>,</span> <span class='nx'>args</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>err</span><span class='p'>,</span> <span class='nx'>stdout</span><span class='p'>,</span> <span class='nx'>code</span><span class='p'>){</span>
<span class='nx'>handleResult</span><span class='p'>(</span><span class='nx'>srcs</span><span class='p'>,</span> <span class='nx'>dest</span><span class='p'>,</span> <span class='nx'>err</span><span class='p'>,</span> <span class='nx'>stdout</span><span class='p'>,</span> <span class='nx'>code</span><span class='p'>,</span> <span class='nx'>done</span><span class='p'>);</span>
<span class='p'>});</span>
<span class='p'>});</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerMultiTask</span><span class='p'>(</span><span class='s1'>'coffee'</span><span class='p'>,</span> <span class='s1'>'compile CoffeeScripts'</span><span class='p'>,</span> <span class='kd'>function</span><span class='p'>()</span> <span class='p'>{</span>
<span class='kd'>var</span> <span class='nx'>done</span> <span class='o'>=</span> <span class='k'>this</span><span class='p'>.</span><span class='nx'>async</span><span class='p'>();</span>
<span class='kd'>var</span> <span class='nx'>files</span> <span class='o'>=</span> <span class='k'>this</span><span class='p'>.</span><span class='nx'>data</span><span class='p'>.</span><span class='nx'>files</span><span class='p'>;</span>
<span class='kd'>var</span> <span class='nx'>dir</span> <span class='o'>=</span> <span class='k'>this</span><span class='p'>.</span><span class='nx'>data</span><span class='p'>.</span><span class='nx'>dir</span><span class='p'>;</span>
<span class='kd'>var</span> <span class='nx'>dest</span> <span class='o'>=</span> <span class='k'>this</span><span class='p'>.</span><span class='nx'>data</span><span class='p'>.</span><span class='nx'>dest</span><span class='p'>;</span>
<span class='c1'>// ex: ./coffee -> ./js</span>
<span class='k'>if</span><span class='p'>(</span><span class='nx'>dir</span><span class='p'>)</span> <span class='p'>{</span>
<span class='c1'>// if destination was not defined, compile to same dir</span>
<span class='k'>if</span><span class='p'>(</span><span class='o'>!</span><span class='nx'>dest</span><span class='p'>)</span> <span class='p'>{</span>
<span class='nx'>dest</span> <span class='o'>=</span> <span class='nx'>dir</span><span class='p'>;</span>
<span class='p'>}</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>helper</span><span class='p'>(</span><span class='s1'>'coffee_dir_to_dir'</span><span class='p'>,</span> <span class='nx'>dir</span><span class='p'>,</span> <span class='nx'>dest</span><span class='p'>,</span> <span class='nx'>done</span><span class='p'>);</span>
<span class='k'>return</span><span class='p'>;</span>
<span class='p'>}</span>
<span class='c1'>// ex: [ '1.coffee', '2.coffee' ] -> foo.js</span>
<span class='k'>if</span><span class='p'>(</span><span class='nx'>files</span><span class='p'>)</span> <span class='p'>{</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>helper</span><span class='p'>(</span><span class='s1'>'coffee_multi_to_one'</span><span class='p'>,</span> <span class='nx'>files</span><span class='p'>,</span> <span class='nx'>dest</span><span class='p'>,</span> <span class='nx'>done</span><span class='p'>);</span>
<span class='k'>return</span><span class='p'>;</span>
<span class='p'>}</span>
<span class='p'>});</span>
<span class='p'>};</span>
</code></pre>
</div>
<p>First, I created two helpers - <code>coffee_dir_to_dir</code> and <code>coffee_multi_to_one</code>. These are helpers which invoke coffee commands I wrote above. “Directory to directory” or “multiple files to one”. You can see how I used “exec” helper here ,too. The compiled results will be passed to <code>handleResult</code>. In <code>handleResult</code>, it shows growl if compiling got error. If not, it logs that compiling was done.</p>
<p>The last part of the code above defines <code>coffee</code> multiTask. In this task, grunt calls the helpers what I craeted from config values. Overall, this multiTasks is the surface of this task file. Let’s see how this works with concrete <code>grunt.js</code> file.</p>
<h2 id='grunt_file'>Grunt file</h2>
<p>Here’s how to use the tasks above.</p>
<div class='highlight'><pre><code class='javascript'><span class='cm'>/**</span>
<span class='cm'> * grunt</span>
<span class='cm'> * CoffeeScript example</span>
<span class='cm'> *</span>
<span class='cm'> * grunt: https://github.com/cowboy/grunt</span>
<span class='cm'> * CoffeeScript: http://coffeescript.org/</span>
<span class='cm'> */</span>
<span class='nx'>module</span><span class='p'>.</span><span class='nx'>exports</span> <span class='o'>=</span> <span class='kd'>function</span><span class='p'>(</span><span class='nx'>grunt</span><span class='p'>){</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>initConfig</span><span class='p'>({</span>
<span class='nx'>coffee</span><span class='o'>:</span> <span class='p'>{</span>
<span class='c1'>// Example1: compile multiple coffees to one js with "--join".</span>
<span class='nx'>dist1</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>files</span><span class='o'>:</span> <span class='p'>[</span> <span class='s1'>'coffee/1.coffee'</span><span class='p'>,</span> <span class='s1'>'coffee/2.coffee'</span> <span class='p'>],</span>
<span class='nx'>dest</span><span class='o'>:</span> <span class='s1'>'js/12.js'</span>
<span class='p'>},</span>
<span class='c1'>// Example2: compile one coffee to one js.</span>
<span class='nx'>dist2</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>files</span><span class='o'>:</span> <span class='p'>[</span> <span class='s1'>'coffee/3.coffee'</span> <span class='p'>],</span>
<span class='nx'>dest</span><span class='o'>:</span> <span class='s1'>'js/3.js'</span>
<span class='p'>},</span>
<span class='c1'>// Example3: compiled files are put in another dir.</span>
<span class='nx'>dist3</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>dir</span><span class='o'>:</span> <span class='s1'>'coffee/45/'</span><span class='p'>,</span>
<span class='nx'>dest</span><span class='o'>:</span> <span class='s1'>'js/'</span>
<span class='p'>},</span>
<span class='c1'>// Example4: compiled files are put in the same dir.</span>
<span class='nx'>dist4</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>dir</span><span class='o'>:</span> <span class='s1'>'insamedir/'</span>
<span class='p'>}</span>
<span class='p'>},</span>
<span class='nx'>watch</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>dist1</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>files</span><span class='o'>:</span> <span class='s1'>'<config:coffee.dist1.files>'</span><span class='p'>,</span>
<span class='nx'>tasks</span><span class='o'>:</span> <span class='s1'>'coffee:dist1 ok'</span>
<span class='p'>},</span>
<span class='nx'>dist2</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>files</span><span class='o'>:</span> <span class='s1'>'<config:coffee.dist2.files>'</span><span class='p'>,</span>
<span class='nx'>tasks</span><span class='o'>:</span> <span class='s1'>'coffee:dist2 ok'</span>
<span class='p'>},</span>
<span class='nx'>dist3</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>files</span><span class='o'>:</span> <span class='s1'>'coffee/45/*.coffee'</span><span class='p'>,</span>
<span class='nx'>tasks</span><span class='o'>:</span> <span class='s1'>'coffee:dist3 ok'</span>
<span class='p'>},</span>
<span class='nx'>dist4</span><span class='o'>:</span> <span class='p'>{</span>
<span class='nx'>files</span><span class='o'>:</span> <span class='s1'>'insamedir/*.coffee'</span><span class='p'>,</span>
<span class='nx'>tasks</span><span class='o'>:</span> <span class='s1'>'coffee:dist4 ok'</span>
<span class='p'>}</span>
<span class='p'>}</span>
<span class='p'>});</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>loadTasks</span><span class='p'>(</span><span class='s1'>'tasks'</span><span class='p'>);</span>
<span class='nx'>grunt</span><span class='p'>.</span><span class='nx'>registerTask</span><span class='p'>(</span><span class='s1'>'default'</span><span class='p'>,</span> <span class='s1'>'coffee ok'</span><span class='p'>);</span>
<span class='p'>};</span>
</code></pre>
</div>
<p>If files were passed, grunt compiles those with <code>--join</code>. Else if directory was passed, grunt compiles those as “directory to directory” compiling. With these task files, you can handle complicated compilings easily. And of course, you can minify or concat those files with other files.</p>
<p>These are also useful for other pre processors like <a href='http://sass-lang.com/'>sass</a>, <a href='http://lesscss.org/'>less</a>, <a href='http://learnboost.github.com/stylus/'>stylus</a> or something like that. I put the code I explained here on GitHub. Use these if it’s useful for you.</p>
<ul>
<li><a href='https://github.com/Takazudo/gruntExamples/tree/master/coffee-all-in-one'>Takazudo/gruntExamples - Coffee all in one</a></li>
<li><a href='https://github.com/Takazudo/gruntExamples'>Takazudo/gruntExamples</a></li>
</ul>
http://takazudo.github.com/blog/entry/2012-04-11-firstpost.html
This blog is powered by Jekyll
2012-04-11T00:00:00+09:00
"Takazudo" Takeshi Takatsudo
http://takazudo.github.com/blog
<p>This is the test post! yey! Jekyll is cool!<br />This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost.</p>
<!--more-->
<p>This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost. This is the test of this blogpost.</p>
<div class='highlight'><pre><code class='javascript'><span class='p'>(</span><span class='kd'>function</span><span class='p'>(){</span>
<span class='nb'>window</span><span class='p'>.</span><span class='nx'>foobar</span> <span class='o'>=</span> <span class='s1'>'awesome!'</span><span class='p'>;</span>
<span class='p'>})(</span><span class='nb'>window</span><span class='p'>);</span>
</code></pre>
</div>