たくさんの自由帳

Next.js 16.3 にした

投稿日 : | 0 日前

文字数(だいたい) : 1616

別に大した話ではない、、

Next.js 16.3 にしたら Route Handlers 経由の OGP 画像が作れなかった

そもそも当時はNext.js公式の方法ではOGP 画像が作れなくて、Route Handlersroute.tsxOGP 画像を生成させる技が静的モード Next.jsでよく使われていた?
が、Next.js 16.3にしたら失敗するようになってしまった。

リネームした

とりあえずRoute Handlersの名前をopengraph-image.pngからopengraph-image.PNG名前変更した。
というのも、.pngのままだとgenerateStaticParams()で一ミリも返していない謎の動的パスが渡されて、そんなもの想定してないせいで後続の処理が落ちてしまった。

▲ Next.js 16.3.0 (Turbopack)
- Environments: .env
✓ Running next.config.ts took 69ms
- Experiments (use with caution):
  ✓ scrollRestoration

  Creating an optimized production build ...
✓ Compiled successfully in 6.3s
✓ Finished TypeScript in 1032ms    
✓ Collecting page data using 5 workers in 2.4s    
Error occurred prerendering page "/posts/-/opengraph-image.png". Read more: https://nextjs.org/docs/messages/prerender-error
Error: ENOENT: no such file or directory, open 'C:\Users\takusan23\Desktop\Dev\NextJs\ziyuutyou-next\content\posts\-.md'
    at async l2.parse (src\MarkdownParser.ts:51:33)
    at async l4.getCache (src\NextJsCacheStore.ts:20:24)
    at async l5.parseMarkdown (src\ContentFolderManager.ts:252:30)
    at async u (app\posts\[blog]\opengraph-image.png\route.tsx:26:26)
  49 |     static async parse(filePath: string, baseUrl: string = '/posts') {
  50 |         // マークダウン読み出す
> 51 |         const rawMarkdownText = await fs.readFile(filePath, { encoding: 'utf-8' })
     |                                 ^
  52 |         const fileName = path.parse(filePath).name
  53 |         // メタデータ
  54 |         const matterResult = matter(rawMarkdownText) {
  errno: -4058,
  code: 'ENOENT',
  syscall: 'open',
  path: 'C:\\Users\\takusan23\\Desktop\\Dev\\NextJs\\ziyuutyou-next\\content\\posts\\-.md'
}
Export encountered an error on /posts/[blog]/opengraph-image.png/route: /posts/-/opengraph-image.png, exiting the build.
⨯ Next.js build worker exited with code: 1 and signal: null

ちょっと考えてopengraph-image.pngNext.js内部で予約されてるのかなと思い.PNGで大文字にしたら直った。
そもそもこのRoute Handlerを使ったOGP 画像生成は自分で<meta>を書かないといけないので、なんならルート名をogp.pngとかにして<meta>に入れてもよかった。

▲ Next.js 16.3.0 (Turbopack)
- Environments: .env
✓ Running next.config.ts took 78ms
- Experiments (use with caution):
  ✓ scrollRestoration

  Creating an optimized production build ...
✓ Compiled successfully in 6.7s
✓ Finished TypeScript in 1015ms    
✓ Collecting page data using 5 workers in 2.3s    
Failed to set Next.js data cache for https://takusan.negitoro.dev/posts/amairo_kotlin_coroutines_suspend/, items over 2MB can not be cached (3020018 bytes)
✓ Generating static pages using 5 workers (22/22) in 17.9s
✓ Finalizing page optimization in 693ms    

Route (app)
┌ ○ /
├ ○ /_not-found
├ ○ /icon.png
├   /pages/[page]
│ ├ ● /pages/about
│ ├ ● /pages/akari_droid_privacy_policy
│ ├ ● /pages/and_aica_roid_privacy_policy
│ └ ● [+2 more paths]
├   /posts/[blog]
│ ├ ● /posts/amairo_kotlin_coroutines_flow
│ └ ● /posts/amairo_kotlin_coroutines_suspend
├   /posts/[blog]/opengraph-image.PNG
│ ├ ● /posts/amairo_kotlin_coroutines_flow/opengraph-image.PNG
│ └ ● /posts/amairo_kotlin_coroutines_suspend/opengraph-image.PNG
├   /posts/page/[page]
│ └ ● /posts/page/1
├   /posts/tag/[tag]/[page]
│ ├ ● /posts/tag/Android/1
│ ├ ● /posts/tag/Kotlin/1
│ ├ ● /posts/tag/KotlinCoroutines/1
│ └ ● /posts/tag/KotlinCoroutines解説/1
├ ○ /posts/tag/all_tags
├ ○ /search
└ ○ /sitemap.xml


○  (Static)  prerendered as static content
●  (SSG)     prerendered as static HTML (uses generateStaticParams)

Route Handlers を使わない公式の OGP 画像生成にすれば?

Next.jsopengraph-image.tsxで生成した画像のパス、末尾に.pngって付かない。
付かないせいで一部の静的サイトホスティングサービスではうまく取得できないことがある。

かくいう私もAmazon CloudFrontCloudFront Functionsを使って、パス末尾にhtmlが付いていない場合は.htmlを付与してからオリジン(Amazon S3)に取りに行く処理があるため、
拡張子がないファイルだと無条件で.htmlになってしまい都合が悪い!!

・・・まあCloudFront Functionsに相当する処理を自分たちで書ける場合はまだマシというか、静的サイトホスティングによっては静的なので何も介入できない場合が多い。

何もできない環境もあることを考えるとやっぱりRoute Handlersで作らせる方が健全に見える、、、

@11ty/gray-matter も更新した

更新したら日付Dateではなくstringを返すようになった?ので自分でDateにする必要がありそうでした。

  const rawMarkdownText = await fs.readFile(filePath, { encoding: 'utf-8' })
  // メタデータ
  const matterResult = matter(rawMarkdownText)
  // yyyy-mm-dd 形式、多分パース出来るはず
- const date = matterResult.data['created_at'] as Date
+ const date = new Date(matterResult.data['created_at'])

おわりに

いい加減unstable_cache(名前によらず安定して動いてた)からCache Componentsに乗り換えなければと思った。

おわりに2

ついにホットリロード付きでMarkdownを読み込む機能が搭載されたっぽい!?