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

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

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

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

1. `require`が違うから。特に`node_modules`等はNodeの独自拡張なので、Titaniumはそこを辿れない。
2. `process`, `http`などNode固有のAPIを呼んでいるから。


## ブラウザではどう解決してきた？
ブラウザのisomorphic JSに関しては、
[RequireJS](http://requirejs.org/)等の混迷期?を経て、天下統一を果たしたのは[browserify](http://browserify.org/)。
このライブラリは、コマンド1つでブラウザ向けにnpmモジュール入りのコードを全て入れて、
必要なshimも入れて、1つのJSファイルを生成してくれます。

## Titaniumはどう解決すべき？
Titaniumは、独自に`require()`を持っていることがブラウザと違う点です。
よって、**global requireを再定義**するようなアプローチが可能です。
ここから2つのアプローチを紹介し、それぞれのメリット・デメリットをみていきます。

# Titanium界の魔法の杖(?) titaniumifier
イタリアの[yuchi](https://github.com/yuchi)氏はbrowserifyを拡張して、Titaniumにもその仕組みの導入を可能にしました。
それが[Titaniumifier](https://github.com/smclab/titaniumifier) です。

`function.prototype.bind()`のちょっとしたshimが入っています。
最終的にTitanium CommonJS Modulesの仕組みでzipファイルをつくってくれます。

動作環境に注意 (Node v4以降ではインストールできず. ([Pull Request中](https://github.com/smclab/titaniumifier/pull/41))
もしNode v4以降でインストールする方は[fork先](https://github.com/CureApp/titaniumifier)を使って下さい。）

## 使ってみる
```bash
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
```

そうすると

```text
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と思います。

```json
{
  "titaniumManifest": {
    "guid": "d46c75a6-d920-45ed-7657-0b7632b56cc7"
  }
}
```

そして再度

```bash
titaniumifier --out dist
```

とすると、晴れてdistのなかに `es6-promise-commonjs-3.0.2.zip`なるファイルができました。


## zipファイルは筋が悪いよ
しかしこの後、[Titaniumでモジュールを使う方法](http://docs.appcelerator.com/platform/latest/#!/guide/Using_a_Module)にしたがってこのzipを適切な場所に置くなどの作業が必要になります。
browserifyみたいに手軽にやりたかったはずがこれでは、Nodeな人に嫌われてしまいます。


## package.jsonの依存モジュールをそのまま使えるHost mode (beta)
Titaniumifierの[Host mode](https://github.com/smclab/titaniumifier/wiki/Host-mode)というのを使えば、 Titaniumプロジェクトに置いたpackage.jsonのdependenciesにあるモジュールが使えるようになります。

ただしβ版であり、何より**Alloyとの併用ができません.**


# requireを再定義するti-commonjs
一方、Appcelerator社員とおぼしき[tonylukasavage](https://github.com/tonylukasavage)さんは、Titaniumの`require()`を再定義してNode.jsと同じ仕組みにするためのライブラリ、
[ti-commonjs](https://github.com/tonylukasavage/ti-commonjs)を公開しています。
こちらは逆にAlloy前提の仕組み(`alloy.jmk`を使う)です。

##使ってみる

```bash
cd <alloyのプロジェクト>
npm install --prefix ./app/lib ti-commonjs
npm install --prefix ./app/lib ti-mocha should underscore # 使うmodule
```

```js: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();
```

これでビルドすると

```text
  ti-commonjs
    + should work 
```
とti-mochaが使えてます！

## 事前コンパイルに難あり
さて、httpリクエストをさばくライブラリ `superagent` は使えるのでしょうか?

```js:app/alloy.js
var superagent = require('superagent/superagent');
```
こうするとsuperagentに入っているファイルをAlloyがcompileする過程でエラーが起きます。
たとえwebで使えるファイルがmodule内にあっても、余計なファイルがあったりするとうまくいかないようです。

## あとshimもない
結局そのエラーの起きるファイルを削除したりすると...動きます。しかし最終的に、

```text
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に分があるといえます。
例えば例に挙げた`superagent`は[ti-superagent](https://github.com/smclab/ti-superagent)というisomorphicなものが公開されており、npmでinstallした後、

```json:package.json
{ "titanium": { "superagent": "ti-superagent"} }
```
と書くことでtitaniumifierがこのshimを使ってくれるようです。

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

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



# tisomorphic を使う (α版)

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

[CureApp/tisomorphic](https://github.com/CureApp/tisomorphic)

※titaniumifierの依存モジュールのbuildの関係で、install時のみnode v0.12以下にしてください。
使用時はNode v4でOK。

## 使い方
```bash
cd <alloyのプロジェクト>
npm install --save-dev tisomorphic
npm run tisomorphic
```
こうすると、package.json内の`dependencies`に含まれる全てのライブラリが
titaniumifyされて、`require()`で使えるようになります。

やってみましょう。

```bash
npm install --save superagent es6-promise
```

```js: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](https://github.com/CureApp/tisomorphic) 使ってフィードバックくださいな。