Next.js+TypeScriptにおけるmonorepoのtips
最近monorepoなるものを知ったので,Next.js+TypeScriptで構築する際の詰まった所などを書き残す.
monorepoとは
monorepoについて簡単に自分の理解を示す.
- 複数のプロジェクトを同一のレポジトリで管理できて便利
- テストやlinterなどを統合できるからメンテも楽
- 開発者全員が全体把握しやすい
などなど.
Projectの構築
基本的には以下のテンプレートをcloneすればいい.
2022/10/17 追記
TypeScript用テンプレートを作りました.
基本的なディレクトリ構成図は以下.
root+
|-blog+ // sub app
| |-public
| |-src
| |-.eslintrc.json
| |-next.config.js
| |-package.json
| |tsconfig.json
| ...
|-home+ // main app
| |-public
| |-src
| |-.eslintrc.json
| |-next.config.js
| |-package.json
| |tsconfig.json
| ...
|-.gitignore
|-.env
|-.eslintrc.json
|-README.md
|-package.json
...
特に特殊なものはない.
問題なのは.eslintrc.json
の設定.このままVercelでデプロイしようとすると,no-html-for-link-pages
のエラーを吐く.
これは,sub appの中でsub appのルートとmain appのルートをnext/linkと<a>
タグを使い分けているために,どっちかにしろよと怒られているためである.
<Link href="/"> {/* sub appのroot */}
<a>Go to Top</a>
</Link>
<a href="/">Go to Home</a> {/* main appのroot */}
これを解決するには,sub appでのaタグによるルート指定をmain appから継承すればいい.
Next.jsは専用のeslint-plugin-next
というrulesが存在するため,main appのrulesをsub appに継承し,sub appにおける/
がmain appにおける/
を指すようにする.
{
"extends": ["../home/eslintrc.json", "next/core-web-vitals"],
}
Vercelでbuildログを見ると
error - ESLint: Failed to load config "../home/eslintrc.json" to extend from. Referenced from: /vercel/path0/chat/.eslintrc.json
のエラーが出てるが,問題なく動いているのでとりあえず大丈夫っぽい.
また,main appのnext.config.js
でroutingを制御していることがわかるが,これはNext.jsやmonorepoだけに制限しているわけではない.
const { BLOG_URL } = process.env;
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
async rewrites() {
return [
{
source: '/:path*',
destination: `/:path*`,
},
{
source: '/blog',
destination: `${BLOG_URL}/blog`,
},
{
source: '/blog/:path*',
destination: `${BLOG_URL}/blog/:path*`,
},
];
},
};
module.exports = nextConfig
BLOG_URL
を全く関係ない別のアプリURLにしたり,NuxtやSvelteへのURLにしてもいい.
つまり,実質monorepoでなくても動く.これは以下のようなときに役立つかもしれない.
- 開発チームが全くの別物だけど,ドメインは同じものを使いたい.つまり,レポジトリは別々だけど同じサービスの一部として開発したい.
- monorepoだと規模が大きくてcloneがつらい.
- 他のアプリのissueやPRを表示したくない.(labelで管理すれば...)
などなど.
おまけ
NextAuth.jsはmonorepoに対してもsessionが有効なので,各アプリ内でSessionProvider
を設定していれば,main appで認証後,sub appでその認証情報を取得できます.
従って,clientId
やclientSecret
は1つ取得すれば十分であり,callbackもhttp://localhost:3000
などroot URLに設定すれば大丈夫そうです.
Discussion