🧊

Node.jsとUndiciの依存関係によって、SvelteKitが予期せぬ挙動になった件

あらすじ

  • SvelteKitのv1.27.0で書かれたアプリがあった
  • <input type=file webkitdirectory multiple>を使って、FormDataに載せてサーバーに送ってる
  • v1.27.0では、サーバー側でそのFile.nameから、パス情報を含んだ状態で受け取れた
  • v1.27.1以降、サーバー側でパス情報が受け取れなくなってた
  • ブラウザからのリクエスト時点では、パス情報が含まれてることは確認できてる

というわけで、SvelteKitをアップデートしたら、パッチアップデートしかないはずなのに壊れた・・・ということを調査してた。

あれこれ調べた結果、Requestのポリフィルの有無でそれが起きることがわかった。

変更としては、v1.27.1のこのPRで入ったもの。

fix: only apply some polyfills below node 18.11 by dummdidumm · Pull Request #10920 · sveltejs/kit https://github.com/sveltejs/kit/pull/10920

すべてのNode.jsバージョンでポリフィルしてたものを、18.11以下でだけやるようになったと。

つまり、ポリフィルが使われなくなった結果、挙動が変わったということ。

調査

アプリ側の問題?

そもそもポリフィルは独自実装であり、できるならば使わないほうが良いように思う。

それで挙動が変わったなら、それこそが本来の挙動というべきであり、アプリの実装が間違った用途で利用してたってことになるはず。

そう思って、最初はアプリ側のコードを修正した。

が、Cloudflare Workersに実際にデプロイしてみると、修正したコードは動かず、修正前のコードでは動作するという状態に。

  • Cloudflare Workers: パス情報を含んでる
  • ローカルでのvite dev: パス情報を含まない

Bunで最小構成を組んでみても、パス情報は含まれてた。

ので、ローカルでの環境に何らかの問題があるはずで、

  • ViteのSSRとSvelteKitの接合点の問題
  • Node.js自体の問題

という切り分けをした。

ViteはただのNode.jsに従うのみ

https://github.com/sveltejs/kit/tree/master/packages/kit/src/exports/vite

コードを追ってみたが、Requestに対して特別なことはしておらず、ただグローバルにあるそれを使ってた。

関係あるとすれば、SvelteKitがグローバルをポリフィルする某PRにもあった部分。

となるとNode.jsの問題

この時点で使ってたNode.jsのバージョンはv20.2.0で、そこそこ新しいものだったので、まさかここに問題があるとは思ってなかった。

が、試しにv21.2.0にあげてみたら、見事に問題が起きなくなった。

Node.js・・・お前だったのか・・って感じ。

原因はNode.jsとUndiciのバージョン

原因は、Node.jsが依存しているundiciのバージョンが、Node.js自体のバージョンによって違うことだった。

  • Node@20.2.0: Undici@5.22.0
  • Node@21.2.0: Undici@5.27.0

この間のundiciのCHANGELOGを追ってみると、ドンピシャなアップデートがundiciに入ってた・・・。

fix: preserve file path when parsing formdata by jimmywarting · Pull Request #2245 · nodejs/undici https://github.com/nodejs/undici/pull/2245

これは、Undiciのv5.24.0から入ってる。

Release v5.24.0 · nodejs/undici https://github.com/nodejs/undici/releases/tag/v5.24.0

Undiciのv5.24.0が使われるようになったNode.jsのバージョンは、v20.8以降なので、v20.2のNode.jsではダメだが、v21.2のNode.jsでは想定通りの挙動になるというわけ。

SvelteKitのバージョンアップによって、ポリフィル(Undici最新)が使われなくなった結果、古いUndiciに依存したNode.jsが使われることになり、それで予期せぬ挙動になった。

久々にこういうハマり方したわ・・・。