前言#
昨日、ツイートを投稿しましたが、ツールチェーンを作成している際に Windows に関連する互換性の問題に直面したため、感情的に一言愚痴を言ったものです。しかし、ツイートの雰囲気からすると、このような「暴論」を発信しないと、意味不明な注目を集めることはできないようです。
一部の人々の不満を引き起こし、さらには Windows と macOS の間で論争を引き起こしました。
そこで、Unix-like システムの視点から、フロントエンドツールチェーン(Node.js)における Windows の「落とし穴」について話しましょう。
パスの悩み#
奇妙なバックスラッシュ#
Windows では、一般的なパスは D:\path\to\file.txt
のように見えます。一方、Unix-like システムでは /home/username/file.txt
となります。
明らかに、Windows はバックスラッシュ \
をパスの階層間の区切り文字として使用しています。そして、ほとんどのプログラミング言語では、スラッシュは特別な処理を必要とせず、バックスラッシュはエスケープ文字として使用されます。
例えば:
console.log('\\') // 実際には1つのバックスラッシュを出力
console.log('\n') // 改行文字を出力
console.log('/') // 予想通り、1つのスラッシュを出力
これは、Unix-like システムで実行することだけを考慮すれば、パスの特別な処理を基本的に無視できることを意味します。
:
区切り文字#
さらに、Windows にはドライブレターの概念がありますが、Linux ではマウント(mount)を使用して異なる物理ハードディスクやパーティションを管理します。これも互換性を考慮する必要がある場所です。
🌰 例を挙げると#
何かを言うよりも、例を挙げる方が適切です。GitHub で私が Windows に苦しめられた記録を検索できます。
test: try fix path for windows#
このコミットは、Windows 上でのユニットテストの互換性の問題を解決することを目的としています。entry
オプションにglob 式を渡す必要があります。Unix-like システムでは、ファイルのパス /path/to/file
を直接渡して正確なファイルにマッチさせることができ、これは素晴らしいです。
しかし、glob は UNIX に由来する機能であり、Windows ではバックスラッシュを使用してもfast-globでは Unix-like システムのように正常に動作しません。これが一貫性の問題を引き起こします。
fast-glob
は Windows のバックスラッシュをエスケープするためのconvertPathToPattern
関数を提供していますが、いくつかのケースでは解決できていません。
このコミットでさらに厄介なのは、コード内で 4 つのバックスラッシュを使用していることです。これは何かというと、このコードがバッククォート `
とシングルクォート '
で二重に包まれているためです。したがって、4 つのバックスラッシュは最終的に 1 つの実際のバックスラッシュとして表されます。
fix: watch ignored
option for windows#
これも一般的な例です。正規表現を使用して、特定のパスがnode_modules
フォルダ内にあるかどうかをチェックします。Windows と互換性を持たせるために、/[\\/]node_modules[\\/]/
と書く必要があります。同様に、ここでの二重バックスラッシュは最終的に 1 つのバックスラッシュにマッチします。
しかし、より厄介なのは、サードパーティの依存関係に対して、Windows ではバックスラッシュのパスが渡されるのか、スラッシュのパスに自動的に置き換えられるのかを確定できないことです。
この例では、chokidar
の処理ルールは非常に奇妙で、関数を渡すとスラッシュのパスを渡し、静的な配列では特別に処理されます。これがこのコミットの理由です。
fix: support Node 22/23 strip types feature#
先ほど言及した:
区切り文字を覚えていますか?以前は互換性の問題がないと思っていました。このように考えるのはあまりにも無邪気でした!
Node.js で絶対パスをインポートする場合、Unix-like システムでは非常に簡単です。
require('/path/to/test.cjs')
特に問題はありません。しかし、Windows では大きな問題が発生します!
require('C:\\path\\test.cjs') // ✅
import('C:\\path\\test.cjs') // ❌
// Error [ERR_UNSUPPORTED_ESM_URL_SCHEME]: Only URLs with a scheme in: file, data, and node are supported by the default ESM loader. On Windows, absolute paths must be valid file:// URLs. Received protocol 'c:'
これは、require
API がパス (path) を引数として受け入れるようですが、import()
はパスだけでなく、File URL もサポートしているためです。したがって、Node.js はC:
を URL として扱い、そのprotocol
はc:
となります。これは本質的にhttp://path/test.cjs
と大差ありません。File URL とパスは混同されがちです 🤷
import('file://C:/path/test.cjs') // ✅
console.log(new URL('C:\\path\\test.cjs')) // protocol = 'c:'
import 文#
import mod from 'a/b/n'
import mod from 'a\b\n'
ESM では、区切り文字としてスラッシュを使用する必要があります。バックスラッシュを直接使用すると、予期しないエスケープが発生する可能性があります。コード生成関連のライブラリでは特に注意が必要です。
🤨 どうすればいい?#
では、どう解決すればよいのでしょうか?patheを使用するのは良い選択かもしれません!Windows 環境でも、パスをスラッシュに変換してくれます。
また、スラッシュは Windows でもバックスラッシュとして扱われます。したがって、ほとんどのケースで一貫性の問題を直接解決できます。しかし、エッジケースはまだたくさんあります。
オープンソースコミュニティ#
私の知る限り、周囲でオープンソースプロジェクトを開発し、貢献しているほとんどの人は Unix-like システムを使用しています。一方、ユーザーの大部分は Windows を使用している可能性があります。これは市場シェアに関係しているかもしれませんが、著者やメンテナにとっても無力感を感じることがあります。
私の例を挙げると、私のオープンソースプロジェクトは GitHub Actions を利用して、Windows 上でユニットテストを実行します。コードを push するたびに、GitHub Action からエラー通知を受け取り、また Windows で実行できないことが分かると非常に苦痛です。手元に Windows の開発テスト環境が整った PC がないため、仕方なく仮想マシンで Windows 上のバグをデバッグしています。
さらに、ユニットテストはすべての側面をカバーできるわけではなく、必ず抜けが出てしまいます。これには実際のユーザーが問題を発見し、issue を提起する必要があります!
互換性問題一覧#
- パス
- バックスラッシュ
- エスケープが必要な場合がある
- スラッシュに変換する必要がある場合がある
- ドライブレター
:
- File URL に変換する必要がある場合がある
- バックスラッシュ
KEY=VALUE command
で環境変数を設定できない- pnpm の
shell-emulator
を使用 cross-env
を使用可能
- pnpm の
rm -fr
コマンドがないfs.rmSync(path, { recursive: true, force: true })
を使用rimraf
を使用
このような状況は数え切れないほどあります。私もすべてのケースを列挙することはできませんが、これらは私が遭遇したいくつかの落とし穴です。
後記#
あなたが 10 のパスの問題に直面したからといって、それが 10 個だけであるとは限りません。もっと多くの問題が待ち受けているかもしれません 🛣️……「パスすら処理できない」という言葉は受け入れられません。
もし気づいているなら、本記事全体でUnix-like
という表現を使用しています。これは、世界には Windows 以外にも多くの他のオペレーティングシステムが存在するからです。
この記事とツイートは、Windows と Linux、macOS の「論争」を引き起こす意図はありません。Windows は多くの面で重要な役割を果たしています(例えば、ゲームをするために)。しかし、事実を述べると、クロスプラットフォームの分野では確かに多くの不便を引き起こしています。
私のライブラリ#
私自身のオープンソースライブラリについては、Windows との互換性を保つために努力し、CI に Windows を追加します。
しかし、エッジケースに関しては無視される可能性があります。私のライブラリを使用している際に問題が発生した場合は、issue や PR を提起していただければ、できる限り解決に努めます。
代替品#
貴重な時間を大切にするために、Unix-like システムでフロントエンド開発を行うことをお勧めします(WSL やサードパーティの仮想マシンを含む)。もちろん、Windows を使い続けることもできますが、互換性の問題に直面した際には、あまり文句を言わず、追加の時間をかけて問題をオープンソースコミュニティにフィードバックし、貢献してください。
つぶやき#
もしこの記事を読み終えた後に、これが「傲慢」または「無知」と思われるなら、どうぞご自由に。