パブリッシュでCoffeeScriptやSassが書き出されたりする環境をWebStormとCakefileで構築

正宗です。
今日はタイトルのとおりFlashみたいにパブリッシュしたら、自動的にCoffeeScriptやSassがコンパイルされて書き出される環境を、WebStormとCakefileを使って構築してみることにしました。
どういうことか順を追って説明しますね。

先ずは次のリンク先を読んでください。
FlashでいうパブリッシュのないHTML開発で、死なないためにやっといたらいいんじゃない的なこと。〜Ctrl+Enter Again〜
スーパー肩パッドさんがF-siteで発表した資料をまとめたものなんですが(俺もいきたかった〜!)ものすごくわかりやすくてものすごくためになります。
公開した瞬間から話題になったので もうすでに読んだ方のほうが多いと思いますが、まだ見の人がいましたらぜひ見でおねがいします。僕は百回読みました(本当は8回くらいかもしれない)。

で、僕の今回のこの記事は、上で紹介した肩パッドさんの記事にインスパイアされて書いた記事になります。上のリンク先の記事がすごすぎて、リンク先を読んだらこの記事は読まなくてもいいくらいなんです。ほんとすごい。

で、僕が最終的に何をやりたいかというと、順番に説明しますね。

  • Flashではflaファイルを作業にあてて、Ctrl+Enterでパブリッシュすることでswfファイルを作って、それをデバッグして最終的に納品ファイルにまとめている
  • あれすごいいいよね。同じようなことがHTML制作でもできないかな 
  • つまり制作はソースファイルで行って、書き出したファイルをチェック用に使い、最終的には納品ファイルとしてまとめる

こういうことがしたいのです。
あとでも説明しますが、こうすることによって得られるメリットは大きいように思えます。
何が便利なのか。
まずパブリッシュと同じような事をするためにCakefileというスクリプトを実行することになります。
このスクリプトにいろいろと処理を書いておけるので、例えばパブリッシュの際に不要ファイルを取り除く、といったことが出来るようになります。
他にもスクリプトで処理できることは色々とできるようになるので開発効率が大幅に上がるのが期待できるんですが、何よりこの記事で売りにしたいのが、CoffeeScriptやSassなどの技術が、日々の制作作業にわりかし簡単に取り入れられるようになること。です。
CoffeeScriptやSassなどはデザイナーさんも注目する、できるだけ試してみたい技術なはずですが、実際に案件に導入しようとなるとコンパイルの面倒くささが障壁となるのか、導入をためらう人も少なからずいると思います。少なくともうちのデザイナーさんはそのようです。

そこで、Flashで気軽にCtrl+Enterを押していたようなことを、HTML制作でもやってしまおう。と。そういう環境を構築しよう。と。
そういう狙いがあります。 

図にすると次のようなことになります。
 0

そしてこのようなことを実現するために、スーパー肩パッドさんの発表された資料を参考に次のように環境を構築してみました。
 1

ちょっとややこしそうだし、手順は複雑で道のりは長いように感じますが、せっかくなのでその環境を紹介したいと思います。

いちおう、記事の売りとしてはCoffeeScriptやSassを導入したいと思っていた人に、実際に業務で使える方法を構築する、というのがありますのでこの記事をきっかけに使うようになった、って人がいるととても嬉しいです。ですが、別にCoffeeScriptやSassを使いこなすのが前提なわけでもありません。
 CoffeeScriptやSassを必要としない人に向けては、そういうのを強要するわけではないので、むしろCtrl+EnterしたらCoffeeScriptやSassがあればついでにコンパイルされてたけど、無いなら無いで別にいい、そういった姿勢で読んでいただけるととても嬉しいです。

あと、いままでCoffeeScriptやSassの導入をしぶっていた人って、コンパイルのたびにコマンドを打たないといけないイメージがあって面倒だと思っていた人も多いはずです。僕もそうです。
今回のねらいのひとつに、WebStormでCakefileを使って処理を自動化する、というのもありますので、この環境を構築するとコマンドは打たなくてもよくなります。 

記事を通して、OSは主にMacを対象として書いています。ですができるだけWindowsでの方法も書いてみたいと思います。
ただWindowsは実際に僕も未検証な部分が多いのでその点はご理解ください。
一個一個はすでに検索すればWebにある情報ばかりなので、つまづいても検索してみると対処方法が必ず見つかると思います。 

あと、内容がプロジェクトの環境を構築するという重大な作業の話しですので、実際に導入する際は完全に自己責任でおねがいします。
実際に僕もこれが完成形ではなくここが出発点で、ここからトライアンドエラーを繰り返して開発効率を高めていこう、という主旨です。 


環境構築にはいろいろとややこしいのですが、フェーズとして大きく3つにわけました。

  1. インストールなど、PCに一回だけ必要な環境構築編
  2. プロジェクトごとに必要な設定編
  3. 毎回、作業の最初に必要な準備編

わりと面倒なインストール作業は、PCに一回やればすみます。
プロジェクトごとに必要な設定も、出来るだけ簡単になるように工夫しました。
毎日の作業はじめに必要な準備に関しては、作業はじめにちょっとアプリを立ち上げておくとか、その程度です。

どうしても僕が説明が下手で、すごく冗長な説明をしてしまって、そこらへんがややこしく思えるかもしれません。
ですができるだけ誰でも導入できるように書いてみたいと思います。というのもこの記事を書いた目的が、うちのデザイナー(プログラム経験とかゼロ)にぜひとも導入してもらいたいからです。

ちなみに記事内での表現の統一として、得に理由が無い限りは「コンパイル」という言葉を、できるだけ「パブリッシュ」というようにしています。 


インストール・環境構築編

まずは必要な環境を構築しましょう。途中何回かターミナル(Windowsではコマンドプロンプト)という、デザイナーにとって恐怖の黒い画面の操作を必要としますが、PCに一回やってしまいさえすればいいので、まあ大丈夫だと思います。

インストールに必要なアプリなどはこちら

  • MAMP(WindowsだとXAMPP
    • 開発環境をサーバーにしてしまう。
    • 実はMAMPは今回の環境には必要無いのです。ただせっかくなのでこの機会にうちのデザイナーにも入れてもらおうかと。理由は後ほど説明。
  • Node.js
    • 開発環境のマシンでJavaScriptが動く。CoffeeScriptのインストールや実行、Cakefileの実行に必要。
  • Ruby(Macだと最初から入っているのでこれを入れる作業は必要無い)
    • Sassのインストールや実行に必要。これで書いたソースはまじかっちょいい。
  • CoffeeScript
    • JavaScriptがちょう楽しく書ける。
    • Cakefileという、パブリッシュのときに実行するスクリプトも書ける。
    • ちょっと最初が大文字CつながりでCoffeeScriptとCakefileがぱっと見混同しますが、Cakefileについては後のほうですこし説明いれます。順番が必要なのでまずは語句だけ覚えておいてください。
  • Sass
    • あのダサかったCSSがわりかしイケてる言語になる。
  • WebStorm
    • 拙者が出会った生涯最高の友になりそうな予感のするIDEでござる。
    • WebStormでCakefileを使うことにより、ターミナルやコマンドプロンプトを使わずにパブリッシュできるようになる。
    • もちろんWebStormの代わりに、その上位版のPhpStormやIntelliJ IDEAを使っても可。僕はすでにPhpStormは購入して使っていたけど、このたびスーパー肩パッドさんのおすすめにならってIntelliJ IDEAにしました。
  • LiveReload
    • パブリッシュしたら自動的にブラウザをリロードするアプリ。
    • ブラウザのリロードという手間さえ面倒くさがる僕みたいなサルには絶対に必要。

全部のインストール方法を細かく書いていくことは(僕が面倒臭くて)できないので、端折れるところは端折って、説明が必要なところはくどくどと説明しながら進めていきます。

MAMP

無償で使えるFree版と、有償だけど機能が豊富なPro版がありますが、得に必要性を感じなければFree版でいいと思います。
前にWebStormを囲む飲み会したときに、ピクトロンの杉山さんから「MAMPよりももっといいサーバーあるよ」と教えて頂いたんですが、飲んでたので忘れてしまいました。そっちのほうがいいかもしれない。 
まあとにかくMAMPの導入目的は、開発機をサーバー化させることなので、すでに開発環境にサーバーを構築していればそっちを使ったらいいのでMAMPを入れる必要はないです。まだの人はMAMP入れましょう。
ダウンロードからインストールはたぶん簡単だと思うので説明は省略します。
だいぶ何年も前に入れたっきりなのでやり方忘れたからです。

ちなみにMAMPのアイコンがDocにさりげなく置いてあると、わかってる開発者ふうでおしゃれ。まぢでモテる。 
Windowsの人はXAMPPを入れましょう。 

Node.js

これを今回入れる目的は、あとに説明するCoffeeScriptをインストールしたり動かしたりするためなんですが、それ以上にNode.jsは世界を変える力を秘めています。
なので恥ずかしがらずにインストールしましょう。 
これもインストーラーをダウンロードして実行したらインストールされるはずなので、説明は省きます。

Ruby

Macの人は、最初からインストールされているのでこの作業は必要ありません。
Rubyはあとで説明するSassをインストールしたり実行したりするのに必要なので、Windowsの人はインストールしてください。
Rubyは世界を変える力があるかと問われれば、僕はRubyを扱ったことがないのでわかりません。わからないことは言えないのですが、Sassしかり世界のいろんな便利なものがRubyで動いていると思うと感動します。

CoffeeScript

以前に@KaedeASさんから「CoffeeScriptいいよ」と教えて頂いて、実際ためしてみて「わあすごい!世界を変える力がある!」といたく感動したのですが、そのまま案件に投入するには至らず、ずっとほったらかしにしていました。
少し前にブログ界隈でCoffeeScript不要論、必要論がわき上がったときも、「うんうんそうだよねえ、過渡期のメタ言語なんて案件でつかいにくいよねえ」「わかってるわかってる、CoffeeScriptの必要なところ、おじさんようくわかってるよ」などと適当に読みながら日和見を続けていました。 

今回、環境を構築したので実案件にぶつけてみたのですが、ほんといいです。CoffeeScript。
今まで自分もJavaScriptでなんとかOOP開発が出来ないか、しょぼいことをしょぼいなりにいろいろ試してみたんですが、どうしてもJavaScriptだけではクラスみたいなことが出来ると言えば出来るんだけど処理の本質じゃないところに気を配らなくてはいけなくて、だいぶ困っていました。

で、CoffeeScriptなんですがこいつはすごい!ほんとすごい。やりたいことが当たり前に書ける。
var self = this;とかいらんかったんや!
もうthis.constructor.prototype.hoge.call(this , arguments);とか書く生活とはオサラバです。

CoffeeScriptさんのいいところを挙げていったらきりがないですが、この記事はそういう主旨ではないのでインストール方法に話を進めます。
Sassもそうですが、別にインストールしたから開発スタイルをCoffeeScriptにしないといけない!なんてことはないので、今まで通りJavaScriptの開発を続けてもいいので、そこは念のため。 

で、インストールですが、まずはMacで説明します。
ターミナル.appを開いて、怖い画面を出します。コマンドを入力してコンピューターを操る、世界でも限られた能力を持つハカーしか触ることをゆるされない、あのターミナルです。
立ち上げ方が分からない、って人はspotlightに「ターミナル」と打ち込んで検索して出てきたアプリを立ち上げてください。
(※「ターミナル」というファイル名でエロ画像などを保存していた場合、このとき大量にざくざくとお宝画像が出てくることになるので、一生ターミナルなんか使わんだろうと思っていたエロいWebデザイナーさんは注意が必要です)

そこでおもむろに次のコマンドを打ち込んでください。コピペで結構です。 

sudo npm install -g coffee-script

パスワードの入力を求められると思うので、Macに設定しているログインのパスワードを入力します。
大丈夫。闇のハカーが裏であなたのパスワードを吸い出しているようなことはないです。安心して。世界はあなたのものです。

これでCoffeeScriptのインストールが完了です。Macの人は次に進んでSassをインストールしましょう。

Windowsの場合は、説明が適当になってしまいますがすいません。実機で試していないもので。書く前にせめて試せよって話しなんですが、面倒臭いのでどうしても嫌です。
なので適当に憶測で書いていきます。コマンドプロンプトを立ち上げます。立ち上げかたがわからない人は、「ファイルを指定して実行」に「cmd」と入力して立ち上げてください。
全てを飲み込むが如く佇む漆黒の闇を具現化したそのウインドウこそがコマンドプロンプトです。 

npm install -g coffee-script

その画面に上の文字を入力するとインストール出来ると思います。まあ適当です。ごめんなさい。
Windowsの人は、次のSassのインストールに進む前にRubyもインストールしないといけないです。ググれば見つかると思うのでやっといてください(ほじほじ)。

Sass

Sassのインストールもターミナルを使います。大丈夫。ターミナル使うのはこれで最後だから。
Macのターミナルに次のコマンドを打ち込みます。

sudo gem install sass

またパスワードを聞かれるので入力してください。

Windowsの場合は、コマンドプロンプトで

gem install sass

と入力すればいけると思いまーす(適当)。

WebStorm

最初にも説明しましたが、WebStormの上位版のPhpStormでもさらにその上位版のIntelliJ IDEAでもいいです。僕はPHP開発もAIR開発も行えるIntelliJ IDEAにしましたが、よっぽどでなければWebStormがあればWEBの開発に困ることはなくなると思います。

このエディタがどういいのか、今回の記事ではその主旨ではないのでエディタの説明は省略します。

このアプリも、ダウンロードして解凍して出来たアプリを任意のディレクトリ(MacならApplicationディレクトリ、WinならProgram files)に移して実行するだけなので説明は省略します。
確か30日間くらいある試用期限のあいだは無償で使うことが出来たはずです。

LiveReload

パブリッシュしたら自動でブラウザをリロードしてくれる、ただそれだけのアプリです。
本当はもっと機能があって、CoffeeScriptやSassをコンパイルして他のディレクトリに書き出してくれるという、まさにこの記事の主旨に似たような機能を、このアプリ一個さえあれば できてしまうんです。

なのになぜ、このアプリの自動書き出し機能を使わずわざわざCakefileで処理しようとするのか。少し重要なことなのでこのタイミングで書いておきます。

このLiveReloadが書き出しをしてくれるタイミングはディレクトリやファイルに変更があったときです。
さらに実を言うとLiveReloadを使わなくても、CoffeeScriptもSassも ディレクトリやファイルに変更があったとき自動的にコンパイルしてくれる機能があるんです。
おれのやってる環境構築なんていらんかったんや!

と考えるのは早計です。
僕は、任意のタイミングで書き出したいのです。 

ディレクトリやファイルに変更があったときに自動的に書き出されるということは、つまりファイルを保存したタイミングで書き出されるということになります。
それは言い方を変えると、書き出したいときにしか保存が出来ないということです。もしくは保存したら勝手に書き出される。 

Flasherのあいだでは昔から次のような言い伝えがあります。

  • Ctrl+Sを制す者はFlashを制す
  • こまめにCtrl+Sを押せる人でないと(Flashを作るのは)難しい
  • 頻繁にCtrl+Sを押してときどきCtrl+Enterを押す簡単なおしごと

もともとはFlashが落ちて作業がパーになる辛さを皮肉って表現した言葉ですが、こまめな保存がいかに大事かを後世に伝える非常に重要な言葉です。

保存とパブリッシュをわけて進行できるFlashのこのやり方は、ぜひともHTML開発に活かしたいと考えています。
そうでなくてもむずかしいこと抜きにして、とにかくッターン!てやりたいんです。 

そういった理由で、僕はLiveReloadの書き出し機能は使わないことにして、LiveReloadさんにはブラウザをリロードするだけの簡単なおしごとをお任せすることにきめました。

で、インストールの話しですが、MacだとAppStoreから購入するだけでいけると思います。なんかWindows版も出ています。

あと、アプリの他にブラウザのほうにも拡張機能もインストールします。Firefox、Chrome、Opera、各種拡張があるので、普段開発で使っているブラウザにあわせてインストールしておきます。

追記

記事を公開後すぐに「WebStorm使うならLiveEdit機能あるからLiveReloadいらないのでは?」という指摘がありました。ありがとうございます。
僕が知らないだけかもしれないですが、LiveEditだと作業しているsrcディレクトリのほうを監視するので、自分がこれからやろうとしている、public_htmlを監視してパブリッシュしたタイミングでリロード、という動作をするのにLiveReloadのほうが都合が良かったのでした。説明不足ですいません。
僕がLiveEdit機能のことをよく理解していないので、もしかしたらLiveEditでも同じような事ができるかもしれません。いずれにしてもひとりでWebぐぐった程度で得た知識ではどうしても偏りがあるのでご指摘があるとせっせとブログを書いた甲斐があるので非常に有り難いです。

インストールはこれで完了

インストール作業はこれで完了です。お疲れ様でした。PCがぶっ壊れて新しいPCとかにならない限り、もうこの作業はしなくてすみます。

あとは・プロジェクトごとに設定する3ステップ ・作業のときに立ち上げておくアプリ2つ の説明が続きますが、この時点で頭がパンパンになった人は一旦休憩入れてください。


プロジェクトごとの設定編

次はプロジェクトごとに必要な設定を行います。面倒なことは嫌いなので3ステップですませます。

  1. Cakefileを用意
  2. プロジェクトディレクトリを作成
  3. WebStormでCakefileを実行する設定

順番に説明します。

1.Cakefileを用意

まずプロジェクトをパブリッシュ出来るようにするためのCakefileを用意します。
その前にCakefileとは何なのかを簡単に説明します。 

Flashにはパブリッシュ、JavaにはAnt、CにはMakeというように、各種開発環境にはビルドを自動化するためのツールが提供されていたりするんですが、今までJavaScriptにはそういうのが無かったわけですね。
いきなりビルドって言葉が出てきてなんじゃらほい?と思う方もいるかもしれませんが、この記事で使われている意味としてはパブリッシュ=ビルドと同じような事と思ってもらうとわかりやすいかと思います。
まあ今まではJavaScriptなんて、せいぜいステータスバーに時計を表示させる程度の使い方しかしてなければそれでよくて、 ちゃっちゃと書いてブラウザで立ち上げたら「あなたは3回目の訪問です!」とか表示されてるのをみてみんなで喜んでおけばそれでよかったわけです。

でもここにきてJavaScriptを書く人の年収がうなぎ登りに上がっていくようになると、誰でも簡単にはじめられるお気軽言語というイメージを払拭して、参入障壁の高い難しいことして初心者を排除し技術を独占して大もうけしようと考えた人がいたのかどうかはわかりませんが、まあとにかくどんどん複雑化を求められる開発環境に対して、JavaScriptでもビルドを自動化するツールが必要になってきたわけですね。

そこでNode.jsが提供するビルドツールとして、Cakefileというものができたそうなんです。

もっと具体的にいうと、CoffeeScript とかSassとか便利そうなんだけどパブリッシュするときいちいちコマンド打ち込むの面倒臭いな、と思った人が(僕もそう思ったしみんなも思ったはずです)、絶対にコマンド打ち込むの嫌でござるということで、自動的にやっといてほしいことをCoffeeScriptで書けるようにしてくれたんですね。
それがCakefileです。 

なのでWebStormでCakefileを使えるようにすればもうパブリッシュのたびにコマンドを打ち込まなくてもすむ、程度に考えてもらっててまあ間違いではないです。

そんなわけで、Cakefileを用意します。
CoffeeScriptで書けるので、ぜひがんばって皆さんのプロジェクトにぴったりなCakefileを書いてみてください。
おわり。

とおわるのはあまりにもあまりなので、いちおう拙作ではありますが自分の作ったCakefileを公開しました。

GitHubにあります

 Githubzip
リンク先にあるZIPでダウンロード出来るボタンがあるので、そこからダウンロードして解凍してできたファイルのsrcの中にCakefileがあります。
で、お約束ですがこのCakefileを使った事によるいかなる被害も僕は責任が持てません。
本来、公開するにはやばすぎるくらい、エラーのハンドリングも未熟なスクリプトで、実際にこれをプロジェクトに投入して危険な場合もあるかもしれません。
僕は自分自身で今後このCakefileをメンテナンスしていくので、何が起きても自分自身では責任が持てますが、他人の作ったCakefileを自分のプロジェクトに使う恐ろしさだけは理解して、それでも使いたい人だけ使ってください。

とまあ、脅し文句みたいなのが入りましたが、実際こんな恥ずかしいファイルを公開したのも、みんなで読んでみてこここうしたほうがいいよとかわりとオープンにご意見を聞きたいからです。
おおっぴらに、自信満々に、「ぜひみんな使ってくださいね!」と言えるほどの完成度のCakefileではないので、歯切れの悪い言い方ですいませんが、まあ、ダウンロードして参考にして頂けたらと…。

ほいで自分としては初めてCakefile作ったので今後もっと勉強してよくしていけたらと思います。そのためにGitHubのほうに公開しました。

いちおうこちらにも現時点でのソースを 貼っておきます。

#無視するファイル名を正規表現で
IGNORE_FILES = [
  '^\\..'         #先頭がドットから始まる隠しファイルや隠しディレクトリ
  '^Cakefile$'    #このファイル
  '^Thumbs.db$'   #Windowsが生成するサムネイルファイル
  '^_notes$'      #DreamWeaverデザインノート
  '^.mno$'        #DreamWeaverデザインノートファイル
  '^Templates$'   #DreamWeaverテンプレートディレクトリ
  '^Library$'     #DreamWeaverライブラリディレクトリ
  '.dwt$'         #DreamWeaverテンプレートファイル
  '.lbi$'         #DreamWeaverライブラリファイル
#  '.fla$'         #Flashファイル
#  '.fw.png$'      #FireWorksファイル
#  '.psd$'         #PhotoShopファイル
#  '.ai$'          #Illustratorファイル
#  '.tmp$'         #一時ファイル
]
#ソースディレクトリと書き出し先
TARGET_DIR = '.'
OUTPUT_DIR = '../public_html'

#CoffeeScriptのファイル名を正規表現で
COFFEE_FILES = ['.coffee$']
#CoffeeScriptファイルのセパレーター
COFFEE_SEPARATOR = '___'
#bareオプションでコンパイルするファイル名
COFFEE_BARE = 'global'


#Sassファイル名を正規表現で
SASS_FILES = ['.scss$']
#コンパイル後のCSSファイルの拡張子
SASS_RENAME = 'css'
#Sassのコンパイルスタイル
SASS_STYLE = 'compressed'        #nested, expanded, compact, compressed

#ディレクトリセパレーター
DIRECTORY_SEPARATOR = '/'
#coffeeコマンドへのパス
COMMAND_PATH = '/usr/local/bin/'

fs = require 'fs'
st = require 'stream'
{exec} = require 'child_process'

option '-o', '--output [DIR]', 'build先のディレクトリ'
option '-t', '--target [DIR]', 'buildする元のディレクトリ'

task 'build', 'webソースをbuildします。', (options) ->
  targetDir = options.target or TARGET_DIR
  outputDir = options.output or OUTPUT_DIR
  copyFiles = []

  #初期化
  init = ->
    return

  #実行
  run = ->
    console.log """
    ----------------------------
    target #{targetDir}
    output #{outputDir}
    ----------------------------
    """
    FileUtil.check targetDir
    FileUtil.check outputDir
    (new Clean(outputDir)).run()
    search = new Search targetDir, outputDir
    #CoffeeScriptを走査
    search.run()
    files = search.getFiles()
    coffees = search.getCoffees()
    sasses = search.getSasses()
    console.log '''
    ----------------------------
    ファイルをコピー
    '''
    (new Copy(files, targetDir, outputDir)).run(->
      console.log '''
      コピー完了
      ----------------------------
      CoffeeScriptコンパイル
      '''
      coffee = new Coffe coffees, targetDir, outputDir
      coffee.run(->
        console.log '''
        コンパイル完了
        ----------------------------
        Sassコンパイル
        '''
        sass = new Sass sasses, targetDir, outputDir
        sass.run(->
          console.log '''
          build完了
          ============================
          '''
          return
        )
      )
    )
    return

  init()
  run()
  return

#ファイルユーティリティ
class FileUtil
  directory_separator = DIRECTORY_SEPARATOR
  #指定されたディレクトリをチェック
  @check = (path)->
    console.log "#{path} を確認"
    if !fs.existsSync path
      throw new Error "#{path} が存在しません。"
    if !fs.statSync(path).isDirectory()
      throw new Error "#{path} はディレクトリを指定してください。"
    console.log "確認完了"
    return

  #パスからファイル名を返す
  @fileName = (path) ->
    return path.split(/[\\\/]/).pop()

  #ファイル名から拡張子を除く
  @file = (fileName) ->
    return fileName.split('.')[0]

  #ルートディレクトリか確認
  @isRoot = (path) ->
    return (path == "#{directory_separator}")

  #カレントディレクトリか確認
  @isCurrent = (path) ->
    return (path == ".#{directory_separator}")

  #ペアレントディレクトリか確認
  @isParent = (path) ->
    return (path == "..#{directory_separator}")


#指定されたディレクトリの中身を空にするクラスです。
class Clean
  directory_separator = DIRECTORY_SEPARATOR
  constructor:(path)->
    @_path = path

  run:->
    if !@_isPeril
      @_check @_path
    return

  _isPeril:->
    return (FileUtil.isRoot(@_path) or FileUtil.isCurrent(@_path) or FileUtil.isParent(@_path) )

  _check:(path, root = path) ->
    stats = fs.statSync path
    if stats.isDirectory()
      @_inDir path, root
    else if stats.isFile()
      @_forFile path
    else
      throw new Error "#{file} を削除できませんでした。"
    return

  _inDir:(path, root) ->
    files = fs.readdirSync path
    for file in files
      current = "#{path}#{directory_separator}#{file}"
      @_check current, root
    if path != root
      fs.rmdirSync path
    return

  _forFile:(path) ->
    fs.unlinkSync path
    return

#指定したディレクトリを調べます。
class Search
  directory_separator = DIRECTORY_SEPARATOR
  ignore_files = IGNORE_FILES
  constructor:(target, output)->
    @_target = target
    @_output = output
    @_files = []
    @_coffees = []
    @_sasses = []

  init:->
    @_files = []
    @_coffees = []
    @_sasses = []
    return

  run:->
    @init()
    @_search @_target
    return

  getFiles:->
    return @_files

  getCoffees:->
    return @_coffees

  getSasses:->
    return @_sasses
    
  _search:(path) ->
    stats = fs.statSync path
    if @_isIgnore path
      return
    if stats.isDirectory()
      @_inDir path
    else if stats.isFile()
      @_forFile path
    else
      throw new Error "#{file} を走査できませんでした。"
    return

  _inDir:(path) ->
    output = path.replace @_target, @_output
    if !fs.existsSync output
      fs.mkdirSync output
    files = fs.readdirSync path
    for file in files
      current = "#{path}#{directory_separator}#{file}"
      @_search current
    return

  _forFile:(path) ->
    if Coffe.isCoffee path
      @_coffees.push path
    else if Sass.isSass path
      @_sasses.push path
    else
      @_files.push path
    return

  _isIgnore:(path) ->
    name = FileUtil.fileName path
    for ignore in ignore_files
      if name.match new RegExp(ignore)
        console.log "#{path} を除外"
        return true
    return false


#ファイルをコピーします。
class Copy
  constructor:(files, target, output) ->
    @_files = files
    @_target = target
    @_output = output
    @_callBack

  run:(callBack)->
    @_callBack = callBack
    @_copyAll(0)
    return

  _copyAll:(index = 0) ->
    if index >= @_files.length
      @_callBack()
      return index
    file = @_files[index]
    @_copy file, file.replace(@_target, @_output), index
    return

  _copy:(src, dst, index) ->
    input = fs.createReadStream src
    output = fs.createWriteStream dst
    input.pipe(output)
    output.on 'close', =>
      @_copyAll(++index)
      return

#CoffeeScriptを扱うツール
class Coffe
  coffee_files = COFFEE_FILES
  coffee_separator = COFFEE_SEPARATOR
  coffee_bare = COFFEE_BARE
  constructor:(coffees, target, output)->
    @_coffees = coffees
    @_target = target
    @_output = output
    @_callback

  run:(callback)->
    @_callback = callback
    @_compile 0
    return

  _compile:(index=0) ->
    if index >= @_coffees.length
      @_callback()
      return index
    path = @_coffees[index]
    bare = ''
    if @_isBare path
      bare = '-b'
    output = @_getOutputDir path
    command = "coffee #{bare}  -o #{output} -c #{path}"
    console.log 'コンパイル開始', command
    exec command,(err,stdout,stderr)=>
      throw err if err
      console.log stdout,stderr
      @_compile(++index)
      return
    return

  _getOutputDir:(path)->
    output = path.replace @_target, @_output
    fileName = FileUtil.fileName path
    return output.replace fileName, ''

  _isBare:(path) ->
    fileName = FileUtil.fileName path
    fileName = FileUtil.file fileName
    funcs = @_funcs fileName
    return @__checkBare funcs

  __checkBare:(funcs) ->
    for func in funcs
      if func == coffee_bare
        return true
    return false

  _funcs:(name) ->
    return name.split coffee_separator

  #CoffeeScriptかどうかをファイル名からチェック
  @isCoffee = (path) ->
    for file in coffee_files
      if path.match(new RegExp file)
        return true
    return false


#Sassを扱うツール。
class Sass
  directory_separator = DIRECTORY_SEPARATOR
  sass_files = SASS_FILES
  sass_rename = SASS_RENAME
  sass_style = SASS_STYLE
  constructor:(sasses, target, output) ->
    @_sasses = sasses
    @_target = target
    @_output = output
    @_callback

  run:(callback) ->
    @_callback = callback
    @_compile 0
    return

  _compile:(index = 0) ->
    if index >= @_sasses.length
      @_callback()
      return
    path = @_sasses[index]
    output = @_rename path
    command = "sass --style #{sass_style} #{path} #{output}"
    console.log(command)
    exec command,(err,stdout,stderr)=>
      throw err if err
      console.log stdout,stderr
      @_compile(++index)
      return
    return

  _rename:(path) ->
    path = path.replace @_target, @_output
    separatePath = path.split(directory_separator)
    separateFile = separatePath[separatePath.length - 1].split('.')
    separateFile[separateFile.length - 1] = sass_rename
    separatePath[separatePath.length - 1] = separateFile.join('.')
    path = separatePath.join(directory_separator)
    return path

  @isSass = (path) ->
    for file in sass_files
      if path.match(new RegExp file)
        return true
    return false

はあ。ほんとダサい。なんでおれこんな恥ずかしいコード晒してるんだろ。tryで例外を補足しないとかコールバックでネストしてるとか、もうほんとだめだおれ…。

いちおう現時点でCakefileが行っていることの説明をしておきます。
ごちゃごちゃと書いてあるんでここは読み飛ばしても大丈夫です。

まず最初にpublic_htmlを空にしています。
その後、srcにあるファイルを全てpublic_htmlにコピーして、CoffeeScriptやSassがあればそれらをコンパイルしています。
CoffeeScriptはデフォルトでは即時関数で囲んでグローバル領域を汚染しないようにしていますが、ファイル名に「___global」(半角アンダーバーみっつの後に半角小文字でglobal)と書かれてあるとグローバルな領域からアクセス出来るようにコンパイルします。つまりcoffee –bareオプションで書き出します。 
Sassのほうは、compressedスタイルで圧縮して書き出すようにしています。確認の時はfirebugやChromeのインスペクタを使うので、CSSファイルは人の目で可読にしておく必要がないと思ったからです。

今のところはこれだけですが、将来的には画像の圧縮とかSassの連結とか、FTP転送とかも足していきたいです。
省HttpRequestのために画像をbase64エンコーディングしてHTMLに埋め込むのも夢がありますよね。
HTMLの圧縮も考えてたんですが、案件ごとにHTMLのコーディングスタイルのレギュレーションが違ったりするので、設定がややこしくなりすぎるのでそれは今のところ見合わせています。 

あと、クリーンしてビルドをいっぺんにしか出来ないのが現状ですが、クリーンとビルドは別で出来るようにするのは、Cakefileの変更とWebStormの設定ですぐできるので、機会があれば後日紹介したいと思います。

2.プロジェクトディレクトリを作成

これはつまりプロジェクト用に新しいフォルダを作るだけです。こういう感じで作ります。
 スクリーンショット 2012 11 08 13 45 53
一番最初の図で説明したように、srcディレクトリがHTMLやCSS、JS、必要であればCoffeeScriptやSassのファイルが入って、それを作業していくディレクトリになります。
対してpublic_htmlのほうは、srcで作業したファイルがパブリッシュのときに自動的に入るディレクトリになります。実際の動作チェックから納品まではこのディレクトリで行うようになります。

注意したいのは、パブリッシュのたびにpublic_htmlディレクトリは一回空になります。処理としてはpublic_htmlを一旦空にしておいて、srcディレクトリから必要なファイルをpublic_htmlに書き出す、というような動作をCakefileにさせているからです。
ですのでpublic_htmlディレクトリの中にだけ重要なファイルを入れるというような事は避けてください。基本的にはpublic_htmlディレクトリの中身は人間の手はアンタッチです。 

あとは、srcの中に先ほど用意したCakefileを入れます。

3.WebStormでCakefileを実行する設定

先ほど作ったプロジェクトディレクトリを、WebStormで開きます。
WebStormを立ち上げて出てくる「Open Project」からプロジェクトディレクトリのsrcフォルダを 選んでください。
注意しないといけないのは、プロジェクトディレクトリそのものではなく、その中のsrcフォルダを選ぶことです。 

開いた画面。
 Config
上のメニューバーの中央あたりにある、下向き三角(▼)をクリックすると「Edit Configrations…」というメニューが表れるので、選択してデバッグ用設定ウインドウを表示させてください。
 スクリーンショット 2012 11 08 14 05 30
左上にある「+」ボタンを押して表れるメニューから「Node.js」を選択します。もし左メニューのリストにNode.jsが無い場合は、プラグインが入っていないので先にプラグインを入れる必要があります。
その場合は、この設定画面を一旦閉じて、WebStormのメニューから「Preference」→左の「Plugins」→画面下の方の「Browse repositories…」→表れる画面の右上の検索ウインドウに「NodeJS」と入力して出てきた「NodeJS」プラグインをダブルクリックでインストール→WebStormを再起動してまた下向き三角(▼)から「Edit Configrations…」を押して上の画面に戻ってきてください。
プラグインの依存関係とかうまくいけば表示されるようになっているはずです。 

次のように入力します。
スクリーンショット 2012 11 08 14 06 51

  • Name
    • てきとうにわかりやすい名前を入れます。
  • Path to Node
    • /usr/local/bin/node
    • 最初から入ってると思います。
    • もしnodeのパスが違う場合、ターミナルでwhich nodeと入力して出てきたパスにかえてください。
  • Node Parameters
    • 空白でおk
  • Working Directory
    • このプロジェクトのパス。最初から入っていると思います。
  • Path to Node App JS File
    • /usr/local/bin/cake
    • なんど入力してもあとで見返すと勝手に相対パスに書き換えられますがきにしないほうがいいです。
    • もしcakeのパスが違う場合、ターミナルでwhich cakeと入力して出てきたパスにかえてください。
  • Application Parameters
    • build
  • Environment variables
    • PATH=/usr/local/bin/:/usr/bin/
    • CoffeeScriptの実行ファイルへのパスとSassの実行ファイルへのパス。
    • それぞれのパスがもし違う場合は、ターミナルで
      which cake とwhich sass と入力して出てきたパスにかえてください。

左のリストの、DefaultってところにNodeJSの設定項目があって、一回ここにWorking Directory以外を埋めておくと次からは他のプロジェクトでも同じ値が埋まると思います。
つまりプロジェクトごとに違うのはWorking Directoryだけで、他の値は使い回したらいいです。 

プロジェクトごとの設定はこれで完了

生来のものぐさな性格が幸いしてか、プロジェクトごとに必要な設定はそんなにないです。もう完了です。
本当はGitのリポジトリ作ったりDreamWeaverのサイト設定とかしたり、ほかにもいろいろあるんですけど、少なくともパブリッシュ環境を構築するにあたって今までの業務に加えないといけない作業としては、たったの2ステップ増えるだけ(プロジェクトディレクトリ作るステップは従来からやってるので今回で増えたステップにはカウントしない)ですみます。 


毎回の作業編

それでは実際にコーディングをしはじめる前に、次の二つのアプリを立ち上げます。

  1. MAMPを立ち上げる
  2. LiveReloadを立ち上げる

たったこの二つのアプリを立ち上げるだけなのでまあそう面倒くさがらずに。

MAMPを立ち上げる

Windowsの人はXAMPPを立ち上げてください。XAMPPの場合、Skypeとポート番号がかぶるかもしれないので、どちらかのポート番号を変更しておくかSkypeを終了しておいてください。

Mamp
サーバーを立ち上げて環境設定すると聞くとものすごく恐ろしいスーパーハカーの所業のように感じますが、簡単です。
立ち上がった画面の環境設定から「Apache」→「Document root」でプロジェクトの「public_html」ディレクトリを選択するだけです。
 スクリーンショット 2012 11 08 14 27 57
これでスーパーハカーの仲間入りです。
念のため気を付けたいのが、選択するディレクトリはプロジェクトディレクトリそのものでもなく、srcディレクトリでもなく、「public_html」ディレクトリです。

あと、いちおう「ポート」の値も確認して、MAMPだとデフォルトで「8888」のはずなのでそれを確認しておきます。

最初のほうにも書きましたが、実は今回の環境でわざわざサーバーを立ち上げる必要は全くないのです。
わざわざローカルにサーバー立ててそれで確認だなんて大げさな、HTMLファイルをふつうにブラウザで開いて確認してもいいじゃないかとも思うのですが、ファイルで確認するのとサーバー上で確認することの大きな違いは誰もがよく知るところだと思います。 

  • ファイルだけで確認してて納期直前にサーバーにアップしたらえらい違いで大変なことになった(主にファイル名の大文字小文字などの違いが原因だったり) 
  • かといって毎回確認のたびにサーバーにアップしてられない。そっちのほうが大げさな
  • Flashとかだと特にローカルとサーバー上での動作が違うよね

というような事を解消するために、僕は必ずローカルサーバーで開発をするようにしています。
システムの開発者でしたら言わずもがなの当然のことなんですが、WEBデザイナーのみなさんもよく経験したことのあるトラブルを防ぐためにもローカルサーバーで確認することをおすすめするわけです。
そういった主張には賛同できない、サーバーなんて立ち上げたくない、というような場合には、わざわざMAMPやXAMPPを立ち上げなくてもいいかと思います。 

LiveReloadを立ち上げる

LiveReloadを起動して、プロジェクトの「public_html」をドラッグ&ドロップしてください。
 スクリーンショット 2012 11 08 14 45 09
もうこれはこれで完了です。念のため注意したいのが、これもsrcディレクトリやプロジェクトディレクトリではなくて、「public_html」ディレクトリのほうを登録することです。

いよいよ実行!Ctrl+Enterッターン!

準備はこれだけです。
あとはWebStormで思うがままに開発してください。

ここだ!と思ったら本能のおもむくままにすかさずCtrl+Enter!ッッターンッッッ!!!
といきたいところですが、実はWebStormではCtrl+Enterではパブリッシュ出来ないのです。今までだましてすみませんでした。

そのかわりに、WebStormでパブリッシュするときはShift+F10を使用します。大丈夫。すぐ慣れます。 

ブラウザで

http://localhost:8888/

にアクセスします。
言うまでもないですが、8888の数字のところはポート番号なのでもしMAMPやXAMPPで違うポート番号に設定していたらその数字に置き換えてください。

ブラウザの拡張機能としてインストールしたLiveReloadアイコンをクリックしてブラウザとLiveReloadを繋げます。
図にするとこういうことをします。
 スクリーンショット 2012 11 08 14 52 17

簡単なことなんですが、ここがわかりにくくて自分は最初に30分くらい悩みました。

あとは、Shift+F10でパブリッシュするたびにブラウザが自動的にリロードするようになりますので、開発効率が飛躍的に上がると思います。


さいごに

実際にこの環境を案件投入した経験はまだ浅いのですが、Cakefileを鍛えていけばかなり強力な環境になると思うのですがどうでしょうか。
そういう振る舞いをするアプリ作れば一発で役に立たなくなる可能性もありますが、それでもCakefileでカスタマイズを自由にできることにこの方法の可能性を感じます。 

めちゃくちゃイケてる資料を公開していただいたスーパー肩パッドさんとF-siteに感謝です。
 YkD

コメントを残す

メールアドレスが公開されることはありません。