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フックとかに仕込んでおこう。