Gojabako ZoneKei Ito

Next.js 13にしました

に公開に更新)履歴 (2)

ようやくこのサイトをNext.js 13にできました。そのメモです。

webpackローダーのESM対応

いきなりNext.jsではないのですが、webpack@5.80でESMのLoaderが指定できるようになりました。これがいちばんありがたかったポイントです。

例えば*.mtsをTypeScriptでロードするために以下のような設定をしたとしましょう。MyTsLoader{"type":"module"}なパッケージとします。

webpack.config.jsjavascript
1const webpackConfig = {2 module: { rules: [{ test: /\.mts$/, loader: 'MyTsLoader' }] },3};

webpack@5.79webpack@5.80それぞれでの動作は以下で確認できます:

webpack@5.79では以下のエラーになります:

5.79を使用した場合のエラーtext
1Error [ERR_REQUIRE_ESM]: require() of ES Module /workspace/ts-loader-mjs/index.mjs not supported.2Instead change the require of /workspace/ts-loader-mjs/index.mjs to a dynamic import() which is available in all CommonJS modules.

そのためwebpack@5.79以前はCJSからESMを呼ぶなど回り道が必要でしたが、

Next.js環境でもESMのLoaderが使えるようになりました。

MDXで書く

https://nextjs.org/docs/pages/building-your-application/configuring/mdx

ESMが楽に使えるようになったのでESMでリリースされているunified関連のパッケージも楽に使えるようになりました。

パッケージの追加

@next/mdx @mdx-js/loaderをインストールします1

terminal
1npm install @next/mdx @mdx-js/loader

mdx-components.jsxの追加

何もしないのであればこれでOKです。

mdx-components.jsxjavascript
1export const useMDXComponents = (components) => ({ ...components });

next.config.mjsの変更

このサイトの実際の設定はnext.config.mjsを参照してください。

next.config.mjsjavascript
1import mdx from '@next/mdx';2import remarkGfm from 'remark-gfm';3import remarkMath from 'remark-math';4import rehypeHighlight from 'rehype-highlight';5import rehypeKatex from 'rehype-katex';6import rehypeSlug from 'rehype-slug';78const withMDX = mdx({9 options: {10 remarkPlugins: [remarkGfm, remarkMath],11 rehypePlugins: [rehypeHighlight, rehypeSlug, rehypeKatex],12 },13});1415const nextConfig = {16 // page.tsx, page.mdxがレスポンスを返します17 pageExtensions: ['tsx', 'mdx'],18};1920export default withMDX(nextConfig);

page.mdxを作って表示してみる

次のapp/mdx-sample/page.mdxを追加して/mdx-sampleでページが表示されればOKです。

app/mdx-sample/page.mdxmdx
1# MDXのサンプル23これは**MDXのサンプル**です。

記事やサイトの画像を生成する

satoriというNext.jsに含まれるツールを利用して画像を生成できるようになったので、これを使っています。

/path/to/pageというページの画像を/cover/path/to/pageで生成します。

<meta>/cover/path/to/pageを追加します。MDXでmetadataを設定するにはpage.tsxと同様にmetadataexportします。参考: configuring/mdx#frontmatter

page.mdxjavascript
1export const metadata = {2 title: 'Next.js 13にしました',3 openGraph: {4 images: [5 {6 url: '/cover/2023/next13',7 width: 1280,8 height: 640,9 },10 ],11 },12};

しかしこのopenGraphの値はパスだけわかれば生成できるし、パスが変わったら修正が必要なのでこのような指定はしたくありません。layout.tsxで設定したいところですが現時点ではうまくできませんでした2。そのため後述のようにASTの時点で追加するようにしました。

MDXのASTをいじる

以下の機能を追加しました。

機能追加は以下のような関数をnext.config.mjsremarkPluginsrehypePluginsに追加すれば動きます。

rehypePlugin.mtstypescript
1// import type { Root } from 'mdast'; // MarkdownのASTをいじる場合2import type { Root } from 'hast'; // HTMLのASTをいじる場合34export const rehypePlugin = async (tree: Root, file: { path: string }) => {5 // ここでtreeをいじる6 return tree;7};

Footnotes

  1. @mdx-js/react*.mdximportする必要があれば追加します。

  2. middlewareでヘッダにパスを追加してheaders()でそれを読むなど言及されていますがうまくいきませんでした。

  3. idをposition:absoluteで実際の要素よりも上に表示した要素につけるとリンクから飛んできたときにヘッダーで隠れないようにできます。

  4. 最初のmetadata = {の直後にopenGraph:{...},を入れます。