エフアンダーバー

個人でのゲーム開発

WebpackでTypeScriptのモジュール解決

TypeScriptのコンパイラオプションには baseUrl、paths、rootDirsといういくつかの特別なモジュール解決の設定があります。

しかし、これらはあくまでもTypeScriptコンパイラのための設定でWebpackやts-loaderはこれらを考慮してくれません。

そこで、Webpackにこれらの設定を反映させるためのプラグインを書いてみました。

TypeScriptのモジュール解決

TypeScriptのコンパイラオプションには主に次の四つのモジュール解決に関する設定があります。

  • moduleResolution
  • baseUrl
  • paths
  • rootDirs

moduleResolutionは"Classic"か"Node"のどちらかでNode.jsのモジュール解決に準拠するか否かの設定です。

baseUrlは非相対パスが与えられたときにどの基準となる場所を指定します。 tsconfig.json内で指定する場合にはtsconfig.jsonを基準に、直接コンパイラで指定する場合にはカレントディレクトリを基準に設定します。

pathsはpath mappingのためのオプションです。 例えば"@model/foo"というモジュールを"src/model/foo"あるいは"lib/model/foo"に対応させたい場合には、

paths: {
  "@model/*": [
    "src/model/*",
    "lib/model/*"
  ]
}

のように書いておくと、コンパイラが探して解決してくれます。

rootDirsは同じ構造を持つ複数のディレクトリを同一のディレクトリのように扱う設定です。 例えば、自動生成されるコードをgeneratedという別のディレクトリに分離しておきたい場合に、

rootDirs: [
  "src",
  "src/generated"
]

と書いておくと、相対パスで指定した時にこれらのディレクトリはまるでマージされたひとつのディレクトリのように振る舞います。

Webpackにおける問題

TypeScriptのモジュール解決は便利ですが、これらの設定をWebpackやts-loaderは考慮してくれません。 例えエディタ上でエラーが発生していなくても、Webpackでビルドした際にモジュールを解決できずにエラーになります。

moduleResolutionは"Node"にしておけば特に問題は起きませんが、 baseUrl, paths, rootDirsはWebpackの設定ではどうにもならない場合があります (aliasの設定等でなんとかなる場合もあります)。

awesome-typescript-loaderのTsConfigPathsPluginを使うとpathsに関しては解決できるらしいのですが、 残念ながら自分の環境では動作しませんでした(Windowsだから?)。

モジュール解決プラグイン

そこで、TypeScriptのモジュール解決用のプラグインを自作することにしました。

ソースコードは後述。

使い方はwebpack.config.js内でインポートして、resolve直下のplugins内にインスタンスを設定します。

const TsConfigPlugin = require("./tsconfig-webpack-plugin");
...

resolve: {
  plugins: [
    new TsConfigPlugin()
  ]
}

デフォルトではカレントディレクトリのtsconfig.jsonの設定を読みに行きます。

別の場所にあるtsconfig.jsonを読ませたいときは場所を指定します。

new TsConfigPlugin("ts/tsconfig.json")
new TsConfigPlugin({ config: "ts/tsconfig.json" })

tsconfig.jsonを読ませたくない場合にはnullを指定します。 その場合、各オプションはoptionsで直接指定します。

new TsConfigPlugin({
  config: null,
  options: { baseUrl: "src" }
})

デフォルトでは.tsファイルや.tsxファイル以外からモジュール解決をリクエストされても無視します。 それ以外のファイルからのリクエストも受け入れる場合にはtestに対象を正規表現で指定します。

new TsConfigPlugin({ test: /\.\w+$/ })

デフォルトではbaseUrl、paths、rootDirsのすべてを解決しようとしますが、個別に指定することもできます。

const { TsBaseUrlPlugin, TsPathsPlugin, TsRootDirsPlugin } = require("./tsconfig-webpack-plugin");

ソースコード

WebpackのResolverプラグインに関する資料が少なすぎて、 正直ただしく書けている自信がないのですがとりあえず自分の環境では動いてます。

gist.github.com

おわりに

とりあえず作ったので公開してみたのですが、 実は自分に必要な部分以外はほぼテストしていません。 プラグインのオプション周りとかrootDirsとかは動くか怪しいので、 もし動かないようであれば連絡もらえるとありがたいです。



執筆時のwebpackのバージョン: 3.8.1