Next.jsのビルドが落ちたので、MadgeでTypeScriptプロジェクトの循環参照を検出して予防する
目次
Next.jsのビルドが突然失敗した
$ next build
   ▲ Next.js 15.1.4
   - Environments: .env
   - Experiments (use with caution):
     · typedRoutes
 
   Creating an optimized production build ...
   Using tsconfig file: ./tsconfig.build.json
 ✓ Compiled successfully
 ✓ Linting and checking validity of types    
 ✓ Collecting page data    
Error occurred prerendering page "/hoge". Read more: https://nextjs.org/docs/messages/prerender-error
ReferenceError: Cannot access '_' before initialization調べると循環参照が原因っぽくて、エラーが起きているレンダリング対象のページにログを仕込んで試していたらやっぱりそうでした。
ただ、ビルド時にしかエラーが発生しないのと同じ状況で実行してもエラーになったりならなかったりしたのでたまたまこれまで気づいていませんでした。
Madge
循環参照を検出できるものを調べると良さげなのがありました。
pahen/madge: Create graphs from your CommonJS, AMD or ES6 module dependencies
Madge は、モジュールの依存関係の視覚的なグラフを生成し、循環依存関係を見つけ、その他の有用な情報を提供する開発者ツールです。
MadgeとDependency Cruiserの違い
以下Issueでの回答によるといろいろと目標が違う、らしい。
Dependency Cruiser vs. Madge · Issue #203 · sverweij/dependency-cruiser
あまり調べてないのでよく分からないけれど、Dependency Cruiserは生成される設定ファイルのjsonがややこしそうで拒否反応が出たのでMadgeを使います。
Madgeを使う
設定ファイルを配置
.madgercというファイル名
設定はこちらを参照。 https://github.com/pahen/madge?tab=readme-ov-file#configuration
{
  "fileExtensions": [
    "js",
    "jsx",
    "ts",
    "tsx",
    "mts",
    "mjs",
    "cjs"
  ],
  "tsConfig": "./tsconfig.json",
  "detectiveOptions": {
    "ts": {
      "skipAsyncImports": true,
      "skipTypeImports": true
    },
    "tsx": {
      "skipAsyncImports": true,
      "skipTypeImports": true
    }
  }
}実行
$ bun run madge --circular src/
Processed 29 files (382ms) (3 warnings)
 
✖ Found 1 circular dependency!
 
1) hoge/fuga.ts > hoge/piyo.ts
 
error: "madge" exited with code 1終わり
適当にコード書いているとモジュールの依存関係がぐちゃぐちゃになりがちなのでこういうもので防げると良さそうですね。
pre-pushフックとかに仕込んでおこう。