White Box技術部

WEB開発のあれこれ(と何か)

.gitignore.localを無理やり実現する

先日、CLAUDE.local.mdが使えることを知り、

「え?もしかして、.gitignoreもいけたりするの?」

と思って調べたら、そんな事はできなかったのですが、なぜか諦めきれず無理やり実現しました。

.gitignore.localというファイルを、デフォルトのignoreファイルにしてしまう

調べていて知ったのですが、.gitignoreにはグローバル設定があるようです。

私の環境では~/.config/git/ignoreが該当し、1つだけ以下の記述が含まれていました。

**/.claude/settings.local.json

ignoreのグローバル設定は、core.excludesfileで指定されたファイルが該当します。 つまり、リポジトリでgit configのこれを上書いてしまえば、.gitconfigのローカル設定が実現できるわけです。

実現手順

手順は以下の通りです。

  1. 対象のリポジトリで.gitignore.localファイルを作成する
  2. git config --local core.excludesFile .gitignore.local を実行

これで.gitignoreに加えて、.gitignore.localの設定もgitの無視設定に利用されるようになります。

注意点として、グローバルのignoreを上書いているので、グローバル側の無視設定を.gitignore.localに記載することを忘れないようにしてください。

ちなみに私は.gitignore.localの先頭に以下の4行を書いておくことにしています。

# git config --local core.excludesFile .gitignore.local
.gitignore.local
settings.local.json
CLAUDE.local.md

導入してみた感想

驚くほど快適になりました。もっと早くやっておけばよかった・・・

開発が長くなると、コミットはできないけどリポジトリには置いておきたいファイルが増えていくのですが、.gitignoreには記載を入れれないこともあり、 コミット時はいつも意図しない追加が無いか確認する必要がありました。
ですが、この設定のおかげで変更は一括addできるようになったので、コミット漏れもなく、ストレスがだいぶ減りました。

これはおすすめです!

【Next.js】 SSGビルドで発生するDBコネクション問題の対応と戒め

Too many database connections opened: sorry, too many clients already

今となっては、「SSGができることがNext.jsの売り!」というイメージはない気がしますが、出た当時はまさしく売りだったSSGビルドを、 今頃使って遊んでいたところ、ビルド時にDBのコネクション数切れエラーが出るようになりました。

Error [PrismaClientKnownRequestError]:
Invalid `prisma.exhibition.findUnique()` invocation:


Too many database connections opened: sorry, too many clients already
    at async a (.next/server/chunks/_1z8uvof._.js:1:230670)
    at async o (.next/server/chunks/[root-of-the-server]__1r9zv_q._.js:1:1640) {
  code: 'P2037',
  meta: {
    modelName: 'Exhibition',
    driverAdapterError: Error [DriverAdapterError]: TooManyConnections
        at y.onError (.next/server/chunks/_1z8uvof._.js:1:224841)
        at y.performIO (.next/server/chunks/_1z8uvof._.js:1:224782)
        at async y.queryRaw (.next/server/chunks/_1z8uvof._.js:1:221368) {
      [cause]: [Object]
    }
  },
  clientVersion: '7.8.0'
}
Export encountered an error on /exhibitions/[id]/wishlist.json/route: /exhibitions/24de2a9b-26da-47bb-bec1-24211e3bc5c1/wishlist.json, exiting the build.
⨯ Next.js build worker exited with code: 1 and signal: null

これを見て、

「コネクション食いつぶして死んでるのか、まあトークンリセットされたら調査依頼するか」

と何の疑問も持たずに思った後、しばらくして

「え?自分で調査するとか、なさらないんですの????」

と内なるお嬢様に突っ込まれたので解消まで調査した記録です。

どこが問題なのか?

解消し終わった今であれば、ログを見ただけで

「/exhibitions/[id]配下でのfindUniqueでToo many database connectionsなんだから、exhibitionsのデータ量が増えたせいで、ビルド時に並列で処理しすぎて落ちたんだろうな」

とすぐ思い当りそうだと思えるのですが、実際には

  1. 「いつも使ってるPrismaのインスタンス化処理周り、今回はAIに任せてしまってたっけかな?」と思いインスタンス化処理を追ってみたり、
  2. 「NODE_ENVをproductionにしないとダメだってこと??」とか思い.env.productionを作ってみたり、
  3. 「DATABASE_URLにconnection_limitをつけろってこと???」とか思って?connection_limit=5を付けてみたりしてから、
  4. 問題が起きているコードのデータ取得処理をコメントアウトして、ようやく「あ、これbuild時に並列で動きすぎてるからか」と気付くなどしていました。

なんというか直感の衰えを実感しました。

解決策

対応としては、next.config.tsに以下の設定を入れ、ビルドの並行処理を抑制することで解消できます。

  experimental: {
    staticGenerationMaxConcurrency: 2,
  },

以下の参考ドキュメントには他のオプションを指定していたりしますが、これだけで大丈夫です。

VSCodeとpackage.jsonのTypeScriptバージョンが不一致の場合、エディタでCSS importしている箇所がts(2882)エラーになる

'@/styles/globals.css' の副作用インポートに対するモジュールまたは型宣言が見つかりません。ts(2882)

ある日、Next.jsのlayout.tsxなどでcssやscssをimportしている箇所が、VSCodeのエディタ上でエラー表示されるようになりました。

なんとなく最近TypeScriptの6がリリースされたからかなと思っていたので、Google検索したところ、型定義を追加しろとのことだったので、 src/types/styles.d.tsというファイルを作成し、以下のように記述したところ、エラーが解消しました。

declare module "*.css";
declare module "*.scss";

もともとtsconfig.jsonには以下のように書いていたので、他の変更が必要ありませんでした。

    "typeRoots": ["src/types", "./node_modules/@types", "../../node_modules/@types"],

珍しくGoogle検索のAIモードの答えがそのままだったなと思ったりしたのですが、 後日、型定義を宣言していないにもかかわらずエラーにならないプロジェクトがあることに気が付きました。

なぜ、プロジェクトによってts(2882)が発生するのか?

開発しているプロジェクトの構成は、どれもYarn Workspaceを利用したモノレポ構成であるにも関わらず、 ものによってはエラーが発生しないことが気になったので検証してみたところ、 以下のIssueで言及されているようにVSCodeとpackage.jsonのバージョンが異なることが関連していました。

github.com

つまりVSCodeでCSSのimportがts(2882)になっていたのは、 package.jsonで利用しているTypeScriptが5系で、VSCodeのTypeScriptのバージョンが6系なことが原因だったようです。

なぜエラーにならないプロジェクトがあったのか?

それはワークスペース内のTypeScriptのバージョンが、VSCodeのTypeScriptのバージョンと全て同じ6系だったためでした。

例えば、VSCodeのTypeScriptのバージョンが6.0.3で、プロジェクト内のpackage.jsonに存在するtypescriptが6.0.36.0.2の場合は、 ts(2882)のエラーが発生しません。

しかし、プロジェクト内のpackage.jsonに5系のtypescriptが存在していた場合、 CSSをimportしているアプリケーションのpakcage.jsonで、typescriptが6系になっていてもエラーが発生します。

・・・どういうことやねん!!!

そもそも実際のビルドではエラーになりませんし、VSCode依存の話のようなので、あまり気にしないのがよさそうです。

問題解消するまでの経緯

そもそもts(2882)が発生する根本原因はなんだろうと、GoogleのAIによる概要を無視して調べたところ、 上記のIssueに行き着き、VSCodeのTypeScriptバージョンが切り替えれることを知りました。

それならなにかTS関連の設定が影響しているのかとtsconfig.jsonの内容を確認してみたり、node_modules内の型定義を調べてみたりしたのですがなにもわからず、 5系を6系に上げてみてもエラーは発生するので「もういいか、諦めて」と何度も思ったのですが、絶対になにか原因があるのは明らかだったので、ソシャゲをしながらも考えていました。

というか実は「もしかしてこれかも?」と思い当りながらも、「まさかそんなことないかw」と思って確認していなかったのが、

ワークスペース内のTypeScriptのバージョンが、VSCodeのTypeScriptのバージョンと全て同じ6系であること

でした。

そう疑っていながらも「アプリケーションが参照しているpackage.jsonのtypescriptを6.0.3にしたのだから、他のpackage.jsonが5系でも関係ないはず」と確認するのを面倒くさがり、勝手な結論を導き出したため、結果として無駄に時間を消費してしまったわけです。

「まあ最後に検証してみるか」と確認したので、原因が判明しましたが、なんとも言えない気持ちでこの記事を書いています。

というのも、仕事でも似たようなことは何度かあり、その都度「まさかこんなことが」というのが原因でしたし、1つは苦い思い出として記憶に残っているため、成長が感じられないというか、なんというか・・・まあ確認しただけ成長したか、とかそういう気持ちです。

ちなみに苦い思い出は、ApacheとBIG-IPの組み合わせが正常に動作しないパターンがあったのですが、それの調査がまさに今回と同じように「このパターンはこれが問題なかったから問題ないはず」としてスルーしてしまったのが、最後は先輩と全パターン網羅チェックしていたらまさにそのスルーしたパターンだったというやつでした。はぁ・・・

今後の検証は面倒くさがらないように頑張ってほしい、自分。

補足情報

  • VSCodeバージョン: 1.119.0 (Universal)

参考

【Next.js】RootLayoutのheadタグの内容を全てmetadataに移行した

今まで管理画面なんかに使うNext.jsのルートレイアウトは、こんな感じだったのですが、

import React from "react";
import type { Metadata } from "next";
import { Inter, Montserrat, Noto_Sans_JP, Noto_Sans_Mono } from "next/font/google";
import { Toaster } from "react-hot-toast";
import { TITLE } from "@/constants/application";
import "@/styles/globals.css";

const noteSansJp = Noto_Sans_JP({
  variable: "--font-noto-sans-jp",
  subsets: ["latin"],
});

const noteSansMono = Noto_Sans_Mono({
  variable: "--font-noto-sans-mono",
  subsets: ["latin"],
});

const inter = Inter({
  variable: "--font-inter",
  subsets: ["latin"],
});

const montserrat = Montserrat({
  variable: "--font-montserrat",
  subsets: ["latin"],
});

export const metadata: Metadata = {
  title: {
    default: TITLE,
    template: `%s | ${TITLE}`,
  },
  description: "This is a sample application.", // TODO
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  const copyright = "your copyright"; // TODO

  return (
    <html lang="ja">
      <head>
        <meta name="robots" content="noindex" />
        <meta name="copyright" content={copyright} />
        <link rel="icon" href="/favicon.ico" />
      </head>
      <body
        className={`${noteSansJp.variable} ${noteSansMono.variable} ${inter.variable} ${montserrat.variable} antialiased`}
      >
        {children}
        <div>
          <Toaster />
        </div>
      </body>
    </html>
  );
}

いつだったかMetadataの定義をなんとなく確認してみたところ、今headタグにベタ書きしている要素も定義できることを知り、現在はこんな感じになっています(変更部分のみ)。

export const metadata: Metadata = {
  title: {
    default: TITLE,
    template: `%s | ${TITLE}`,
  },
  description: "This is a sample application.", // TODO
  icons: {
    icon: "/favicon.ico",
  },
  robots: {
    index: false,
    follow: false,
  },
  other: {
    copyright: "your copyright", // TODO
  },
};

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="ja">
      <body
        className={`${noteSansJp.variable} ${noteSansMono.variable} ${inter.variable} ${montserrat.variable} antialiased`}
      >
        {children}
        <div>
          <Toaster />
        </div>
      </body>
    </html>
  );
}

だから何だというわけでもないのですが、headタグに当たる内容が分散しているのが気になっていたので、スッキリしていい感じです。

openGraphを使えばOGPの設定もできるようなので、次公開サービスを作るときはちょっと楽できそうです。

補足情報

  • next: 16.2.6