問題
特定のページでJavaScriptが実行されず、画面が崩れる現象が発生しました。興味深いことに、middleware matcherで定義された複数のパスの中で、/signup
ページでのみこの問題が現れました。
export const config = {
matcher: [
'/',
'/account',
Path.ITEMS, // ここで変数を使用!
'/blog/:path*',
'/user/:path*',
'/signin',
'/signup',
// ...
]
};
原因
Next.js公式ドキュメントの説明
Next.js公式ドキュメントによると、middleware matcherの値はビルド時に静的に分析できる必要があり、変数などの動的な値は無視されると説明されています。
最初はPath.ITEMS
をconfigの直上でconst
として宣言したため問題ないと思っていました。しかし、実際には問題が発生し、その理由を見つけるためにNext.jsのソースコードを分析しました。
Next.jsのConfig分析プロセス
以下はNext.jsの実際のコードの一部で、SSGやmiddlewareなどの設定を分析する際に次のようなプロセスを経ます:
export async function getPageStaticInfo(params: {
nextConfig: Partial<NextConfig>
}): Promise<PageStaticInfo> {
const fileContent = (await tryToReadFile(pageFilePath, !isDev)) || ''
if (/runtime|getStaticProps|getServerSideProps|matcher/.test(fileContent)) {
const swcAST = await parseModule(pageFilePath, fileContent)
const config = tryToExtractExportedConstValue(swcAST, 'config') || {}
// ...
}
}
このコードの重要なポイントは次の通りです:
- まず設定ファイルを読み込む
config
という名前の変数を探す- AST(Abstract Syntax Tree)を生成して解析する
さらに詳しく見ると、Next.jsはconst
宣言のみを許可しています:
export function extractExportedConstValue(
module: Module,
exportedName: string
): any {
for (const moduleItem of module.body) {
if (!isExportDeclaration(moduleItem)) continue;
const declaration = moduleItem.declaration
if (!isVariableDeclaration(declaration)) continue;
if (declaration.kind !== 'const') continue;
// ...
}
}
このコードはconst
で宣言されていない値は抽出されないことを示しています。
SWCのASTと静的分析
Next.jsはSWC(Speedy Web Compiler)を使用してコードを解析し、ASTを生成します。このプロセスでは、変数を使用した設定は静的分析が難しいため、ビルド時の最適化が困難になります。
静的分析が可能な正しい設定例:
// ✅ 良い例
export const config = {
matcher: [
'/',
'/account',
'/items', // 直接文字列を使用
'/blog/:path*',
]
};
残された疑問
なぜ他のページは正常に動作し、/signup
ページでのみ問題が発生したのかについては解明できませんでした。これはおそらくNext.jsのビルドプロセスやコード分割(code splitting)プロセスの特異性に関連していると推測されます。
結論
Next.js middleware matcherで変数を使用する際は注意が必要です。ビルド時の最適化と静的分析のために、直接的な文字列リテラルを使用すべきです。