仮説: case-sensitiveなファイルシステム (例: ext4) でのng serve時にファイル名が大文字小文字違いのファイルが同居していると小文字のファイルが404 Not Foundになる?
Page content
謎解き orz
Node.js初心者・Angular初心者でありつつAngularアプリの動作確認を行う必要があったとき。ng serve
するとExpressというウェブサーバがローカル起動されますが、このサーバのファイル名の大文字小文字の扱いに関して、いわゆる“おま環”と思われる現象に遭遇しました。この現象は他の人の環境 (macOS環境) では再現できず、私のメイン環境 (Ubuntu環境) では再現できてしまう。また、私の別環境 (macOS環境) では再現できない。ウェブ検索しても原因がわからなかったのですが、どうやら実行環境の「ファイルシステム」の仕様に関係するような気がして頭を捻った結果、再現手順が作れましたので、下記に掲載します。
具体的にはこれは、記事タイトルに挙げている「case-sensitiveなファイルシステム (例: ext4) でのng serve時にファイル名が大文字小文字違いのファイルが同居していると小文字のファイルが404 Not Foundになる?」という仮説を検証する問題になります。再現手順で用いるAngularアプリとしてはng serve
が試せればおそらくなんでも良いわけで、Angular公式リポジトリ https://github.com/angular/examples にある「walk-my-dog」を用いています。
- GitHub: angular/examples
本記事に辿り着いた方のなにかの参考になりましたら幸いです。
Case-1. Ubuntuのext4領域での再現手順
- ext4: デフォルトではファイル名の大文字小文字を区別するcase-sensitiveなファイルシステム
Step-1. Terminal-A: 環境確認・ext4領域に移動
$ npm version | grep -E 'npm|node'
npm: '8.19.2',
node: '16.17.1',
v8: '9.4.146.26-node.22',
$ cd <ext4領域の適当なディレクトリ>
$ df -Th .
Filesystem Type Size Used Avail Use% Mounted on
/dev/nvme0n1p2 ext4 457G 322G 112G 75% /
Step-2. Terminal-A: ソース取得してcase-sensitiveなファイル作成してserve
$ git clone https://github.com/angular/examples
$ cd ./examples/walk-my-dog/
$ ls -al ./src/assets/dog-walker-logo.*
-rw-r--r-- 1 mah mah 2976 3月 25 23:03 ./src/assets/dog-walker-logo.svg
$ cp -ip ./src/assets/dog-walker-logo.{svg,SVG}
$ ls -al ./src/assets/dog-walker-logo.*
-rw-rw-r-- 1 mah mah 2976 3月 25 23:21 ./src/assets/dog-walker-logo.SVG
-rw-rw-r-- 1 mah mah 2976 3月 25 23:21 ./src/assets/dog-walker-logo.svg
$ npm install
$ ng serve
Step-3. Terminal-B: Case-SensitiveなファイルのHTTPステータスを確認
小文字のファイルが「404 Not Found」?! 存在するのになぜ??
$ curl -I http://localhost:4200/assets/dog-walker-logo.svg
HTTP/1.1 404 Not Found
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Security-Policy: default-src 'none'
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8
Content-Length: 166
Date: Mon, 25 Mar 2024 14:27:49 GMT
Connection: keep-alive
Keep-Alive: timeout=5
大文字のファイルは「200 OK」
$ curl -I http://localhost:4200/assets/dog-walker-logo.SVG
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: image/svg+xml
Accept-Ranges: bytes
Content-Length: 2976
ETag: W/"ba0-06SevQUxlDl5sFFmZNn8+H9NSZE"
Date: Mon, 25 Mar 2024 14:28:14 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Step-4. Terminal-A: 大文字のファイルを削除して再serve
(Ctrl+C でng serveを停止する)
$ rm ./src/assets/dog-walker-logo.SVG
$ ls -al ./src/assets/dog-walker-logo.*
-rw-rw-r-- 1 mah mah 2976 3月 25 23:21 ./src/assets/dog-walker-logo.svg
$ ng serve
Step-5. Terminal-B: HTTPステータスを再確認
小文字のファイルが「200 OK」に変わっている
$ curl -I http://localhost:4200/assets/dog-walker-logo.svg
HTTP/1.1 200 OK
X-Powered-By: Express
Access-Control-Allow-Origin: *
Content-Type: image/svg+xml
Accept-Ranges: bytes
Content-Length: 2976
ETag: W/"ba0-06SevQUxlDl5sFFmZNn8+H9NSZE"
Date: Mon, 25 Mar 2024 14:30:30 GMT
Connection: keep-alive
Keep-Alive: timeout=5
謎です。まるで「ファイル名小文字のファイルへのgetリクエストに、ファイル名大文字のファイルの存在が影響する (ファイル名小文字のファイルへのgetを邪魔する)」かのよう。どうしてこのような挙動をするのだろう?
Case-2. Ubuntuのntfs3領域では
- ntfs3 (NTFS): ファイル名の大文字小文字を区別するcase-sensitiveなファイルシステム
$ df -Th .
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda1 ntfs3 233G 72M 233G 1% /media/mah/TEST_NTFS
Ubuntuのext4領域での再現手順と同じ結果になる。
参考) 上記再現手順が実施できなかった環境
MacのAPFS領域 (not 「大文字/小文字を区別」)
- Apple File System (APFS): デフォルトではファイル名の大文字小文字を区別しないcase-insensitiveなファイルシステム
$ npm version | grep -E 'npm|node'
npm: '10.2.3',
node: '20.10.0',
v8: '11.3.244.8-node.25',
$ cd ~/tmp/
$ diskutil info / | grep 'File System'
File System Personality: APFS
cp
時にファイル名の大文字小文字違いが同一視されるためエラーになる$ cp -ip ./src/assets/dog-walker-logo.{svg,SVG} cp: ./src/assets/dog-walker-logo.SVG and ./src/assets/dog-walker-logo.svg are identical (not copied).
- なお、この
cp
をpassして進めた場合、npm install
,ng serve
はノーマルな操作であって特に問題ない
Ubuntuのvfat領域
- vfat (FAT): ファイル名の大文字小文字を区別しないcase-insensitiveなファイルシステム
$ df -Th .
Filesystem Type Size Used Avail Use% Mounted on
/dev/sda1 vfat 233G 28M 233G 1% /media/mah/TEST_FAT
cp
時にファイル名の大文字小文字違いが同一視されるためエラーになる$ cp -ip ./src/assets/dog-walker-logo.{svg,SVG} cp: './src/assets/dog-walker-logo.svg' と './src/assets/dog-walker-logo.SVG' は同じファイルです
- なお、この
cp
をpassして進めると、npm install
でsymlinkが使用できない旨のエラーが発生してng serve
まで進めない$ npm install (途中省略) npm ERR! code EPERM npm ERR! syscall symlink npm ERR! path ../@angular/cli/bin/ng.js npm ERR! dest /media/mah/TEST_FAT/examples/walk-my-dog/node_modules/.bin/ng npm ERR! errno -1 npm ERR! Error: EPERM: operation not permitted, symlink '../@angular/cli/bin/ng.js' -> '/media/mah/TEST_FAT/examples/walk-my-dog/node_modules/.bin/ng' npm ERR! [Error: EPERM: operation not permitted, symlink '../@angular/cli/bin/ng.js' -> '/media/mah/TEST_FAT/examples/walk-my-dog/node_modules/.bin/ng'] { npm ERR! errno: -1, npm ERR! code: 'EPERM', npm ERR! syscall: 'symlink', npm ERR! path: '../@angular/cli/bin/ng.js', npm ERR! dest: '/media/mah/TEST_FAT/examples/walk-my-dog/node_modules/.bin/ng' npm ERR! } npm ERR! npm ERR! The operation was rejected by your operating system. npm ERR! It is likely you do not have the permissions to access this file as the current user npm ERR! npm ERR! If you believe this might be a permissions issue, please double-check the npm ERR! permissions of the file and its containing directories, or try running npm ERR! the command again as root/Administrator.