JSXをPhantomJSで動かしてみる

遅くなりましたが、AltJS Advent Calendarの7日目です。

PhantomJSというのは、コマンドラインで使えるJavaScript実行環境です。Mac+Homebrewだと brew install phantomjs でインストールできます。

さて、まずは以下のようなJavaScript source codeを用意してみましょう。通常のJavaScriptと違うのは、 phantom.exit() を実行しないとスクリプトが終了しないことです。

// hello-phantom.js
console.log("Hello, world!");
phantom.exit();

実行するにはphantomjs(1)にファイル名を与えて起動するだけです。

$ phantomjs hello-phatntom.js
Hello, world!

これだけみると単なるJavaScript実行環境に見えますが、PhantomJSはWebKit frameworkを使ってGUIなしにHTML+JavaScriptからなるウェブアプリケーションを実行できるというものなのです。実用的な観点から見ると、HTML5ウェブアプリケーションの自動テストやスクレイピング、開発ツールの提供などに使うことができます。

さて、PhantomJSはJavaScript実行環境、ということはJSXもPhantomJSで動かすことができるはず!そこでPhantomJSのJSXバインディング、PhantomJSXを作ってみました。

PhantomJSXをcloneしたら、make hello で簡単なデモを見ることができます。

phantomjsx$ make hello
jsx --executable web --output example/hello.jsx.js example/hello.jsx
JSX_RUNJS=phantomjs jsx --executable commonjs --run example/phantom-hello.jsx
Hello, PhantomJSX!
system.version: {"major":1,"minor":7,"patch":0}
system.os: {"architecture":"32bit","name":"mac","version":"10.8 (Mountain Lion)"}
console:  start hello.jsx
open hello.html

最初の一行は、実行しようとするWebApp用JSXコードのコンパイルで、二行目がPhantomJSX用コードのコンパイルと実行です。三行以降が出力結果です。実行されるコードは以下のようになっています。

// `make hello` to run (see Makefile for details)
//
// `example/hello.jsx(.js)` and `example/hello.html` is a web application,
// and example/phantom-hello.jsx (this file) is a PhantomJSX driver
// which controls the web application.
//

import "js/web.jsx";

import "../lib/phantom.jsx";

class _Main {
  static function main(args : string[]) : void {
    log "Hello, PhantomJSX!";
    log "system.version: " + JSON.stringify(phantom.version);
    log "system.os: "      + JSON.stringify(system.os);

    var page = webpage.create();

    page.onConsoleMessage = function(msg, line, id) {
      log "console: ", msg;
    };

    page.open("example/hello.html", function(status) {
      log "open hello.html";
      phantom.exit();
    });
  }
}

// vim: set tabstop=2 shiftwidth=2 expandtab:

このコードは、hello.htmlをWebPageとして実行し、そのWebPageの中でJavaScriptアプリケーションがconsole.log()で出力するものを page.onConsoleMessage でフックする、というものです。これで phantom.exit() が必須な理由もお分かりいただけたでしょう。WebPageはスクリプトコードを実行し終えた後も存在し続けている(イベントループが生きている)ので、どこかで無理やり終了させなければならないからですね。

なお、フックできるイベントは、コンソール出力の他にも alert() などのモーダルダイアログやページのロード開始・終了などがあります。詳しいことはPhantomJSのAPI referenceをどうぞ。面白いことに、fsモジュールでファイルシステムにアクセスしたり、webserverモジュールでhttpdになったりすることもできます。これを利用すれば、テキストエディタに対してブラウザのデバッグコンソールと等価なREPLを提供することもできそうです。

さてPhantomJSのAPIを呼び出せるようにはなりました。今後はJSX本体のweb interface (web compilerなど) にテストがなくしばしば壊れがちなので、JSX本体に組み込んでしまおうと計画中です。