C++ ときどき ごはん、わりとてぃーぶれいく☆

USAGI.NETWORKのなかのひとのブログ。主にC++。

CRA=create-react-app が WSL で start できない理由と回避方法のメモ

問題

WSLでCRAしてyarn startするとcmd.exeを実行できずにウェブブラウザーの起動どころかサーバーも起動せず死んでしまいます。

再現方法:

  1. WSLで
  2. npx create-react-app hoge して
  3. cd hoge; yarn start します
Starting the development server...

events.js:291
      throw er; // Unhandled 'error' event
      ^

Error: spawn cmd.exe ENOENT
    at Process.ChildProcess._handle.onexit (internal/child_process.js:268:19)
    at onErrorNT (internal/child_process.js:468:16)
    at processTicksAndRejections (internal/process/task_queues.js:80:21)
Emitted 'error' event on ChildProcess instance at:
    at Process.ChildProcess._handle.onexit (internal/child_process.js:274:12)
    at onErrorNT (internal/child_process.js:468:16)
    at processTicksAndRejections (internal/process/task_queues.js:80:21) {
  errno: -2,
  code: 'ENOENT',
  syscall: 'spawn cmd.exe',
  path: 'cmd.exe',
  spawnargs: [ '/s', '/c', 'start', '""', '/b', 'http://localhost:3000' ]
}
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

そもそもWSLで実行しているのにcmd.exeなどとわけのわからない事を言われたので「またyarnか何かの実行バイナリーがWSL内ではなくWindowsの何かが何故か優先されているふぁっきゅーかな」と思いましたが、同じくらいふぁっきゅーなCRA側の問題でした。

回避策; 本質的な解決策は執筆時点でもまだCRAにマージされていません

1. BROWSER=none 戦術

いまのところ、わたしのおすすめはこちらの回避策です。

  • yarn startBROWSER=none yarn start にします
  • または package.json"start": "BROWSER=none react-scripts start" して yarn start します

note:

  • package.json を変更する場合に、開発環境の可搬性に PowerShell, cmd など対応したい場合は cross-env 付きで仕込みます。
    • またはプロジェクトで採用するビルドエコシステムの方針によって run-script-os が有用な場合もあるかもしれません。
 "scripts": {
  "start": "run-script-os",
  "start:default": "cross-env BROWSER=none react-scripts start",
  "start:win32": "react-scripts start",

2. PATH=$PATH:/mnt/c/Windows/System32 戦術

  • yarn startPATH=$PATH:/mnt/c/Windows/System32 yarn start にします
  • または package.json"start": "PATH=$PATH:/mnt/c/Windows/System32 react-scripts start" して yarn start します
  • または WSL の実行環境の PATHexport PATH=$PATH:/mnt/c/Windows/System32 します
  • または /etc/wsl.conf[Interop] セクションで appendWindowsPath = True または False を定義しない設定へ変更します
  • または cmd.exe を実行可能な他のお膳立てをしてあげます

原因

  • CRAがブラウザーを起動するために"cmd.exeを使える状態を前提"にのみ実装されている
  • is-wsl で WSL を検出して気を利かせてcmd.exeを起動しようとしているっぽい †参考1

参考

関連おまけ

思うところメモ

BROWSER=none 回避策は PATH 回避策より一般的には良いです。WSLのユーザーのおそらくほとんどはWSLを"WindowsGNU/Linux悪魔合体した何か"ではなく、Windowsから便利に利用しやすいGNU/Linux環境として利用し、WSLがWindows上で実行されながらもWindows特有の環境要因にはできるだけ依存せずGNU/Linux環境としての純粋性や可搬性を維持したWindowsとは異なる環境かつWindowsとのつながりもユーザーが求めれば構築しやすい、そんな何かであることを望んでいるのではないかな、と思います。参考の Issue #7251 でもそういう考え方の開発者さんは少なくも無さそうな雰囲気があります。

もちろん、is-wslからcmd.exeによるWindowsネイティブのブラウザー呼び出しを試みようとする、その気の利いた工夫は良い事です。しかし、現状の実装は想定が甘く、WSLを悪魔合体的な環境で利用されている状況だけを前提に fallback も無い実装になっている事が悲しみを生んでしまった原因になっていると思います。開発環境の可搬性も保守性には有用な視点の1つにもなりますし、 default でも巧く解決されて多くのユーザーが意図に反したエラーに悩まされない実装になると嬉しいです。