TypeScriptをWebPackでTreeShakingするにはmoduleをes6にする+α

2023-04-24

TypeScriptでコードを書いて、ブラウザで動かすためにWebPackでまとめてるアプリで、Worklet用に別に出力しているソースに不要なコードも含まれてしまってサイズが大きくなってしまっていた。 「Worklet側でもimportされるコードをソースを手動で分離するか〜」と思ったが、TreeShakingという機能でできるということなのでやってみた。

TypeScriptのコードでTreeShakingを有効にする

WebPackのTree Shakingの説明によればwebpack.config.jsにoptimization: {usedExports: true}と指定すればいいとのこと。 がやってみるとされなかった。 他にもpackage.jsonに"sideEffects": falseを指定してみたりしたけど変わらず。

ググったところ、TypeScriptから変換している場合にはtsconfig.jsonの"compilerOptions"内で"module": "es6"とする必要があるとのこと: (Typescript + Tree shaking configuration · Issue 995 · webpack/webpack.js.org

"commonjs"だったのを"es6"に変更することでTreeShakingが機能するようになって、生成されるjsのファイルサイズが小さくなった、めでたしめでたし…

  • 要するにTypeScriptからJavaScriptにトランスパイルする際に、"commonjs"だとimportrequireに変換されてしまい、それだとES6モジュール定義が失われてしまいTreeShakingで判定されない
  • WebPackのTypeScriptのBasic Setup はちゃんと"module": "es6"となっていた

ts-nodeも動くようにする

しかし上記の設定をすると、ツールとしてローカルで動かしていたTypeScriptのコードでエラーが出るようになってしまった:

$ npx ts-node ./tools/getmapperno.ts test.nes
(node:43571) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
(Use `node --trace-warnings ...` to show where the warning was created)
./tools/getmapperno.ts:1
import * as fs from 'fs';
^^^^^^

SyntaxError: Cannot use import statement outside a module

TypeScriptのコードをts-nodeで動かしていたんだけどその場合も"es6"モジュールとしてコンパイルされ、それがnode.jsで動かせないっぽい。 ワーニングに示されてる対応をしても動作しなかった。

あれこれ調べたが、結局はts-nodeの説明に書かれていた。 曰く、tsconfig.jsonに追加で"ts-node": {"compilerOptions": {"module": "CommonJS"}}と指定する:

[tsconfig.json]
{
"compilerOptions": {
"module": "es6",
...
},
...,
"ts-node": {
"compilerOptions": {
"module": "CommonJS"
}
}
}

(他にはWebPackで使用するtsconfigのファイルを変更(webpack + ts-loader で使う tsconfig.json を動的に切り替える - mizdev)、 またはts-nodeにコマンドラインオプション-Pで指定という方法もあった。)

ts-nodeの場合"CommonJS"としてコンパイルするとして回避したけど、そうじゃないと動かない理由というのはよくわかってない…JavaScriptのモジュールの仕組みもよく理解してない…。

リンク