unpkg.com が指定された UMD を無視して /index.js にリダイレクトしてしまう


# TL;DR

- 既知のバグでした
https://github.com/unpkg/unpkg.com/issues/145
- UMD のパスを調べて ~.js まで書くか, 自分のパッケージであれば `index.js` という名前のファイルをルートディレクトリに追加して新しいバージョンを publish することを検討しましょう

# unpkg.com?

https://unpkg.com

- オープンソースで開発されている JavaScript 開発者のためのサービス
- npm publish されている全てのパッケージを `<script src="https://unpkg.com/hogehoge"></script>` と書くだけで自分のウェブサイトに追加できる
- babel や react なんかの公式ドキュメントなんかにもよく登場する, おなじみの CDN
- unpkg.com は Cloudflare と Heroku で運用されている
- Cloudflare と Heroku がスポンサードしているので, 利用料はおろか, 登録すら必要ない

最高にクールですね…✨

- - -
※ ここから備忘録


# 何が起こったのか

ある日, 会社で運用していた Web サービスのユーザーから「動かない」という連絡がきました

しかし, こちらの手元の PC では原因を再現できません. ユーザーの画面に出ているエラーから察するに, https://unpkg.com/@hackforplay/common@^0.8 (自分で公開しているパッケージ) が 404 Not Found になっているらしいのですが, 手元の PC ではちゃんと javascript コードが取得できているのです

どうして…？

## How unpkg works?

unpkg は取得するパッケージのバージョンを semver 形式で指定できるようになっています. どのように動作するのかと言うと,

https://unpkg.com/@hackforplay/common@^0.8 にアクセスした場合:

- /@hackforplay/common@0.8.10 (その時点での latest) にリダイレクト
	- /@hackforplay/common@0.8.10/dist/main.min.js にリダイレクト
		- dist/main.min.js (実在するJSファイル)を配信

と, ２回のリダイレクトを経て, ようやく実在するJSファイルを配信します

[![Image from Gyazo](https://i.gyazo.com/e4ad5b308afa57d7b090d4447d9ba1b7.png)](https://gyazo.com/e4ad5b308afa57d7b090d4447d9ba1b7)
↑僕の手元で `/@hackforplay/common@0.8.10` から `/@hackforplay/common@0.8.10/dist/main.min.js` にリダイレクトされる様子

## 404 Not Found の要因

この時, 僕がなんでー！と連ツイしているのに反応してくれた優しい御仁が「こちらで試したら /@hackforplay/common@0.8.10/ **index.js** にリダイレクトされます」とリプして下さり, どうやら２回目のリダイレクトに問題があるらしいぞ！ということが分かります

index.js なんてファイルは存在しないので, 404 になって当然です. しかしなぜ僕の手元では再現できないのか…

## DNS キャッシュをクリア

さらになんでー！とツイートしていると, 「DNS キャッシュクリアしてみては？」という助言をいただきました. (優しいインターネットだ…)

恥ずかしながらこの時まで DNS がローカルにキャッシュされていることを知らなかったので, ググった知識で試してみます:

- Chrome: chrome://net-internals/#dns
- macOS: https://support.apple.com/ja-jp/HT202516

どちらも試してみたが, 残念ながら結果は変わらず. Safari, Chrome, cURL, Advanced REST Client, どれで試しても結果は同じでした

(やり方が悪かっただけかも知れません)

## Heroku で curl

御仁の発言にヒントを得て, Heroku のインスタンス上で試してみることに. これならキャッシュとか関係ないはずです

> $ heroku run bash


> ~ $ curl -I 'https://unpkg.com/@hackforplay/common@0.8.10'
HTTP/1.1 302 Found
Date: Tue, 18 Dec 2018 08:12:40 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 58
Connection: keep-alive
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=31536000, immutable
Location: /@hackforplay/common@0.8.10/index.js
Vary: Accept
Via: 1.1 vegur
CF-Cache-Status: HIT
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
CF-RAY: 48b02a70cc572426-IAD


**/index.js になってる!!**

## Issued?

とりあえず unpkg の問題であることは確認できたので, https://github.com/unpkg/unpkg.com に Issue が立ってないか見に行きます

https://github.com/unpkg/unpkg.com/issues/145

立ってました

現時点ではまだ原因特定されていないそうです. この報告者によると, whatwg-fetch@2.0.4 で同様の問題が起きているらしいです
https://unpkg.com/whatwg-fetch@2.0.4

これヤバそう…


さて, これ以上追うのは流石に大変なので, スレを Subscribe してそっとブラウザを閉じます

ｶﾞﾝﾊﾞｯﾃ…!!

## 対処法

404 になってるのは /index.js にリダイレクトされたあとです

じゃあ index.js があればいいのでは？ という訳で, fallback 用の index.js を追加して再度パッケージを公開します


> cp dist/main.min.js index.js 
> echo /index.js >> .gitignore
> np patch --any-branch # package.json の version を 0.8.11 にして npm publish


※ この問題が起きるのは特定のバージョンだけらしいので, 0.9 以降のことは考えません

## 結果

[![Image from Gyazo](https://i.gyazo.com/7541bee5f922ad33e90d53e4c27c0e0e.png)](https://gyazo.com/7541bee5f922ad33e90d53e4c27c0e0e)


ｴｯ

と思ったけど, しばらくするとちゃんと取得できました

[![Image from Gyazo](https://i.gyazo.com/67fb1b1f986e2da5607773973d749ff3.png)](https://gyazo.com/67fb1b1f986e2da5607773973d749ff3)

アクセスが集中してたんですかね


Heroku 上でも

> ~ $ curl -I 'https://unpkg.com/@hackforplay/common@^0.8'
HTTP/1.1 302 Found
Date: Tue, 18 Dec 2018 08:34:52 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 49
Connection: keep-alive
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=60
Location: /@hackforplay/common@0.8.11
Vary: Accept
Via: 1.1 vegur
CF-Cache-Status: MISS
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
CF-RAY: 48b04af24e1dc1ef-IAD

> ~ $ curl -I 'https://unpkg.com/@hackforplay/common@0.8.11'
HTTP/1.1 302 Found
Date: Tue, 18 Dec 2018 08:35:07 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 58
Connection: keep-alive
Access-Control-Allow-Origin: *
Cache-Control: public, max-age=31536000, immutable
Location: /@hackforplay/common@0.8.11/index.js
Vary: Accept
Via: 1.1 vegur
CF-Cache-Status: HIT
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
X-Content-Type-Options: nosniff
Expect-CT: max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
Server: cloudflare
CF-RAY: 48b04b55e8eb9fd2-IAD


🎉🎉🎉


# まとめ

- 同じバグを踏んだ方は, 次の方法を試されると良いかと思います
	
	- 自分のパッケージ ＆ URL を書き換えられる: index.js をリポジトリに追加するか, ファイルパスまで URL に書く
	- 自分のパッケージ ＆ URL を書き換えられない: index.js をリポジトリに追加する
	- 他人のパッケージ ＆ URL を書き換えられる: ファイルパスまで URL に書く
	- 他人のパッケージ ＆ URL を書き換えられない: unpkg に Let's contribute! ✨
	
- 問題が再現できない場合は物理的に異なるデバイスを使うと良いかも知れません
	- こういう時にクラウドを使うのは楽チンでいいですね ☺️
- unpkg.com が信用できない人はセルフホスティングしましょう 
	- [Dockerfile が公開されています](https://github.com/unpkg/unpkg.com)🐳