Remixの世界: ビルドとデプロイ #5


:::note warn
記事データの反映がうまくいってなかったので再度公開しました。 2024/12/12
:::

## はじめに

はじめまして、WEB フロントエンドエンジニアの nuintee です。

この度かねてより関心があった Remix に入門する覚悟が出来たので、
その学習過程をアドベントカレンダーにしてみました。

Remix に少しでも興味のある方は、ぜひご覧ください。

## フロントエンドの世界 2024 について

「[フロントエンドの世界 2024](https://qiita.com/advent-calendar/2024/frontend-assort-2024)」は普段 [`Next.js`](https://nextjs.org/) を書いている筆者が、同じフロントエンドライブラリである [`Svelte(Kit)`](https://svelte.jp/docs/introduction), [`Remix`](https://remix.run/docs/en/main) ,[`SolidJS`](https://docs.solidjs.com/), [`Qwik(City)`](https://qwik.dev/)の 4 つにアソート形式で触れ、理解を深めていく様子を収めたアドベントカレンダーです。

![frontend-assort-2024-banner.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3294810/7666d209-1aef-9ce5-c298-d8e60847e830.png)

## もくじ

- [はじめに](#はじめに)
- [フロントエンドの世界 2024 について](#フロントエンドの世界-2024-について)
- [もくじ](#もくじ)
- [デプロイ先](#デプロイ先)
- [必要なパッケージの導入](#必要なパッケージの導入)
- [コードの修正](#コードの修正)
- [ローカル環境の立ち上げ確認](#ローカル環境の立ち上げ確認)
- [wrangler cli からのデプロイ](#wrangler-cli-からのデプロイ)
- [Cloudflare プロジェクトの追加](#cloudflare-プロジェクトの追加)
- [Github リポジトリとの連携](#github-リポジトリとの連携)
- [Cloudflare プロジェクト設定](#cloudflare-プロジェクト設定)
- [参考記事](#参考記事)
- [おわりに](#おわりに)

## デプロイ先

`Remix`は以下の中から選んだランタイムに対し、最適化されたビルドを行うことができます。

- [Cloudflare Pages](https://www.npmjs.com/package/@remix-run/cloudflare-pages)
- [Cloudflare Workers](https://www.npmjs.com/package/@remix-run/cloudflare-workers)
- [Express 環境](https://www.npmjs.com/package/@remix-run/express)
- [Deno 環境](https://www.npmjs.com/package/@remix-run/deno)
- [Node 環境](https://www.npmjs.com/package/@remix-run/node)

今回は元々気になっていた`Cloudflare Pages`を選択しました。

(参考: [Deployment](https://remix.run/docs/en/main/guides/deployment))

## 必要なパッケージの導入

ランタイム用に`@remix-run/cloudflare` `@remix-run/cloudflare-pages`を、型定義用に`@cloudflare/workers-types`をインストールします。

```shell
# turborepoを使用している場合 (ルートで実行)
pnpm --filter remix-client install @remix-run/cloudflare @remix-run/cloudflare-pages
```

```shell
# 通常インストール
npm install @remix-run/cloudflare @remix-run/cloudflare-pages
```

**devDependencies のインストール**

```shell
# turborepoを使用している場合 (ルートで実行)
pnpm --filter remix-client install -D wrangler @cloudflare/workers-types
```

```shell
# 通常インストール
npm install -D wrangler @cloudflare/workers-types
```

また cloudflare-pages のランタイムに変わるので node ランタイムを削除します。

```shell
# turborepoを使用している場合 (ルートで実行)
pnpm --filter remix-client uninstall @remix-run/node
```

```shell
# 通常アンインストール
npm uninstall @remix-run/node
```

## コードの修正

### tsconfig.json

型定義として`@remix-run/cloudflare`と`@cloudflare/workers-types/2023-07-01`を compilerOptions.types に追加します。

```diff_json:tsconfig.json
...
"compilerOptions": {
    "lib": ["DOM", "DOM.Iterable", "ES2022"],
    "types": [
+     "@remix-run/cloudflare",
      "vite/client",
+     "@cloudflare/workers-types/2023-07-01"
    ],
}
...
```

### vite.config.ts

`cloudflareDevProxyVitePlugin`を plugins 配下に追加します。

(参考: [Cloudflare Proxy](https://remix.run/docs/en/main/guides/vite#cloudflare-proxy))

```diff_typescript:vite.config.ts
import {
  vitePlugin as remix,
+ cloudflareDevProxyVitePlugin,
} from "@remix-run/dev";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

declare module "@remix-run/cloudflare" {
  interface Future {
    v3_singleFetch: true;
  }
}

export default defineConfig({
  plugins: [
+   cloudflareDevProxyVitePlugin(),
    remix({
      future: {
        v3_fetcherPersist: true,
        v3_relativeSplatPath: true,
        v3_throwAbortReason: true,
        v3_singleFetch: true,
        v3_lazyRouteDiscovery: true,
      },
    }),
    tsconfigPaths(),
  ],
});

```

### functions/[[path]].ts

プロジェクトルートに `functions/[[path]].ts` ファイルを追加します。
追加をすることですべての Remix リクエストが `Cloudflare Pages` 用のアダプターでハンドリングされるようになります。

(参考: [functions/[[path]].ts](https://remix.run/docs/en/main/guides/vite#:~:text=%F0%9F%91%89%20Create%20a%20catch%2Dall%20route%20for%20Remix))

```ts:functions/[[path]].ts
import { createPagesFunctionHandler } from "@remix-run/cloudflare-pages";

import * as build from "../build/server";

export const onRequest = createPagesFunctionHandler({ build });
```

### public/\_routes.json

public 配下に`_routes.json`を追加することで、functions のハンドリング対象に含めるパス、除外するパスの定義が行えます。

今回ビルド後の静的アセットはハンドリングして欲しく無いので `exclude` します。

(参考: [Create a \_routes.json file](https://developers.cloudflare.com/pages/functions/routing/#create-a-_routesjson-file))

```json:public/routes.json
{
  "version": 1,
  "include": ["/*"],
  "exclude": ["/assets/*"]
}
```

### app/entry.server.tsx

Remix プロジェクト作成時に元々含まれていたファイルですが、Cloudflare Pages ランタイムではよしなに処理してくれるので削除します。

### wrangler.toml

cloudflare のデプロイ設定を記述します。

**name**
任意のプロジェクト名を設定します。

**main**
先ほど適宜したアダプターのパスを設定します。

**assets**
静的コンテンツを Workers で配布できるように静的コンテンツのディレクトリを指定します。

**compatibility_date**
現在の日付を `YYYY-MM-dd` 形式で設定します。

```toml:wrangler.toml
name = "frontend-assort-2024-remix"
main = "./functions/[[path]].ts"
assets = { directory = "./build/client" }
compatibility_date = "2024-12-05"
```

## ローカル環境の立ち上げ確認

### 立ち上げコマンド変更

元々の remix-serve コマンドを wrangler を用いた立ち上げ小マントに変更します。

(参考: [pages dev](https://developers.cloudflare.com/workers/wrangler/commands/#dev-1))

```diff_json:package.json
{
  ...
  "scripts": {
    "build": "remix vite:build",
    "dev": "remix vite:dev",
    "lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
-   "start": "remix-serve ./build/server/index.js",
+   "start": "wrangler pages dev build/client",
    "typecheck": "tsc"
  },
  ...
}
```

### ビルド

立ち上げ前にビルドを行います。

```shell
# turborepoの場合
pnpm --filter remix-client build
```

```shell
# 通常ビルド
npm run build
```

### 立ち上げ

```shell
# turborepoを使用している場合 (ルートで実行)
pnpm --filter remix-client start
```

```shell
# 通常立ち上げ
npm run start
```

http://localhost:8788/で立ち上がれば成功です。

## wrangler cli からのデプロイ

wrangler cli から そのまま Cloudflare Pages へのデプロイも可能です。

(参考: [pages public](https://developers.cloudflare.com/workers/wrangler/commands/#publish-1))

```shell
npx wrangler pages publish build
```

## Cloudflare プロジェクトの追加

右上の「+ Add」から新しい `Pages` プロジェクトを追加します。

![cloudflare-add-pages.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3294810/2ec21aef-6f81-be0e-82bd-94ce1c62955b.png)

## Github リポジトリとの連携

デプロイアセットの連携方法を聞かれるので今回は `Connect to Git`から `Github` を選択します。

![スクリーンショット 2024-11-27 0.51.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3294810/ed3153b9-6517-f01b-7dc2-d26d10d493cc.png)

連携したい `Github` アカウントとリポジトリを選択します。

今回は本記事用に作成した `frontend-assort-2024`というリポジトリを選択し、「Begin setup」からセットアップを開始します。

![cloudflare-github.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3294810/ecfb6c05-afa9-7de6-9229-8eba921b6e8b.png)

## Cloudflare プロジェクト設定

### プロジェクト名と対象ブランチの選択

任意のプロジェクト名を入力し、自動デプロイの対象ブランチを選択します。

今回は `frontend-assort-2024`というプロジェクト名で`main`ブランチを自動デプロイ対象に選択します。

### ビルド設定

フレームワークのプリセットは`none`を選択します。

※ 通常は `Remix` のプリセットで問題ないですが、本アプリケーションは`Turborepo`によるモノレポ管理を行っているので`none`で設定します。

```:Framework preset
None
```

:::note warn
モノレポではない場合、`Remix`のフレームワークプリセット選択時の自動設定のままで基本的に問題ないです。
:::

<details>
  <summary>モノレポの場合 (省略可能)</summary>

本アプリケーションで使用している`Turborepo`と`pnpm`を用いた設定例です。

```shell:Build command
pnpm build
```

```shell:Build output directory
build/client
```

```shell:Root Directory
apps/remix-client
```

</details>

![スクリーンショット 2024-12-11 23.55.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3294810/309dd94c-098d-0b6c-0254-8202d94a289c.png)

### デプロイ

ここまでの設定を完了し"Save and Deploy"を押すとデプロイ処理が実行されます。
数分程度待ち、無事にデプロイが完了したことを確認できました。

https://frontend-assort-2024-remix.pages.dev/game

## 参考記事

https://blog.stin.ink/articles/remix-on-cloudflare-pages-tutorial

## おわりに

必要なファイルや設定が多く、正直デプロイ周りが一番詰まりました。
もっと本質的な理解を目指して慣れていこうと思います。

また本シリーズを通してお気軽にコメントお待ちしております。
また完走賞も目指しているので是非応援お願いします！

---

この記事は [フロントエンドの世界 Advent Calendar 2024](https://qiita.com/advent-calendar/2024/frontend-assort-2024)の 12 記事目です。
次の記事はこちら [SolidJS の世界: SolidJS とは？ #1](https://qiita.com/nuinteedev/private/ba0b6c49d448fdd3c0ea)