電脳麻将 は HTML5 + CSS3 + JavaScript で動作するフロントエンドのWebアプリです。サーバ側にコードはありません。ですので今までは Node.js を使わずに開発していました。思考アルゴリズムの検証でAI同士に自動対戦をさせる場合もブラウザ上で実行していたのですが、1000半荘の対戦に8時間以上かかるようになり、さすがにブラウザ上での実行は厳しくなってきました*1。
そこで Node.js を導入することにします。せっかく導入するのですから以下の方針とします。
- AI同士の自動対戦や大量の牌譜解析を Node.js 上で行う
- プログラムをNode.jsで実行できる範囲でES6の記述に改める
- ユニットテストを導入する
1.のためにプログラムを require()
を使用したNode流の記述に改めます。ブラウザが理解できるJavaScriptには webpack で変換します。
2.のために Babel を導入します*2が、ES6での記述はNodeが理解できる範囲にとどめ、バックエンド側で変換は行いません。*3
3.のためのフレームワークとして Mocha を導入します。アサーションはNode標準のものを使います。
今回はこれらのソフトウェアのインストールと設定までを行います。
Node.js のインストール
開発環境は OS X 10.9.5(Mavericks) なのですが、pkg 形式のインストーラは使わず、Homebrew を使ってインストールしました。まず Homebrew 自身を最新化します。
$ brew update
Node.js をインストールします*4。
$ brew install node
Node.js と npm がインストールされました。
$ node -v v8.4.0 $ npm -v 5.3.0
プロジェクトディレクトリの初期化
プロジェクトを npm に対応させるために、プロジェクトのルートディレクトリで npm init
を実行し、
package.json
を生成します。
$ npm init -y
すでに README.md
などがある場合は、その内容も反映されるようです。修正が必要な箇所は手作業で修正します。
webpack のインストールと設定
webpackをインストールします。グローバルにインストールしてもよいのですが、まだ仕様が安定していないのでモジュールにより異なるバージョンを使うことが多いようです。という訳でローカルにインストールしますが、開発時にのみ使うので -D
オプションを指定します。
$ npm install -D webpack
新たにディレクトリ node_modules/
が作成され、そこにモジュールがインストールされます。package.json
の devDependencies
にはインストールしたモジュールのバージョン情報が追加されます。npm 5 からは package-lock.json
というファイルも自動生成されるようになりました。*5
npm run build
で webpack がビルドを行うよう package.json
の scripts
にタスクを追加します。
"scripts": { "build": "webpack", "test": "echo \"Error: no test specified\" && exit 1" },
webpack の動作は webpack.config.js
で指定します*6。まずは入力と出力のみ指定します。
module.exports = { entry: './src/js/entry.js', output: { path: __dirname + '/www/js/', filename: 'majiang.js' }, };
entry
は変換前のJavaScriptのエントリーポイントを相対パスで指定します。エントリーポイントとは、そこから require()
をたどればすべてのモジュール参照を解決できる起点となるファイルです。output
は変換後のJavaScriptの置き場所とファイル名です。こちらは絶対パスで指定する必要があります。__dirname
にカレントディレクトリが設定されるのでこれを利用して設定すればいいです。
Mocha のインストールと設定
Mochaをインストールします。開発時のみ使用するのでオプションに -D
を指定します。
$ npm install -D mocha
npm test
でMochaでテストを行うよう、package.json
の scripts.test
を修正します。
"scripts": { "build": "webpack", "test": "mocha -u tdd" },
TDDスタイルでテストコードを書く場合は、オプションに -u tdd
が必要です。
Babel のインストールと設定
ES6に対応していないブラウザ(IEやSafari)のために Babel で変換をかけます。Babel には多くのモジュールがありますが、Babel本体の babel-core
と webpack 向けインタフェースの babel-loader
をインストールします。
$ npm install -D babel-loader babel-core
webpack.config.js
に module
の項目を追加します。
module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' } ] },
上記の設定で、node_modules
以外の .js
ファイルに変換がかかるようになります。
Babel がどのような変換をするかは preset で指定する必要があります。env を指定すると環境に合わせて*7変換してくれるようです。env 用のプラグイン babel-preset-env
をインストールします。
$ npm install -D babel-preset-env
プロジェクトルートに以下の内容の .babelrc
を作成し、env を使用することを Babel に伝えます。
{ "presets": ["env"] }
IEにも対応する場合は、さらに babel-polyfill
も必要になります。babel-polyfill
はES6で追加されたメソッドなどを実行時に既存のクラスに追加します。実行時のモジュールなので -D
を指定せずにインストールします。
$ npm install babel-polyfill
babel-polyfill
を実行時のJavaScriptに含めるよう webpack.config.js
を修正します。
entry: [ 'babel-polyfill', './src/js/entry.js' ],
ソースマップの出力と圧縮
Babelに変換されたJavaScriptをブラウザ上でデバッグするためにはソースマップが必要です。webpack のオプション --devtool inline-source-map
で inline-source-map
形式でソースマップを出力します。また、変換後のソースを直接見ることはないので圧縮してしまいましょう。webpack のオプション -p
で圧縮を指定します。
"scripts": { "build": "webpack --devtool inline-source-map", "release": "webpack -p", "test": "mocha -u tdd" },
package.json
の scripts
を上記のように変更しました。npm run build
でソースマップつきのデバッグ用の出力をします。npm run release
は本番用の出力です。ソースマップは出力せず*8、ファイルも圧縮します。
*1:ブラウザ上で実行する場合、ブラウザが画面の前面に出ていないと実行がスリープしてしまうので長時間の試験がやりにくい。と思っていたのですが、実はヘッドレスChromeで解決できそうです。ですが今後のことを考え、この機会にNode.js化します
*2:すでに for ... of とか限定的に使っているのでIEでは動作しないのだが、この状態が改善されるかも
*3:APIドキュメント生成に ESDoc を使うのであれば、requireをimportにする必要がありますが、それはやり過ぎと思い断念しました
*4:もし複数のバージョンの Node.js を切替えて使いたいのであれば、ここで nodebrew をインストールするのですが、私は perlbrew すら使っていないのでパス
*5:インストールされたすべてのモジュールのバージョン番号を特定しているようです。npmの破綻ぶりがうかがえます😝
*6:恐ろしいことにこの設定ファイルはJavaScriptの「プログラム」なので、モジュールをロードしたり式を評価したりできます
*7:使用しているNodeのバージョンとか
*8:babel-polyfill を使うとソースマップは700kbほどになります