Vite + HTML + TSで複数ページをGitHub Pagesにデプロイする方法

## はじめに

今回この構成を使用するに至ったのは、ハッカソン形式のチーム開発で「Webで動作する２次元ゲーム」を作成することになったことがきっかけです。

https://github.com/takatyan5656/music-game

その際、TypeScriptを用いた環境構築と、GitHub Pagesのデプロイを実施したのですが、複数ファイルの取り込み→GitHub Pages公開までの記事がなかった（あったとしても`React`や`Vue`などのフレームワーク限定）ので、今回は記事を上げさせていただきました。

- `JS`→`TS`で書きたいが、デプロイまでの構築が分からない
- `React`や`Vue`ではない、純粋な`HTML`+`TS`での開発に慣れている

これらを実施したい人がターゲットになります！

:::note info
事前に`node`がインストールされており、`npm`コマンドが使用できる前提で話を進めていきます
:::

### Viteはtsでの効率的な開発＆buildツール

開発は型変換のあるtsファイルで実施し、Buildして最終的にjsファイルに変換し、静的コード（SSG）としてビルドします。これらを簡単に行えるのがViteというツールです。

似たようなツールとしてWebPackなどもありますが、あれは難しすぎますね。

## HTML + TS環境の構築（Vite）

### viteのセットアップ

既にセットアップしている人は飛ばして大丈夫です。`npx create-vite`から実施する方法もありますが、初期ファイルだと少し理解しにくい部分があるので、不安な方は以下のファイルをコピペしてください。

<details><summary>package.json</summary>

```json:package.json
{
  "name": "my-project-name",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "preview": "vite preview"
  },
  "devDependencies": {
    "typescript": "^5.2.2",
    "vite": "^5.3.1"
  }
}
```

</details>

<details><summary>tsconfig.json</summary>

```json:tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "isolatedModules": true,
    "moduleDetection": "force",
    "noEmit": true,

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true
  },
  "include": ["src"]
}
```

</details>

<details><summary>vite.config.ts</summary>

```ts:vite.config.ts
import { defineConfig } from "vite";

export default defineConfig({
  server: {
    open: true,
  },
});
```

</details>

<details><summary>index.html</summary>

```html:index.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + TS</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

```

</details>

- src/main.ts（こちらは空のままで大丈夫です）



```shell
npm i # パッケージのインストール
npm run dev # localhostで起動
```

これで、最低限のファイルで起動構成をセットアップすることができました。実際に、今後はindex.htmlやmain.tsをいじることで、HTML + JSと同じような感覚で開発ができるようになります。

:::note info
その他、画像やcssファイルは、publicディレクトリにいれてください。
:::


## 複数ページの実装

本記事の目標である、複数ページのデプロイです。具体的には、

```
/ （ホーム画面）
/select （選択画面）
/game （プレイ画面）
```

のように、複数のページを用意するイメージです。これがReactであれば`router-dom`を使用することでReactファイルのコードにわかりやすく記載が可能ですが、今回はシンプルにViteのみを使用するので別の書き方が必要になります。

### ファイル構成

```shell
.
├── game # /game に対応するファイル
│   └── index.html
├── public
│   ├── images
│   │   ├── android-1440x2560-wallpaper_02132.jpg
│   │   ├── bad.png
│   │   ├── good.png
│   │   ├── great.png
│   │   ├── miss.png
│   │   └── perfect.png
│   └── styles # CSS files
│       ├── select.css
│       ├── game.css
│       └── index.css
├── select # /select に対応するHTML
│   └── index.html
├── src # ts files
│   ├── game.ts
│   ├── index.ts
│   └── select.ts
├── index.html # / に対応するHTML
├── package.json
├── tsconfig.json
└── vite.config.ts
```

上記のようにするだけで、`npm run dev`で起動すると`/`, `/select`, `/game`にそれぞれアクセスできます。

:::note info
index.htmlが不格好に記載されていますが、私が試した限り、`/`以降のルーティングがそのまま配置されてしまうため、もう少しスッキリまとめられる方法ありましたらご教示いただけると嬉しいです。
:::


### HTML内のPATHは`/`, tsは`/src`以降に

```html
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>トップ画面</title>
    <!-- CSSの読み込み -->
    <link rel="stylesheet" href="/styles/index.css" />
  </head>
  <body>
    <div>
      <p>Joken音ゲー</p>
      <button class="GO">START</button>
    </div>
    <!-- JSの読み込み -->
    <script src="/src/index.ts" type="module"></script>
  </body>
</html>

```

CSSは`/public`直下にフォルダが配置されているので、ビルド時にもファイルが保存されます。今回の場合は`/`から始まったパスを入力することで反映されます。

tsファイルだけは、`/src`をつけて読み込みます。ビルド後は`/assets`フォルダにコンパイル済みのjsが保存され、HTML内のパスもきちんと変更されます。`/`から読み込んでいるので、`/select/index.html`なども同様のパスで読み込んでOKです。

```html:ビルド後のHTML
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>トップ画面</title>
    <link rel="stylesheet" href="/styles/index.css" />
    <!-- パスが書き換わってる -->
    <script type="module" crossorigin src="/assets/main-UY9zy4Kc.js"></script>
    <link rel="modulepreload" crossorigin href="/assets/modulepreload-polyfill-B5Qt9EMX.js">
  </head>
  <body>
    <div>
      <p>Joken音ゲー</p>
      <button class="GO">START</button>
    </div>
    <!-- tsの読み込みは削除されている -->
  </body>
</html>
```

## 複数ページのビルド

しかしこれだけでは、ビルド時は`index.html`しかコンパイル、ビルドされません。これを解決するには、`vite.config.ts`を編集します。

```ts
import { defineConfig } from 'vite';

export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        // ここにパス一覧を記述
        main: 'index.html',
        game: 'game/index.html',
        select: 'select/index.html',
      },
    },
  },
});

```

inputの中、左側の`main`や`game`などは好きな名前で大丈夫です。右側のパスは`/`直下からのパスを間違いなく入力してください。

これを記述するだけでビルド時に対象のHTML、TSがきちんとコンパイル、ビルドされるようになります。

## GitHub Pagesにデプロイする

次はデプロイです。GitHub Pagesにデプロイをすることで、自身でドメイン・サーバーを契約することなくWebサイトを公開できます。

手順は以下のとおりです。

1. `gh-pages`のインストール
2. `package.json`の編集
3. `vite.config.ts`の編集
4. GitHub Pagesの設定

### gh-pagesのインストール

```shell
npm i --save-dev gh-pages
```

gh-pagesは、新たに`gh-pages`というブランチを作成し、そこにbuildしたファイルを上げるまでを自動で行うツールです。GitHub Pagesは、buildした`gh-pages`ブランチを公開する流れとなります。

### package.jsonの編集

以下の2行を追加します。

```json
{
  "homepage": "https://<自身のGitHubアカウントID>.github.io/<リポジトリ名>/", ←この行を追加

  "scripts": {
    "deploy": "gh-pages -d dist" ←scriptsの中に追加
  }
}
```

実際にデプロイをする際は、

```shell
npm run build
npm run deploy
```

をすることで、`gh-pages`ブランチのファイルが最新の状態にビルドされて更新されます。

### vite.config.tsの編集

```ts
import { defineConfig } from 'vite';

export default defineConfig({
  base: '/<リポジトリ名>/', // ←この行を追加
  build: {
    rollupOptions: {
      input: {
        main: 'index.html',
        game: 'game.html',
        select: 'select.html',
      },
    },
  },
});
```

baseを追加することで、URLが`/`から`/<リポジトリ名>/`を含むようになり、GitHub Pagesのルーティングに最適化されます。`index.html`に記載の`css`, `ts`などのパスは`/`, `/src`のままで編集する必要ありません。

### GitHub Pagesの設定

`Settings` > `Pages`に移動し、Branchを指定してセーブしてみましょう。

 ![スクリーンショット 2024-09-04 7.22.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538408/395f73bc-d76c-a335-4f58-79198cb5d898.png)

設定が完了すると、`gh-pages`が更新された際にデプロイのプロセスが自動で行われ、最終的に公開されます。

![スクリーンショット 2024-09-04 7.26.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538408/fa68f4c4-de3a-14a9-3193-839e8db61d61.png)

![スクリーンショット 2024-09-04 11.44.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538408/97fc147a-44bb-684f-353a-24f789734aa3.png)

これで、GitHub Pagesのデプロイが完了しました。