はきくま!制作日記:Cloudflare 編
- 投稿日
Astro で生成した静的サイトを Cloudflare Workers で公開するまでに、つまずいたところ。
microCMS ──(API)──▶ ビルド時に取得 ──▶ 静的HTML(dist/) ──▶ Cloudflare Workers
その1:Cloudflare のビルドだけが Invalid media query で落ちる
ローカルの npm run build は通るのに、Cloudflare のビルドだけが落ちる。
[lightningcss minify] Invalid media query
Location: undefined:8:17
Vite は CSS の圧縮に lightningcss を使っていて、生成された CSS の構文が不正だと落ちる。疑って潰していった順番:
package-lock.jsonがコミットされていて、依存のバージョンは固定されているか- Node のバージョン差。ビルド用変数に
NODE_VERSIONを設定。パッチまで指定(22.2.2)するとビルド環境にない版でインストールに失敗するので、メジャーだけ(22)にする vite.build.cssMinifyをesbuildに切り替え- メディアクエリをすべて外したブランチを push
どれも効かず、最終的に同じ構成で動いている別プロジェクトの astro.config.mjs をそのまま移植して通った。原因の一行は特定しないまま。後日、日を置いてリトライしただけでエラーが消えた時もあり、Cloudflare 側の環境やビルドキャッシュの影響もありそう。
学び
- ローカル(macOS)と Cloudflare(Linux)で結果が違う時は、依存の固定・Node のバージョン・設定ファイルの差の順に疑う
- 動いている実物があるなら、設定ファイルごと比べるのが一番早い
その2:astro add cloudflare と wrangler.jsonc の名前
デプロイで Missing file or directory: public/.assetsignore。Cloudflare 側の wrangler deploy が、毎回「初回セットアップ」だと判断してウィザードを走らせていた。
このサイトは、CSS をユーティリティクラス中心で管理したくて作った、自作の Astro テンプレートから始めている。
astro add cloudflare は、wrangler.jsonc の name に package.json の name を書き込む。テンプレートから作ったプロジェクトだと、そこがテンプレートの名前のままで、Cloudflare 側の Worker 名と食い違う。
{
"name": "hakikuma-site", // Cloudflare 側の Worker 名と一致させる
"main": "@astrojs/cloudflare/entrypoints/server",
"assets": { "directory": "./dist", "binding": "ASSETS" }
}
テンプレートから新しく作るたびに直すのを忘れないよう、package.json と wrangler.jsonc の name を一括で書き換える scripts/setup.mjs を用意した。詳細は別途記事を作成する。
その3:Workers に環境変数が入らない
ブランチのビルドで microCMS のクライアントが parameter is required (check serviceDomain and apiKey)。ダッシュボードで変数を足そうとすると、
Variables cannot be added to a Worker that only has static assets.
いったんは Pages に作り直す方向で進めたが、よく考えると microCMS のキーが必要なのはビルドの時だけ。公開後の Worker は静的ファイルを配るだけで、キーは使わない。
Workers の設定画面には変数の欄が二つある。
- Runtime variables and secrets:公開後の Worker が動く時の変数。静的アセットのみの Worker には入れられない
- Builds → Variables and secrets:ビルドの時の変数
後者に MICROCMS_SERVICE_DOMAIN と MICROCMS_API_KEY(Secret)を入れたら、そのまま通った。Pages への移行は取りやめ。
学び
- 静的サイト生成なら、外部APIのキーはビルド用の変数に置く
- キーを変えた時は、ローカルの
.envと Builds の変数の両方を直す
その4:ブランチごとのプレビュー
Builds の「Builds for non-production branches」を有効にしておくと、main 以外のブランチを push した時もビルドが走り、ブランチごとのプレビューURLが発行される。
- 本番に影響を出さずに、原因の切り分け用のブランチ(メディアクエリを全部外した版など)を Cloudflare 上で試せる
- クラウド上で作業してもらったブランチも、pull せずにスマホでプレビューを確認できる
その5:更新のたびの再ビルド(予定)
ページは getStaticPaths でビルド時に静的生成しているので、読者がページを開いても microCMS の API は叩かれない。そのぶん、microCMS で記事を更新したら再ビルドが必要になる。
- Cloudflare の Builds → Deploy Hooks でビルド開始用のURLを発行
- microCMS の各 API の Webhook に登録(公開・公開終了・削除のタイミングで発火)
- 作品・話・カップリングの全 API に設定しないと、どれかを直した時に反映されない
- フックのURLは知っていれば誰でもビルドを走らせられるので、外に出さない
Cloudflare 編の学び
- 静的サイト生成では、「ビルド時に必要なもの」と「公開後に必要なもの」を分けて考える
- ローカルと本番で結果が違う時は、依存の固定・ランタイムのバージョン・設定ファイルの差から疑う
- 原因の一行を突き止めるより、動く状態に戻すことを優先していい
追記
その6:Invalid media query の再発(@media (hover: hover))
ホームの小説カードに「ホバーでサムネイルが背景に出る」処理を足したところ、その1と同じエラーで Cloudflare のビルドが落ちた。
[vite] ✗ Build failed in 814ms
[lightningcss minify] Invalid media query
Location: undefined:8:17
- 落ちたのは「Building static entrypoints」の段階。CSS の圧縮(
finalizeCss)で止まっていて、場所はファイル名なしのundefined:8:17だけ - ローカルでは出なかった。開発サーバー(
npm run dev)は CSS を圧縮しないので、これだけでは気づけない grep -rn "@media" srcで直書きの@mediaを探すと、_mixin.scssのm.mqの中と、追加したindex.astroの1か所だけだった- 追加した
@media (hover: hover) { &:hover::before { … } }のブロックを消したら、ビルドが通った
原因の一行は特定できていない。構文は正しいので、見えない文字(全角スペースや NBSP)が混ざっていた可能性を疑ったが、確認はしていない。その1と同じ症状だが、同じ原因かどうかも分からない。
現状は @media なしで、:hover だけで動かしている。PC では問題ない。スマホ(ホバーのない端末)で、タップ後にホバー状態が残る可能性がある。
学び
npm run devが通っても、圧縮を通すビルドで落ちる CSS がある。push する前にnpm run buildも通す- 場所の出ない圧縮エラーは、新しく足したブロックから外して切り分ける。ブランチごとのプレビュー(その4)があるので、本番に影響なく試せる
- 直書きの
@mediaは少ないので、grep -rn "@media" srcですぐ洗える
その7:shiki を入れた時の注意
日記のコードブロックの色付けに shiki を使った。
@astrojs/cloudflareを使っていると、shiki標準の WASM エンジンが動かない、または不安定になることがある。JavaScript 版のエンジン(shiki/engine/javascript)にしてある- JavaScript 版のエンジンにした状態で、Cloudflare 上のビルドも表示も問題なく通った
- 日記のページは
getStaticPathsでビルド時に静的生成しているので、shikiはビルド時だけ動く