React Routerのvite+wrangler v4化作業メモ ~環境分けでturborepoに注意しろ~

RR7で、Cloudflare Viteプラグイン対応と、Wrangler v4対応を一気に行う。
Wrangler v4
ランタイム型が書き出されるようになった
React Router v7 (旧Remix)をCloudflare Workersで動かす場合、wrangler typesで worker-configuration.d.ts
を生成する。
昔はこういう内容だったが、wrangler CLIをv4にしたらクソデカくなった。
// Generated by Wrangler by running `wrangler types`
interface Env {
SUPABASE_URL: string;
SUPABASE_ANON_KEY: string;
}
AFTER:
/* eslint-disable */
// Generated by Wrangler by running `wrangler types` (hash: 609019d24d79c2fdca1f4fb535a9b39c)
// Runtime types generated with workerd@1.20250416.0 2025-03-17 nodejs_compat
declare namespace Cloudflare {
interface Env {
SUPABASE_URL: string;
SUPABASE_ANON_KEY: string;
}
}
interface Env extends Cloudflare.Env {}
// Begin runtime types
/*! *****************************************************************************
Copyright (c) Cloudflare. All rights reserved.
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
// 以下、延々とNodejs互換のための型が書かれている
これコミットしていいのか??? →コミットしていいはず。CIで生成すると、.dev.varsをコピーするとかしないと、シークレットだけEnvから欠けてしまう。

なんでここにランタイム型があるんだよ
v3.66以降では、 --experimental-include-runtime
フラグを有効にすると、.wrangler/types/runtime.d.ts
にランタイム型が出力されていた。
If you are running a version of Wrangler that is greater than 3.66.0 but below 4.0.0, you will need to include the --experimental-include-runtime flag. During its experimental release, runtime types were output to a separate file (.wrangler/types/runtime.d.ts by default). If you have an older version of Wrangler, you can access runtime types through the @cloudflare/workers-types package.
v4でフラグのexperimentalが外れ、デフォルトでtrueになった。
--include-runtime (default: true)
Whether to generate runtime types based on thecompatibility_date and compatibility_flags in your config file.
wrangler types will now produce one file that contains both Env types and runtime types based on your compatibility date and flags. This is located at worker-configuration.d.ts by default.
で、React routerのテンプレートのwrangler.tomlを読んで、クソデカファイルが生成された。

v4にしたら必要な対応
compatibility dateを更新する
wranglerを更新した日にする
rr本体の更新
そもそもReact Routerが古い場合は最新のバージョンにする。
@cloudflare/workers-typesを消す
If you were previously using @cloudflare/workers-types we recommend you run uninstall (e.g. npm uninstall @cloudflare/workers-types) and run wrangler types instead. Note that @cloudflare/workers-types will continue to be published.
- "types": ["@cloudflare/workers-types", "vite/client"],
+ "types": ["vite/client"],
- "types": ["@cloudflare/workers-types", "node"],
+ "types": ["node"],
load-context.tsの修正
CloudflareEnvironment
は生成されるから不要になった。
- declare global {
- interface CloudflareEnvironment extends Env {}
- }
コンテキストはこんな感じになる。D1を使うならここにd1とか生やす。
export interface AppLoadContext {
cloudflare: {
env: Env
ctx: Omit<ExecutionContext, "props">
}
}

@cloudflare/vite-pluginを使う
ありがたいことに、React Routerを簡単に使えるプラグインが作られた。
RR7の場合、このような設定になる。バンドルの設定が必要なくなり、めちゃくちゃスッキリした。
import { cloudflare } from "@cloudflare/vite-plugin"
import { reactRouter } from "@react-router/dev/vite"
import tailwindcss from "@tailwindcss/vite"
import { defineConfig } from "vite"
import tsconfigPaths from "vite-tsconfig-paths"
export default defineConfig({
plugins: [
cloudflare({ viteEnvironment: { name: "ssr" } }),
tailwindcss(),
reactRouter(),
tsconfigPaths(),
],
})
つまり、mainの指定もtsを直接指定する形になる。
"main": "./workers/app.ts",
CIを直す必要が生じた
CIでwrangler deploy --envをしていると、エラー
You need to set the environment in your build tool, rather than via Wrangler
CLOUDFLARE_ENVでビルド時に環境を決定しろということらしい。
今までは--envが決定権を持っていたが、@cloudflare/vite-pluginを使う場合は CLOUDFLARE_ENV
が環境を決定する。

Environments機能による名前分けについて
まずViteのcloudflareプラグインでビルドした場合の挙動について。
The default Vite environment name for a Worker is always the top-level Worker name. This enables you to reference the Worker consistently in your Vite config when using multiple Cloudflare environments. See Vite Environments for more information.
vite configのenvironmentsは、常にtop-levelのワーカー名になる。
When you create an environment, Cloudflare effectively creates a new Worker with the name <top-level-name>-<environment-name>. For example, a Worker project named my-worker with an environment dev would deploy as a Worker named my-worker-dev.
ただし、実際のワーカー名は、環境を接尾辞に付けますよ とも書かれている。
試しに CLOUDFLARE_ENV=dev
でビルドすると、このようにnameが変化する。
{
"topLevelName": "foobar-frontend",
...
"name": "foobar-dev",
"main": "index.js",
...
}
よく見ると "legacy_env": true
という項目がある。
legacy_env: trueの場合
v3~v4.12.0時点では、trueがデフォルトだった。
legacy_env: falseの場合
「Service Environment」という機能がGAになれば、falseがデフォルトになるらしい。
最終的な挙動の違いは謎。env内にnameを書くことが禁止される(そもそも書く必要はないが)

環境分けする際はturborepoに注意
- name: "ビルド"
run: pnpm build
env:
# ビルド時にこの環境変数で環境を判定する
CLOUDFLARE_ENV: ${{ inputs.wrangler_env }}
- name: "Cloudflareにデプロイ"
id: deploy
uses: cloudflare/wrangler-action@v3
with:
wranglerVersion: "3.114.6"
command: deploy
apiToken: ${{ secrets.cf_api_token }}
accountId: ${{ secrets.cf_account_id }}
が、pnpmのモノレポでCLOUDFLARE_ENVをうまく渡せない。nameが変化しない現象が発生。
→ turborepoがENVをガン無視していたことが判明!!!
buildタスクの設定で"env": ["CLOUDFLARE_ENV"],
を使おう。
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": [
".env"
],
"globalEnv": [
"NODE_ENV"
],
"tasks": {
"build": {
"env": [
+ "CLOUDFLARE_ENV"
],
"dependsOn": [
"^build"
],
"inputs": [
"$TURBO_DEFAULT$",
".env*",
".dev.vars"
],
"outputs": [
"build/**",
"dist/**"
]
}
}
}