blog.naokihaba.com

2026 年 10 月の OSS 活動

10 月は、Vite+ と vite-task の不具合を直したほか、Nuxt の Storybook モジュールにも PR を出しました。10 月 24 日には Vue Fes Japan 2026 で、「Vite+への貢献と、Team Memberになって見えてきた風景」という題で話しました。資料は Docswell に、スピーカーノートは Zenn(日本語)と DEV(英語)に置いています。

voidzero-dev/vite-plus

fix(config): skip git config writes that change nothing (#2958)

vp config は、値が変わらないときでも、実行するたびに git config に 4 回書き込んでいました。書き込むたびに .git/config.lock を取るので、linked worktree で同時に実行すると、遅れたほうが could not lock config file で失敗します(#2849)。先に今の値を読んで、同じなら書き込まないようにしました。

ただ、clone した直後の初回は値を書く必要があるので、初回の実行が重なるとまだ失敗します。これには待って再試行する仕組みが必要で、その設計には別の判断がいるので、次の PR に分けました。

#2958 の前は、worktree A と B が値が同じでも毎回 .git/config に書くので、B が could not lock config file で失敗する。#2958 の後は、先に値を読み、同じなら書かないのでロックを取らない。ただし clone 直後の初回は書くので、まだ競合する

fix(config): wait for a shared git config lock on first run (#3199)

#2958 の続きです(#2959)。Git 2.55 以前の git config はロックの解放を待たないので、vp config の側で、ロックのせいで失敗した書き込みを再試行するようにしました。最初は 50 ms 待ち、待つ時間を 2 倍にしながら最大 5 回再試行します(合計で最大 1.55 秒)。再試行の前には値を読み、他の worktree が同じ値をもう書いていれば、書き込まずに成功として終わります。

A がロックを持って書いている間、B は 0、50、150、350 ms の時点で書き込みに失敗する。750 ms の時点で値を読むと A の値がもうあるので、B は書かずに成功する。1550 ms の最後の再試行でもロックがあれば、Git のエラーで終わる

fix(env): call Nushell built-ins with the % sigil in env.nu (#3123)

Nushell で def update [] のように組み込みコマンドと同じ名前のコマンドを定義すると、Nushell が起動しなくなるという報告がありました(#3092)。Nushell はパースの時点でコマンド名を解決し、Vite+ の env.nu はユーザーの設定のあとに読み込まれます。そのため、env.nu の中の update がユーザーのコマンドに解決され、引数が合わずに nu::parser::extra_positional で止まっていました。

env.nu では、組み込みコマンドを % sigil を付けて呼ぶようにしました(例: %update)。% sigil は Nushell 0.112.1 で追加されたので、vp env setup が PATH 上の nu のバージョンを確かめて、0.112.1 以降のときだけ付けています。

ユーザーの設定で def update が定義され、そのあとに Vite+ の env.nu が読み込まれる。変更前は env.nu の update がユーザーの update に解決され、引数が合わずに nu::parser::extra_positional で起動しない。変更後は %update が組み込みの update を呼ぶので起動する

fix(env): use the named completer input in env.nu for Nushell 0.116 (#3267)

Nushell 0.116 で、completer が入力を位置で受け取る形が非推奨になり、buffer、place、token という名前で受け取るようになりました(Unified custom completions)。env.nu の completer はまだ位置で context を受け取っていたので、補完のたびに Positional completer input deprecated という警告が出ていました(#3127)。古い形が削除されると補完が動かなくなるので、今のうちに名前で受け取る形に変えました。

fix(env): fall back to file completion in Nushell without fish (#3276)

env.nu の vp と vpr の completer は、^fish を呼んで補完の候補を作っています。fish が PATH にないとこの呼び出しが失敗し、Nushell 0.116 では何も候補が出なくなっていました(#3272)。0.115 では、この場合はファイルの補完が出ていました。

Nushell Book によると、completer が null を返すと、Nushell はファイルの補完に戻ります。そこで、vp と vpr が共通で使う補完の関数に、fish がないときは null を返す 1 行を足しました。

fix(cli): set package-manager lifecycle env for vp run (#2895)

止まっていた #2385 を引き継いだ PR です(#2897)。fengmk2 さんと話して引き継ぐことになり、#2385 の変更を取り込んでコンフリクトを解消しました。#2398 で PackageManager のフィールド名が install_dir から bin_prefix に変わっていて、#2385 は CI のビルドで失敗していたので、その参照とテストも今の API に合わせています。

voidzero-dev/vite-task

fix(reporter): wait and retry when task output forwarding hits EAGAIN (#801)

CI のように vp run の出力をパイプに渡しているときに 2 つのタスクを同時に実行すると、すべてのタスクが止まることがありました(#782、vite-plus#2824)。再現用のリポジトリを作って Vite+ v1.0.0 で試すと、次の流れで止まっていました。

  1. Node.js のタスクが、共有している出力先を O_NONBLOCK にします。そのため、vp の fd 1 もノンブロッキングになります。
  2. もう一つのタスクが大量のログを出し、vp がそれをパイプに書きます。
  3. パイプのバッファがすぐにいっぱいになります。
  4. vp の write が、待たずに EAGAIN を返します。
  5. vp がこれを致命的なエラーとして扱い、すべてのタスクを止めます。

write か flush が WouldBlock を返したら、待って再試行するようにしました。ターミナルやファイル、ブロッキングのパイプへの出力では WouldBlock は起きないので、そちらの動作は変わりません。

タスク A の出力はパイプ ① から vp に入り、vp の fd 1 からパイプ ② を通って CI のログに流れる。Node のタスク B は vp と同じ出力先を共有していて、それを O_NONBLOCK にする。パイプ ② のバッファがいっぱいになると、vp の write が待たずに EAGAIN を返す。変更前は全タスクを止め、#801 の後は WouldBlock なら待って再試行する

nuxt-modules/storybook

fix(addon): use Nuxt’s app context for Nuxt 4.6 compatibility (#1139)

Nuxt 4.6.0 で、ブラウザー側の Nuxt のインスタンスを保存する場所が、unctx から globalThis.__nuxt_app_ctx__ に変わりました(nuxt/nuxt#36371)。この addon は unctx に直接インスタンスを登録していたので、Nuxt 4.6 では useNuxtApp() がインスタンスを見つけられず、NUXT_E1001 を投げていました(#1138)。#app/nuxt の getNuxtAppCtx を使うように変えて、不要になった unctx の依存も消しました。

Nuxt 4.5 までは、addon が unctx に登録し、useNuxtApp() も unctx を読む。Nuxt 4.6 では useNuxtApp() が globalThis.nuxt_app_ctx を読むが、addon は unctx に登録したままなので、NUXT_E1001 になる。#1139 の後は getNuxtAppCtx() で Nuxt が使う場所に登録する