7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Appcelerator TitaniumAdvent Calendar 2015

Day 11

Titaniumでもnpmモジュールを使う

Last updated at Posted at 2015-12-10

JSの資産をTitaniumでも利用したい

今やnpmに登録されていないJSライブラリは、存在していないも同然。
そんな時代ですね。
TitaniumでもそのJSの資産を使いたい。そんなことは可能なんでしょうか。

2つの問題

Titaniumでなぜnpmモジュールがそのまま使えないのでしょうか?
理由は2つです。

  1. requireが違うから。特にnode_modules等はNodeの独自拡張なので、Titaniumはそこを辿れない。
  2. 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
app/alloy.js
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 は使えるのでしょうか?

app/alloy.js
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にも言えますが)

どうすればいいのか?

  1. shim欲しい
  2. zip要らない
  3. Alloyで使いたい

1に関しては、browserifyの仕組みでshimを入れられるtitaniumifierに分があるといえます。
例えば例に挙げたsuperagentti-superagentというisomorphicなものが公開されており、npmでinstallした後、

package.json
{ "titanium": { "superagent": "ti-superagent"} }

と書くことでtitaniumifierがこのshimを使ってくれるようです。

ただhttpモジュール自体をwrapするものではないので、すべてのネットワークアクセスに対する
shimにはなりませんが。

2, 3に関しては、ちょっとした工夫で回避できるでしょう。
なので、
titaniumifierのshimをみんなで作ればいい。
yuchi氏も呼びかけています。

tisomorphic を使う (α版)

せっかくこの記事を書いたので、2,3に関する問題を回避したnpmモジュールを公開してみました。

CureApp/tisomorphic

※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
app/alloy.js

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 使ってフィードバックくださいな。

7
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?