Node.js + Express で HTTPサーバを作りコンパクトな実行形式にする（Scratch 2 拡張を例に）

# Node.js 版 Scratch 2 HTTP拡張ヘルパーを作ろう

Node.js + express で作成したものを、zeit/pkg を使って各OSの実行形式ファイルにするテストも行います。
ブラウザ(Chromium)は含める必要がないため、Electron より軽量（小さいファイルサイズ）になるはずです。

https://github.com/memakura/scratch2-ex-nodejs

## Scratch 2 とHTTP拡張ヘルパー

HTTPサーバ (ヘルパーアプリ) を用意すると、ブロックプログラミングの Scratch 2（オフライン版）に独自機能（拡張ブロック）を加えることができます。
[こちらの記事](https://qiita.com/memakura/items/fb48d7f6fb6b4b88b5bb)ではPython + aiohttpでヘルパーを作成しましたが、これを Node.js + Expressで作成してみます。さらに、それを Node.js がインストールされていない環境で使用するために、 Windows の実行形式にしてみます（Mac や Linux も出来ますが実行できるかは手元に環境がないため未テスト）。

## ここで作成するヘルパーは簡易HTTPサーバ

Scratch 2 (offline版) の拡張ブロックには、Scratch側からコマンドを送る「コマンドブロック」（四角い形のブロック）と、Scratch側で値を受け取る「レポーターブロック」（角の丸い形のブロック）など、いくつかの種類があります。いずれも Scratch側から GET を送っており、値を受け取るときもポーリング方式をとっています。したがって、Scratch からの GET を待ち受けるような簡易ローカルHTTPサーバを作成してそこで何か処理を行うようにすれば、新たな機能を拡張ブロックとして追加できます。

![command_and_reporter.png](https://qiita-image-store.s3.amazonaws.com/0/55780/a015423c-7608-c898-d96c-5af35c450717.png)

 
[こちらの記事](https://qiita.com/memakura/items/fb48d7f6fb6b4b88b5bb)のように、Python版では aiohttp を使って以下のようなコードにしました。

```py3:testhelper.py
from aiohttp import web

volume = 0

async def handle_poll(request):
    text = "volume " + str(volume) + "\n"
    return web.Response(text=text)

async def handle_beep(request):
    print("play beep!")
    print("\007")
    return web.Response(text="OK")

async def handle_setvolume(request):
    global volume  # 忘れずに
    tmp_volume = int(request.match_info['vol']) # いったん別の変数へ
    if tmp_volume >= 0 and tmp_volume <= 10:
        volume = tmp_volume
        print("set volume= " + str(volume))
    else:
        print("out of range: " + str(tmp_volume))
    return web.Response(text="OK")

app = web.Application()
app.router.add_get('/poll', handle_poll)
app.router.add_get('/playBeep', handle_beep)
app.router.add_get('/setVolume/{vol}', handle_setvolume)

web.run_app(app, host='127.0.0.1', port=12345)
```

なお `volume` という変数名ではありますが、この実装では音量が変わるわけではないです・・（単に値のやりとりのテストです。）

## Express + Node.js 版

上記のコードを Express + Node.js で作ると以下のようになります。

``` js:scratch-ex.js
"use strict";

var express = require('express');
var app = express();

var server = app.listen(12345, function(){
    console.log("Server start... listening port " + server.address().port);
});

var volume = 0;

app.get('/poll', (req, res) => {
    res.send("volume " + volume);
});

app.get('/playBeep', (req, res) => {
    console.log("play beep!");
    process.stderr.write("\x07"); // to prevent new line (instead of console.log)
    res.send("OK");
});

app.get('/setVolume/:volume', (req, res) => {
    var tmp_volume = req.params.volume;
    if(tmp_volume >= 0 && tmp_volume <= 10){
        volume = tmp_volume;
        console.log("set volume= " + volume);
    }else{
        console.log("out of range: " + tmp_volume);
    }
    res.send("OK");
});
```

実行するにはまず Node.js および Express をインストールしておきますが、たとえば[この記事](https://qiita.com/nkjm/items/723990c518acfee6e473)が参考になります。

```bat
> mkdir new_project
> cd new_project
> npm init
```

順に答えていき、`"main"` はファイル名（上の例では`scratch2-ex.js`）にします。また、このディレクトリ内にファイル `scratch2-ex.js` を置いておきます。

```bat
> npm install express --save
> node scratch2-ex.js
```

これで Express サーバが実行されます。


## zeit/pkg で実行形式にする

実行形式にするには Electron を使うこともできますが、インストール後のサイズがかなり大きくなります（Hello world でも 100MB を越える）。[Electrino](https://github.com/pojala/electrino) など、Electron を軽量化するプロジェクトもあるようですが、今回はコンソールアプリでブラウザ機能は不要なので、（[EncloneJS](https://github.com/igorklopov/enclose)の後継の）[zeit による pkg というツール](https://github.com/zeit/pkg)を使ってみます。他にも [node-packer (nodec)](https://github.com/pmq20/node-packer) というのもありましたが、pkg の方がすんなり動いたので今回はこちらにします。

インストールは非常に簡単です。

```
> npm install -g pkg
```

使い方も簡単で、以下のコマンドで Windows (x64) の実行形式にできます。

```
> pkg -t win-x64 scratch2-ex.js
```

これで `scratch2-ex.exe` ができるので、実行して試してみます。

Scratch 2 オフライン版を立ち上げ、シフトを押しながら「ファイル」メニューをクリックし、「実験的なHTTP拡張を読み込み」から[このような感じのJSON形式のファイル](https://github.com/memakura/scratch2-extension/blob/master/test.s2e)を読み込んで試してみます。詳しくは[以前の記事](https://qiita.com/memakura/items/fb48d7f6fb6b4b88b5bb)を参照してください。
Scratch 2 からのポーリングに反応していれば、赤丸が緑色になります。

`-t` はターゲット指定で、nodeのバージョン、OS (freebsd, linux, macos, win)、アーキテクチャ (x64, x86, armv6, armv7) などを指定できます。

```
> pkg scratch2-ex.js
```

のように省略すると、linux, macos, win がデフォルト値で一度に作成されます。詳しくは [https://github.com/zeit/pkg](https://github.com/zeit/pkg) を確認してみてください。Windows 以外のプラットフォームについては機会を見てテストしてみることにします。