今年は残暑がそこまで厳しくなくて過ごしやすく、とてもいい感じです。この数年、残暑というか秋が無くていきなり夏から冬にいくような感じでしたし。

最近弄っているTypeScriptで、Native ESM + uvuでテストを動かせるようにしてみたいのですが、とても苦戦したのでとりあえずメモを残しておきます。

やりたいこと

素直にvitest使えばいいじゃんという声が聞こえますが、仕事で効率を求めるならともかく、個人では色々触っておきたいので、jestと同じような書き味になるのがわかってるvitestには食指は動きません。

困っていること

以前も書きましたが、TypeScriptが挟まっているので、どうしても TypeScript → JavaScript という変換を挟む必要があります。しかも、TypeScriptはimportしているライブラリなどについては 一切 書き換えないことを保証しているため、 paths で絶対パスのインポートとかしていると100%ハマります。

ここについては、 tsconfig-paths を利用すると同時に、NodeJSのESM loader(まだまだexperimental)を利用することで解消できます。ここまでは前回書きました。

現在の問題としては、 esbuild-registerがbundleしてしまうため、どうしてもテストの実行に時間がかかる という点です。これ自体に問題はないのですが、テストのビルド時間と実行時間が、

テストの実行時間
20ms前後
テストのビルド時間
2s前後

となってしまっています。uvuは必要最低限のrunnerなので、watchモードとか変更されたものだけ実行とかそういうのは用意されてませんので、このinitialコストは常に残りつづけます

作者のスタンス的に、そういうのはchokidarとかでできるだろ?というスタンスな模様。まぁそりゃそうではある。

試していること

元々esbuild-registerを利用しているところを、swcを利用してみようとしてます。swcはバンドルせずに、単に変換するだけなので、毎回変換するとはいえ、結構速いんではないか?という想定です。

一応動きはして、大体ビルド時間が半分くらいにはなったんですが、今度は xstream に課題があることがわかりました。xstreamは Fake ESM と界隈で呼ばれている、babelなどでTypeScriptからtranspileされたものとして提供されています。これをNative ESMから利用すると、default importが全滅するという状態になってます。( xs.default としないと使えない) esbuildの場合は、1ファイル毎にbundleしていると思われるため、xstreamがCJSでもよしなにしてくれているのだと思いますが。

ただ、数十ファイルくらいしかないのに1秒かかるということは、一つあたり20msだとして、ほぼ直列で処理が走っているということでもあります。FFIはIO待ちではないのかな。

ここをどうしたらいいのか?で止まってしまっています。一応、 babelのプラグインとして node-cjs-interopというのがあるので、これを利用してswc→babelとしてやればそこまで問題ない・・・というところは確認できました。まぁbabelもかけるって時点で色々無駄なのですが。

前述のplugin、swc版もあるのですが、どうもexperimentalの宿命か、swc自体のバージョンアップに伴なって仕組み自体が変更されたためか、現状npmにpublishされているものは、それなりに古いswcでしか動作しないようです。ここを動くようにしたPRを出す、とかやった方がいいかもしれないんですが・・・。

過去のレガシーとの付き合い方

この辺、仕様を先行して実践が進んでしまい、かつそれが広く利用されていたがために発生している、という形なのがまた難しいところです。ライブラリ側が対応するのが基本とはいえ、ライブラリによってはメンテナがすでに活動していないとかもありえると思いますし。

現状は利用側が色々やってあげる必要があります。実際、viteなりwebpackなりを利用していれば、特に問題なくよしなにしてくれるので。でもこういうややこしさがバンドラとかにはあるんだぞ、というのを知るというのも大事かなーと思ったり思わなかったりしている今日この頃です。なんか他にいい方法ないかなーと探している感じです。