🗂️

Cloudflare (npm create cloudflare) で作った React + Vite プロジェクトに @ エイリアスを導

に公開

この記事は、個人メモを下にGPT-5に作成してもらいました。
簡単に見たい方は、こちら に個人メモをそのまま貼り付けたページを作りましたので、参考程度にご覧ください。


Cloudflare の公式テンプレート (npm create cloudflare@latest -- <app> --framework=react) で作成した Vite + React プロジェクトには、@ エイリアス(@/components/... のような書き方)が最初は設定されていません。
そのため自分で設定する必要がありますが、tsconfig.base.json を分割している構成だと少しハマりやすいポイントがあります。
この記事では設定手順と、なぜ「tsconfigPaths を入れたのに解決されない」状態になるのかを整理します。


TL;DR (要約)

  • vite-tsconfig-paths を導入しても動かない場合、プラグインが参照している tsconfig.jsonpaths が存在していない可能性が高い
  • tsconfig.base.json を作った場合は tsconfigPaths({ projects: ['tsconfig.base.json'] }) のように対象を明示する
  • あるいはルートの tsconfig.json にも paths を複製するか、Vite の resolve.alias で直接書いてしまう

ゴール

src 配下のファイルを以下のように書けるようにする:

import '@/App.css'
import '@/components/Button'

環境例

  • Node: 18+
  • パッケージ生成:
    npm create cloudflare@latest -- my-react-app --framework=react
    
  • ツール: Vite + React + TypeScript + Cloudflare プラグイン

なぜデフォルトで @ エイリアスがないのか

npm create cloudflare で生成される React テンプレートは、標準的な Vite テンプレートよりも「Cloudflare 向け設定(Worker / Pages 等)」を優先しており、パスエイリアスは必須機能ではないため初期値に含まれていません。


導入ステップ

1. 依存を追加

npm install -D vite-tsconfig-paths

2. tsconfig.base.json を作成

プロジェクトルートに配置:

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@/*": ["./src/*"],
      "~/*": ["./public/*"]
    }
  },
  "include": [
    "**/*.ts",
    "**/*.tsx",
    "**/*.css"
  ]
}

ポイント:

  • baseUrl"./"(プロジェクトルート)
  • 必要最低限なら "~/*" は省略してもよい(public はルートパス /foo.png で参照できるため)

3. 既存の tsconfig.app.json / tsconfig.node.json から継承

それぞれに extends を追加:

{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    // 既存の設定 …
  }
}

(既存プロパティはそのまま残して OK)

4. vite.config.ts にプラグインを追加

import {defineConfig} from 'vite';
import react from '@vitejs/plugin-react-swc';
import {cloudflare} from "@cloudflare/vite-plugin";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [
    tsconfigPaths({
      projects: ['tsconfig.base.json'] // ← ここが重要
    }),
    react(),
    cloudflare(),
  ],
})

なぜ projects が必要?
→ デフォルトだとルートの tsconfig.json(または最初に見つかったもの)を読む。そこに paths が無いと何も alias 化されない。

5. 動作確認

// 例: src/main.tsx など
import '@/App.css'

npm run dev を再起動して、解決エラーが消えていれば成功。


よくあるハマりどころ

症状 原因 対処
直書きの @/App.css が解決されない プラグインが base の tsconfig を読んでいない projects オプションを指定
TypeScript は補完できるのに Vite がビルド失敗 TS 側はパス解決できるが Vite に alias 未設定 tsconfigPaths を plugins に追加していない / 順序が不適切
Worker 用ビルドでだけ失敗 worker 用 tsconfig が base を extends していない tsconfig.worker.json も修正
CI で突然失敗 ローカルには古い tsbuildinfo が残っていた rm -rf node_modules/.tmp などでキャッシュ削除

切り分けの最速手段

一時的に Vite へ直書きしてみる:

resolve: {
  alias: { '@': new URL('./src', import.meta.url).pathname }
}

これで動いた → vite-tsconfig-paths の設定問題
動かない → ファイルパス / 大文字小文字 / 実在確認を再チェック


代替アプローチ (比較)

方法 メリット デメリット
vite-tsconfig-paths TS と Vite の同期が簡単 マルチ tsconfig だと設定追加が必要
resolve.alias 手書き 仕組みが単純で可視性が高い TS 側にも paths を二重管理したくなる
何もしない 相対パスだけで完結 階層が深くなると可読性低下

個人的には「単一プロジェクト: resolve.alias」「複数ツール共有 or monorepo: tsconfigPaths」が分かれ目になりやすいです。


moduleResolution: "bundler" について

moduleResolution: "bundler" を使っている場合、NodeNext 系と微妙に解決挙動が違うことがあります。もし pathstsc --showConfig に出力されないなど意図しない挙動があれば、一時的に "NodeNext" に戻して差分を観察するのも手です。

確認コマンド:

npx tsc --project tsconfig.app.json --showConfig | grep '"paths"'

ディレクトリ例

project/
  tsconfig.json
  tsconfig.base.json
  tsconfig.app.json
  tsconfig.node.json
  vite.config.ts
  src/
    App.tsx
    App.css
    components/
      Button.tsx

App.tsx:

import '@/App.css'
import '@/components/Button'

運用ポリシー例

  • 相対パス推奨: 近接 (同階層 or 2 階層以内)
  • @/ 使用: 横断的 (components, lib, hooks, styles, config)
  • 短縮 alias を増やしすぎない (@c, @u などは後から読む人に不親切)

トラブルシューティング チェックリスト

# 1. プラグインは入っているか
grep -n "tsconfigPaths" vite.config.ts

# 2. 参照対象は正しいか
cat tsconfig.base.json | grep '"paths"'

# 3. tsc が alias を認識しているか
npx tsc --project tsconfig.app.json --showConfig | grep '"@/*"'

# 4. Vite の再起動をしたか(ホットリロードでは拾わないことあり)

まとめ

  • tsconfig.base.json を切っていると vite-tsconfig-paths は “読んでいない tsconfig” 問題にハマりやすい
  • projects オプションで明示するか、ルートに paths を複製する
  • 切り分けは resolve.alias の直書きが最速
  • 過剰な alias 追加はメンテ負荷になるので最小限から始める

付録: もっとシンプルにしたい場合

小規模なら分割をやめてルート tsconfig.json を 1 枚にまとめ、tsconfig.app.json などを削減することで混乱を避けやすくなります。


何か別の周辺(Cloudflare Workers 側の import、Vitest 設定など)も気になればコメントください。改善案も歓迎です。お疲れさまでした。

Discussion