Next.js で next build した時、出力先フォルダがクリーンされない件


2021/06/26 追記: Next.js 11 からビルド時 distDir がクリーンされるようになりました。以下のビルド前にデータを消すが必要なのは Next.js 10 以下の場合です。


Next.js では設定をしない状態だとビルドの度に新しいビルド ID が割り当てられ、データフォルダにビルド ID 名のフォルダが作成されます。ところが、ビルド時に出力先がクリーンされません。そのため過去のビルド結果が積み重なり、出力先フォルダが肥大化してしまいます。

Next.js の GitHub の Discuttions に「ビルド時に .next フォルダをクリーンしてほしい」というリクエストが上がっていますが、Next.js 10 (記事日付現在) ではクリーンはされないようです。

[RFC] cleaning distDir on next build · Discussion #6009 · vercel/next.js

フォルダ構成

next export したフォルダは以下のような構成になっていました。

out/
  ├ _next/
  │   ├ ビルド_ID_1/
  │   ├ ビルド_ID_2/
  │   ├ ビルド_ID_3/
  │   ├ ...
  │   ├ data/
  │   │   ├ ビルド_ID_1/
  │   │   ├ ビルド_ID_2/
  │   │   ├ ビルド_ID_3/
  │   │   ├ ...
  │   └ static/
  │        ├ ビルド_ID_1/
  │        ├ ビルド_ID_2/
  │        ├ ビルド_ID_3/
  │        ├ ...
  │        ├ chunks
  │        ├ css
  │        └...
  ├ index.html
  ├ 各ページの html
  ├ ...(/public のファイル)

ビルドした回数だけビルド ID (ランダムな文字列) のフォルダができてしまいます。

対処法

以下 2 つの組み合わせでなんとかなりました。

  1. ビルド前にデータを消す
  2. 任意の Build ID を指定する

ビルド前にデータを消す

  • ルートフォルダにある .next フォルダ
  • next export で作成されるエクスポート先のフォルダ

これらはビルド時に作られるので完全に削除しても問題はありませんでした。
エクスポート先のフォルダが /out の場合は以下のようなコマンドで消せます。

rm -rf .next out

などして削除してから再度 next build && next export すればビルド時点のデータだけになりました。

かといって毎回コマンドを打つのも大変なので、 rimraf (OS に依らず rm -rf を実行してくれるパッケージ) をインストールして npm script に書いてしまうのがいいかもしれません。

私の package.json には以下のような scripts を書いています。

...
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "export": "next build && next export",
    "clean": "rimraf .next out",
    ...
  },
...

過去のデータをビルド結果に含ませたくないときは npm run clean を実行してから npm run export します。

毎回必ず削除してからビルドするなら export などにまとめしまってもいいかもしれません。

  "scripts": {
    ...
    "export": "rimraf .next out && next build && next export"
    ...
  }

任意の Build ID を指定する

next.config.jsgenerateBuildId を用意することで任意の Build ID を割り当てることができます。

next.config.js: Configuring the Build ID | Next.js

私のケースでは GitHub のリリースに応じて Build ID を変えていきたかったので、以下のような感じで package.json の version を参照して設定するようにしてみました。

module.exports = {
  ...
  generateBuildId: async () => {
    // package.json の version を取得する
    const version = process.env.npm_package_version;
    console.log('Use package version as Build ID: ', version);
    // ピリオドをハイフンに置換したものを Build ID にする
    return version.replace(/\./g, '-');
  },
  ...
}

これでバージョン 1.0.0 なら 1-0-0 フォルダにビルド結果が入るようになりました。
バージョンアップが続いて過去のデータが溜まってきたら ビルド前にデータを消す 方法で消す感じの運用にしています。

あとがき

クリーンしないことで何かメリットがあるのかもしれませんが、私の使用状況では見当たりませんでした。 逆にクリーンすることで生じるデメリットがあるのかもと悩みましたがそちらも特に思い当たるところはなし。 転送量の制限が厳しいサーバーや記憶域が小さいサーバーでホスティングする時に困りそうな気がするので手動でも削除したほうがよいかなと思いました。

unimoku

Web サイトの制作、運営とアプリケーションのフロントエンド開発などをやっています。主に使うのはTypeScript、JavaScript、PHP、C/C++。特にTypeScriptが好きです。