Mainly Devel Notes

Twitter, GitHub, StackOverflow: @ovrmrw (short hand of "overmorrow" that means the day after tomorrow)

TypeScript + System.jsの構成におけるSystem.config()の基本パターン。そしてモダンWeb開発の環境をマッハで作る。

System.js, TypeScript, System.config()

前回こういう↓エントリーを書きましたが、System.jsについてよくわかっていなかったのでドキュメントを読んだりして調べました。

overmorrow.hatenablog.com

そういうわけで今回は前回と直接関連はありませんが、実質続編ということになります。

苦労したのは特にTypeScriptのコンパイラとの整合性。
例えばhoge.tsファイル内で同じフォルダにあるfoo.tsのクラスをimportしようとするとき、 import {xxx} from './foo'のように書きますが、 おそらく同じ場所に同じファイル名の.tsファイルと.jsファイルが存在しているのが普通かと思います。
そうなるとTypeScriptのコンパイラはOKを出しても、ブラウザで実行するときにファイルが見つからないエラーになります。
これを解決するためには後述するpackagesdefaultExtension指定が必要です。

また、Lodashのようなライブラリをimportしようとするとき、 import * as _ from 'lodash'のように書きますが、 後述するbaseURL,paths,mapをうまく組み合わせて実体ファイルまでのパスを正確に指定しておかないと、 ブラウザで実行するときにファイルが見つからないエラーになります。

これらを踏まえて TypeScript + System.js の構成でどのようにSystem.config()を書けば良いか、 僕なりに調べた結果をメモとして残します。
この例ではconfig.jsファイルにまとめて書いて、index.htmlから<script>タグで参照します。
ちなみにbowerjspmのようなフロントエンドライブラリ管理ツールは使いません。npmだけです。それにgulpgruntのようなビルドツールも使いません。
とりあえず"何かを試したい、それもちょっとだけ"というとき、モダンWeb開発の環境をいちいち構築するのはめんどくさいじゃないですか。気軽にさくっとやりたいですよね。タイトルのようにマッハで、とはいきませんが本当に最低限のことしかやりません。

npmで必要なものをインストール

npm init -y
npm install systemjs lodash babel babel-polyfill --save
tsc --init

babelbabel-polyfillは実行時に.jsファイルをBabelでコンパイルするのに使うライブラリです。
詳しくはElectronでAngular2を動かすついでにasync/awaitも試してみた。を参照してください。

tsdで定義ファイルをインストール

tsd install lodash --save

フォルダを作ります

mkdir src

(root)/src/index.html の例 (angular2は今回のエントリーと直接関連はありません)

<head>
  <script src="../node_modules/systemjs/dist/system.js"></script>
  <script src="../node_modules/angular2/bundles/angular2.dev.js"></script>
  <script src="config.js"></script>
  <script>
    System.import('app/app');
  </script>
</head>

(root)/src/config.js の例 【今回のエントリーで一番書きたかった部分】

System.config({
  baseURL: '.',
  transpiler: 'babel',
  paths: {
    'node:*': '../node_modules/*'
  },
  map: {
    'babel-polyfill': 'node:babel-polyfill/dist/polyfill.min.js'
  },
  packages: {
    'app': { defaultExtension: 'js' }
  },
  meta: {
    'app/*.js': { deps: ['babel-polyfill'] }
  }
});
  • baseURLプロパティは何よりも優先される。'/'を指定するとこの後の全てのパス指定はルートが基準となる。ただしElectronの場合はドライブのルートになる点に注意。ブラウザ環境でしか開発しない人は気にしないでください。
  • baseURLを省略するか'.'とすると呼び出し元のindex.htmlが存在するパス、今回の例では(root)/src/baseURLとなる。
  • transpilerプロパティで実行時コンパイルに使いたいライブラリを指定する。特に指定しない場合はfalseにする。
  • pathsプロパティで'node:*': '../node_modules/*'と指定すると、mapプロパティ内のnode:を置き換えられる。
  • この場合map'lodash': 'node:lodash/index.js'と指定すると、 System.import('lodash')と書いたときに(root)/node_modules/lodash/index.jsを参照することになる。
  • packagesプロパティの'app'は、baseURL,paths,mapの全てを使ってパスが解決される。 この場合はbaseURLと結合して、(root)/src/app/となる。
  • defaultExtension: 'js'と指定しているので、(root)/src/app/フォルダにapp.tsファイルとapp.jsファイル が両方存在する場合、指定と一致するapp.jsを参照する。
  • この場合System.import('app/app')と書いたら、 最初のapp(root)/src/app/フォルダを意味し、次のappapp.jsを意味する。よって (root)/src/app/app.jsを参照することになる。
  • metaプロパティでは依存関係を記述できる。それ以外の使い方はわからない。 'app/*.js'とは(root)/src/app/*.jsを意味する。それらが読み込まれるとき、depsで指定した'babel-polyfill'を一緒に読み込む。つまり依存関係の解決をする。

.tsファイルのコンパイルやブラウザのオートリロードなんかはpackage.json"scripts"を書くだけで最低限のことはできます。がっつりやるのでなければgulpgruntは要りません。その辺は前回のエントリーを参照してください。


以上です。ありがとうございました。