2016/12/19追記
現在では様々な状況が変わっており,この記事の内容だけでは動作しません。コメント欄に追加の情報がありますのでそちらを参照してください。
やりたいこと
- JavaScriptを書くのがつらい → TypeScriptを使って型のあるプログラミングをする
- テストを書くためにAPIを覚えるのがダルい → power-assertを使う
- テストをNode.js上ではなくブラウザ上で動かしたい → Karmaを使ってブラウザ上でテストを動かす
それによって生じる依存関係
- TypeScriptを使う → TypeScriptからJavaScriptに変換しなければならない → ts-loader (TypeScriptコンパイラをwebpackから扱うためのモジュール) を使う
- power-assertを使う → intelli-espower-loaderのようなツールを使ってJavaScriptをpower-assert用に変換しなければならない → webpack-espower-loader (power-assert用に変換するコンパイラをwebpackから使うためのモジュール) を使う
- Karmaを使う → ts-loaderとwebpack-espower-loaderを経て変換されたJavaScriptを読み込むためにKarmaからwebpackを呼び出さなければならない → karma-webpackを使う
1. TypeScriptを使う
とりあえず,TypeScriptで書いたソースコードをwebpack, ts-loaderを使ってJavaScriptに変換し,ブラウザで実行するところまで試してみる。
まずはnpmで必要なツールを入れる。
$ npm i -D typescript webpack ts-loader
package.json
は以下のようになった(本当はnameとversionも必須なので記入するべき)。
{ "devDependencies": { "ts-loader": "^0.8.2", "typescript": "^1.8.10", "webpack": "^1.13.1" } }
tsconfig.json
(typescriptの設定ファイル) を書く。書き方は以下のページを参照。
{ "compilerOptions": { "noImplicitAny": true, "noImplicitReturns": true }, "exclude": [ "node_modules" ] }
webpack.config.js
(webpackの設定ファイル) を書く。
module.exports = { entry: { // エントリポイント (main関数のあるファイルみたいなもの) の場所を列挙する。 // エントリポイントの中でrequireやimportによって指定した依存関係のあるファイルをwebpackが自動的に結合してくれる。 // __dirnameはwebpack.config.jsのあるディレクトリ app: __dirname + '/src/browser/app.ts', }, output: { // 出力先のディレクトリを指定する path: __dirname + '/dist', // 出力するファイル名 // [name]にはentryのキーにした名前が入る。今回の例ではapp.bundle.jsになる filename: "[name].bundle.js", }, resolve: { // requireやimportしたときに省略を自動的に補完してくれる拡張子の一覧 // http://dackdive.hateblo.jp/entry/2016/04/13/123000#resolve extensions: ['', '.ts', '.js'], }, module: { loaders: [ // testに書いた正規表現にマッチするファイルをloaderに投げる // loaderを複数指定したときには右から左に適用される // http://dackdive.hateblo.jp/entry/2016/04/13/123000#moduleloaders { test: /\.ts$/, loader: 'ts-loader' }, ], }, };
ブラウザ上で実行するためのHTMLとTypeScriptを書く。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>index.html</title> </head> <body> <script src="dist/app.bundle.js"></script> </body> </html>
// src/browser/app.ts import {add} from "./sub"; console.log(add(2, 3));
// src/browser/sub.ts export function add(x: number, y: number): number { // あとでテストの結果を見るときのためにわざと間違えている return x * y; }
ビルドしてTypeScriptからJavaScriptに変換する。
$ $(npm bin)/webpack
ブラウザで確認するとconsole.log
の結果がコンソールに出ている(出力は5
じゃなくて6
だけど)。
今のディレクトリ構成はこんな感じ。
. ├── dist │ └── app.bundle.js ├── index.html ├── node_modules/ │ └── 略 ├── package.json ├── src │ └── browser │ ├── app.ts │ └── sub.ts ├── tsconfig.json └── webpack.config.js
Mochaとpower-assertでテストを書く
ブラウザ上テストを動かす前に,まずはNode.js上でテストを動かしてみる。
まずは必要なツールを入れる。intelli-espower-loaderはJavaScriptをpower-assert用に変換するモジュール。typingsはTypeScript用の型定義ファイルの管理ツール (Mochaとpower-assertの型定義ファイルを入れたい)。
$ npm i -D mocha power-assert intelli-espower-loader typings
typingsを使って型定義ファイルをダウンロードする。
$ $(npm bin)/typings install --save --global dt~mocha dt~power-assert
power-assertが依存しているファイルが足りないというメッセージが表示されたので,足りていないファイルもダウンロードする(自動的に依存関係を解決してくれないのはなぜ?)。
$ $(npm bin)/typings install --save --global dt~empower dt~power-assert-formatter
TypeScriptでテストを書く。今回は tsc
コマンドでコンパイルするのでreference pathの記述が必要。
// test/browser/sub_test.ts /// <reference path="../../typings/index.d.ts" /> import * as assert from "power-assert"; import {add} from "../../src/browser/sub"; it("2 + 3 = 5", () => { assert(add(2, 3) == 5); });
テストコードをTypeScriptからJavaScriptに変換する。今回はお試しなので tsc
でコンパイルする。
$ $(npm bin)/tsc test/browser/sub_test.ts
テストを実行する
$ $(npm bin)/mocha --require intelli-espower-loader test/browser/sub_test.js
結果がかっこいい。
1) 2 + 3 = 5: AssertionError: # test/browser/sub_test.js:6 assert(sub_1.add(2, 3) == 5) | | | | 6 false Object{add:#function#} [number] 5 => 5 [number] sub_1.add(2, 3) => 6
Karmaを使ってブラウザ上でテストを実行する
必要なツールを入れる。
$ npm i -D karma karma-webpack webpack-espower-loader
Karmaの設定ファイルを生成する。
$ (npm bin)/karma init Which testing framework do you want to use ? Press tab to list possible options. Enter to move to the next question. > mocha Do you want to use Require.js ? This will add Require.js plugin. Press tab to list possible options. Enter to move to the next question. > no Do you want to capture any browsers automatically ? Press tab to list possible options. Enter empty string to move to the next question. > Chrome > What is the location of your source and test files ? You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js". Enter empty string to move to the next question. > test/browser/*.ts > Should any of the files included by the previous patterns be excluded ? You can use glob patterns, eg. "**/*.swp". Enter empty string to move to the next question. > Do you want Karma to watch all the files and run the tests on change ? Press tab to list possible options. > yes Config file generated at "/??/karma.conf.js".
package.json
の devDependencies
にKarmaが必要としているパッケージが追加されているので,インストールする。
$ npm install
karma.conf.js
に以下の内容を追加して,webpackを呼び出してテストコードを変換することについて記述する。
+preprocessors: { + 'test/browser/*.ts': ['webpack'], +}, + +webpack: require(__dirname + '/webpack.config.js'),
webpack.config.js
の中身も編集する。_test.ts
で終わるテスト用のコードは,ts-loaderでTypeScriptからJavaScriptに変換した後にwebpack-espower-loaderでpower-assert用に変換する。
module: { loaders: [ { test: /\.ts$/, loader: 'ts-loader' }, ], + postLoaders: [ + { test: /_test\.ts$/, loader: 'webpack-espower-loader' } + ], }, };
Karmaでテストを実行してみる。
$ $(npm bin)/karma start
Module parse failed: node_modules/estraverse/package.json Unexpected token
なる 謎のエラー が出たのでjson-loaderを入れる。
$ npm i -D json-loader
webpack.config.js
も編集する。
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' },
+ { test: /\.json$/, loader: 'json-loader' },
],
postLoaders: [
{ test: /_test\.ts$/, loader: 'webpack-espower-loader' },
],
},
};
気を取り直して実行する。
$ $(npm bin)/karma start
実行できた。
AssertionError: # sub_test.ts:6 assert(sub_1.add(2, 3) == 5) | | | | 6 false Object{add:#function#} [number] 5 => 5 [number] sub_1.add(2, 3) => 6
まとめ
大変だった… 闇しか感じない…
追記
多段SourceMapを実現するにはどうすれば良いのだろう?