JSの資産をTitaniumでも利用したい
今やnpmに登録されていないJSライブラリは、存在していないも同然。
そんな時代ですね。
TitaniumでもそのJSの資産を使いたい。そんなことは可能なんでしょうか。
2つの問題
Titaniumでなぜnpmモジュールがそのまま使えないのでしょうか?
理由は2つです。
-
require
が違うから。特にnode_modules
等はNodeの独自拡張なので、Titaniumはそこを辿れない。 -
process
,http
などNode固有のAPIを呼んでいるから。
ブラウザではどう解決してきた?
ブラウザのisomorphic JSに関しては、
RequireJS等の混迷期?を経て、天下統一を果たしたのはbrowserify。
このライブラリは、コマンド1つでブラウザ向けにnpmモジュール入りのコードを全て入れて、
必要なshimも入れて、1つのJSファイルを生成してくれます。
Titaniumはどう解決すべき?
Titaniumは、独自にrequire()
を持っていることがブラウザと違う点です。
よって、global requireを再定義するようなアプローチが可能です。
ここから2つのアプローチを紹介し、それぞれのメリット・デメリットをみていきます。
Titanium界の魔法の杖(?) titaniumifier
イタリアのyuchi氏はbrowserifyを拡張して、Titaniumにもその仕組みの導入を可能にしました。
それがTitaniumifier です。
function.prototype.bind()
のちょっとしたshimが入っています。
最終的にTitanium CommonJS Modulesの仕組みでzipファイルをつくってくれます。
動作環境に注意 (Node v4以降ではインストールできず. (Pull Request中)
もしNode v4以降でインストールする方はfork先を使って下さい。)
使ってみる
npm install -g titaniumifier # Node v0.12以下で実行してください
mkdir node_modules
npm install es6-promise # このライブラリをtitaniumifyするとします。
cd node_modules/es6-promise
mkdir dist
titaniumifier --out dist
そうすると
Error: No `guid` found. Here’s one for you: d46c75a6-d920-45ed-7657-0b7632b56cc7
などとでてきます。
どうやらguidというのをpackage.jsonのtitaniumManifestという項目に書かないといけないのだと。
よそのnpm moduleのpackage.jsonをいじるなんてできないよ!!と半ベソ書きながら
package.jsonに下記を追加しましょう。guidはguidならなんでもokと思います。
{
"titaniumManifest": {
"guid": "d46c75a6-d920-45ed-7657-0b7632b56cc7"
}
}
そして再度
titaniumifier --out dist
とすると、晴れてdistのなかに es6-promise-commonjs-3.0.2.zip
なるファイルができました。
zipファイルは筋が悪いよ
しかしこの後、Titaniumでモジュールを使う方法にしたがってこのzipを適切な場所に置くなどの作業が必要になります。
browserifyみたいに手軽にやりたかったはずがこれでは、Nodeな人に嫌われてしまいます。
package.jsonの依存モジュールをそのまま使えるHost mode (beta)
TitaniumifierのHost modeというのを使えば、 Titaniumプロジェクトに置いたpackage.jsonのdependenciesにあるモジュールが使えるようになります。
ただしβ版であり、何よりAlloyとの併用ができません.
requireを再定義するti-commonjs
一方、Appcelerator社員とおぼしきtonylukasavageさんは、Titaniumのrequire()
を再定義してNode.jsと同じ仕組みにするためのライブラリ、
ti-commonjsを公開しています。
こちらは逆にAlloy前提の仕組み(alloy.jmk
を使う)です。
##使ってみる
cd <alloyのプロジェクト>
npm install --prefix ./app/lib ti-commonjs
npm install --prefix ./app/lib ti-mocha should underscore # 使うmodule
var should = require('should'),
_ = require('underscore');
require('ti-mocha');
describe('ti-commonjs', function() {
it('should work', function() {
_.each([1,2,3], function(num) {
num.should.equal(num);
});
});
});
mocha.run();
これでビルドすると
ti-commonjs
+ should work
とti-mochaが使えてます!
事前コンパイルに難あり
さて、httpリクエストをさばくライブラリ superagent
は使えるのでしょうか?
var superagent = require('superagent/superagent');
こうするとsuperagentに入っているファイルをAlloyがcompileする過程でエラーが起きます。
たとえwebで使えるファイルがmodule内にあっても、余計なファイルがあったりするとうまくいかないようです。
あとshimもない
結局そのエラーの起きるファイルを削除したりすると...動きます。しかし最終的に、
undefined is not a function (evaluating 'xhr.open(this.method, this.url, true)')
となります。xhrのshimがないんですね。
そうするとJSのロジックのみのライブラリのみが使用可能で、
あまりNodeの資産を利用できないということになります。(これはtitaniumifierにも言えますが)
どうすればいいのか?
- shim欲しい
- zip要らない
- Alloyで使いたい
1に関しては、browserifyの仕組みでshimを入れられるtitaniumifierに分があるといえます。
例えば例に挙げたsuperagent
はti-superagentというisomorphicなものが公開されており、npmでinstallした後、
{ "titanium": { "superagent": "ti-superagent"} }
と書くことでtitaniumifierがこのshimを使ってくれるようです。
ただhttpモジュール自体をwrapするものではないので、すべてのネットワークアクセスに対する
shimにはなりませんが。
2, 3に関しては、ちょっとした工夫で回避できるでしょう。
なので、
titaniumifierのshimをみんなで作ればいい。
yuchi氏も呼びかけています。
tisomorphic を使う (α版)
せっかくこの記事を書いたので、2,3に関する問題を回避したnpmモジュールを公開してみました。
※titaniumifierの依存モジュールのbuildの関係で、install時のみnode v0.12以下にしてください。
使用時はNode v4でOK。
使い方
cd <alloyのプロジェクト>
npm install --save-dev tisomorphic
npm run tisomorphic
こうすると、package.json内のdependencies
に含まれる全てのライブラリが
titaniumifyされて、require()
で使えるようになります。
やってみましょう。
npm install --save superagent es6-promise
var Promise = require('es6-promise').Promise;
var superagent = require('superagent');
var p = new Promise(function(resolve, reject) {
superagent.get('https://github.com/CureApp').end(function(err, res) {
if (err) return reject(err);
resolve(res);
});
});
p.then(function(res) {
console.log(res.text.slice(0, 400));
}, function(err) {
console.log(err);
});
こうするとes6-promiseもsuperagentも使えているのが分かります!
まとめ
Titaniumでisomorphicを実現するには
-
browserify拡張
-
require再定義
がありそれぞれライブラリもあるけど一長一短。 -
みんなでshim作ろう
-
CureApp/tisomorphic 使ってフィードバックくださいな。