タオルケット体操

サツバツいんたーねっと

Haxeで書いたコードをJavaScriptから呼ぶ方法

HaxeとJavaScriptの連携

上記のように、Haxeで書いたコードをJavaScriptから呼び出すには、@:exposeを使います。
以下のコードはHaxe3のコンパイラを使ってコンパイルしています。

とりあえず適当な例が思いつかないので、お気に入りの画像を使った記事ツイートリンクを生成する感じのアレな関数を作ってみます。

// Main.hx

package ;


class Main
{
    static function main()
    {
        trace("foo!");
    }
}

まず、主となるコードが書かれているMainというファイルを用意します。
とりあえずなんかそれっぽいのがあるということにしておきましょう。

次に、今回の主目的となるSocialというクラスを作ります。

// Social.hx

package ;

import js.Browser;

using StringTools;

@:expose
class Social
{
    static function main() { } // コンパイルの為に必要になってしまうっぽい

    public static function twitter(place_to_img: String): js.html.AnchorElement
    {
        var encorded_my_url = js.Browser.location.href.urlEncode();
        var title: Dynamic = Browser.document.getElementsByTagName("title")[0];
        var ref_to_myarticle = 'http://twitter.com/share?url=${encorded_my_url}&text=${title.innerText}';

        var ancor_to_twitter = Browser.document.createAnchorElement();
        var image = Browser.document.createImageElement();
        ancor_to_twitter.href = ref_to_myarticle;
        image.src = place_to_img;

        ancor_to_twitter.appendChild(image);
        return ancor_to_twitter;
    }
}

とりあえず適当な感じで書いておきます。

次からが厄介で、普通にMainをコンパイルするだけではSocial.hxに含まれるコードは展開してくれません。
なので明示的に指定してやります。

# build.hxml

-js main.js
-main Main
-debug

--next

-js social.js
-main Social

これで、Social.hxのコードはsocial.jsとして展開されてくれるはずです。

では、試しにhtmlから読んでみます。ちなみに、画像は適当に用意しておいてください。

<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        <title>ぬわぁああ</title>
    </head>

    <body>
        <script src="./main.js"></script>
        <script src="./social.js"></script>

        <header>
            <h1>foo</h1>
        </header>

        <footer>
            <script type="text/javascript" charset="utf-8">
                !function(doc, script_tag, id){
                    var fjs = doc.getElementsByTagName(script_tag)[0];
                    if (!doc.getElementById(id)) {
                        var twi = Social.twitter('./twitter.png');
                        fjs.parentNode.insertBefore(twi, fjs);
                    }
                }(document, "footer", "twiwije");
            </script>
        </footer>

    </body>

</html>

適当なブラウザで開いてみてみませう。いい感じの画像でボタンが生成されてますね? 多分。

なんだか、規模が大きくなったら破綻しそうな気がしなくもない方法ですが、今のところこれしかやり方がわからないのでこんな感じで作ってます。
もっとスマートなやり方をご存知の方がいたら教えて頂けるとスゴい喜びます。スゴい喜びます。スゴい嬉しいです。

追記: jinjorさんに教えていただきました

コンパイル結果に含めたいクラスを、Mainからimportしてあげることでmain.jsのコンパイル結果に含めることが出来ます。

この方法であれば、importしたいクラスに対して不必要なmainメソッドを実装する必要もないのでより良いですね。

ただし、一つだけ注意があり、dead code elimination(-dce)をfullに指定すると、importをしていても、mainメソッドから呼ばれていないコードは全て削除されてしまいます。 なので、dce fullでコンパイルしたい場合はmainで無理矢理呼ぶなどの対策が必要になるかもしれませんね。

さらに追記

http://haxe.org/manual/tips_and_tricks

@:keep: the given class or field will be preserved in the generated output even if never directly referenced. Use it in conjunction with --dead-code-elimination.

oh……