Javascriptでコマンドラインアプリを作ってみる: JXcore, nexe, EncloseJSの比較

# はじめに
Javascript(Node.js)は非常に便利なモジュールが揃っているのでちょっとしたツール作りに重宝していますが、Node.jsをインストールしていないユーザに配布するなら、実行ファイル形式に変換できたら便利だよなーということで、Javascript→実行ファイル変換の機能を持つ以下のアプリを試してみました。

* [JXcore](https://github.com/jxcore/jxcore)
* [nexe](https://github.com/nexe/nexe)
* [EncloseJs](http://enclosejs.com/)


# パッケージ化例
以下のコードをパッケージ化してみました

```js:test.js
console.log('Hello world');
console.log(process.argv);
console.log(process.versions);
```

Windows10 64bitのcmd.exe上で、以下のコマンドでパッケージ化しています。

```text:jxcore
jx package test.js -native -name output/jxcoreteest
```

```text:nexe
nexe -i test.js -o output\nexetest.exe
```

```txt:EncloseJS
enclose -o output\enclosetest.exe test.js
```

パッケージ化したファイルの実行結果

```text:JXcore
Hello world
[ 'd:\\user\\js\\packaging-test\\src\\output\\jxcoreteest.exe' ]
{ http_parser: '1.0',
  node: '0.10.40',
  jxcore: '0.3.1.1',
  ch: 0,
  v8: '3.14.5.9',
  sm: 0,
  embedded: { sqlite: '3.9.1' },
  ares: '1.9.0-DEV',
  uv: '0.10.36',
  zlib: '1.2.3',
  modules: '11',
  openssl: '1.0.1p' }
```

```text:nexe
Hello world
[ 'd:\\user\\js\\packaging-test\\output\\nexetest.exe',
  'nexe.js' ]
{ http_parser: '2.8.0',
  node: '8.11.1',
  v8: '6.2.414.50',
  uv: '1.19.1',
  zlib: '1.2.11',
  ares: '1.10.1-DEV',
  modules: '57',
  nghttp2: '1.25.0',
  openssl: '1.0.2o',
  icu: '60.1',
  unicode: '10.0',
  cldr: '32.0',
  tz: '2017c' }
```

```text:EncloseJS
*** Evaluation version. Please subscribe to full version.
Hello world
[ 'd:\\user\\js\\packaging-test\\output\\enclosetest.exe',
  'd:\\user\\js\\packaging-test\\output\\enclosetest.exe' ]
{ http_parser: '2.7.0',
  node: '6.3.1',
  v8: '5.0.71.57',
  uv: '1.9.1',
  zlib: '1.2.8',
  ares: '1.10.1-DEV',
  icu: '57.1',
  modules: '48',
  openssl: '1.0.2h',
  enclose: '2.2.0' }
```

また、`npm install`でインストールした外部モジュールを使用したコードも、node_modulesディレクトリごとパッケージ化してくれるので、配布の際は実行ファイル1個だけになり非常に楽です。

## [JXcore](https://github.com/jxcore/jxcore)
### 長所
* パッケージ化が速い

### 短所
* 内包しているエンジンがNode.js Ver0.1x相当なのでES6の文法が使用できない
* ネイティブモジュール使用不可
* パッケージ化の有無で`process.argv`の内容が違う

`jx test.js 1 2 3` のprocess.argv

```json:パッケージ化前
[ 'jx',
  'd:\\user\\js\\packaging-test\\src\\test.js',
  '1',
  '2',
  '3' ]
```

パッケージ化後に`実行ファイル名 1 2 3`を実行したときのprocess.argv

```json:パッケージ化後
[ 'd:\\user\\js\\packaging-test\\src\\output\\jxcoreteest.exe',
  '1',
  '2',
  '3' ]
```

パッケージ化したかどうかは`process.isPackaged`で判別できるので、以下のようなコードを書く必要があります。

```js
if (!process.isPackaged) {
   process.argv.shift();
}
```

* **開発終了している**

## [nexe](https://github.com/nexe/nexe)
### 長所
* 内包するNode.jsのバージョンを任意に選択できる
* 当然ES6の文法使用可

### 短所
* ネイティブモジュール、動的require使用不可

```js:動的requireは以下のようなコード
const moduleName = 'somemodule';
const module = require(moduleName);
```

* ~~インストールが非常に手間~~
 * ~~nexe本体以外にnode.jsをコンパイルできる環境(VisualStudio 2010以上(+.NET Framework4.6), Python2.x)が必要~~
 * nexe Ver2.0になって、パッケージ化の際にコンパイル済みバイナリをダウンロードするようになりました。そのためいちいちnode.jsをビルドする必要はありません
* ~~パッケージ化にべらぼうに時間がかかる~~
 * ~~初回パッケージ化時にNode.jsのダウンロード+ビルドを行うため、非常に時間がかかります。パッケージ化例のコードのパッケージ化にはi7-4790環境で15分くらいかかりました~~
 * バイナリのダウンロードを行う初回パッケージ化時は少し時間がかかりますが、2回目以降はあっという間にパッケージ化が終わります
* 以前はパッケージ化時にソースコード中のマルチバイト文字が全て削除されるという問題がありましたが、直ってました

## [EncloseJs](http://enclosejs.com/)
### 長所
* パッケージ化が速い
* 内包するNode.jsのバージョンを選択できる(0.12.x, 4.x or 6.x.) なのでこれもES6の文法使用可
 * 使用できるバージョンは [binaries.json](https://github.com/igorklopov/enclose/blob/master/bin/binaries.json)にあります。
* ネイティブモジュール使用可、らしい
 * ただしネイティブモジュールは実行ファイルにパッケージングはされず、パッケージ化した実行ファイルと一緒に配布する必要があるらしい(未確認)。

### 短所
* **プロキシ下の環境でインストールできない**
 * 素の`https.get()`を使用してバイナリをダウンロードしようとしているため、プロキシ下だとインストールに失敗する。postinstall.jsを修正すればインストール可能。
* **パッケージ化したファイルを実行すると、カスペルスキーが「これはAdwareだよ」警告を出す**
* パッケージ化したファイルを実行すると必ず「これは評価版だから登録してね」メッセージが表示されるが、「登録」する方法が見当たらない。公式サイトの"BUY"ページは"Not available"になっているし。

# まとめ(2018/5/15更新)
* パッケージ化にちょっと時間はかかるが、機能的にはnexe1択。他のツールを使う必要はなさそう
* Node.js 0.1xの文法のみを使用して書くのであればJXcoreは安定している
* カスペルスキーの警告や「登録しろ」メッセージが出るEncloseJSはちょっと不安

こんな印象です。
他にもJavascriptをコマンドラインアプリ化できるよいツールをご存じでしたら、ぜひご教示くださいませ。