Gojabako ZoneKei Ito

Next.js 13にしました

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

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

## webpackローダーのESM対応

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

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

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

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

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

webpack@5.79 でのエラー#code2
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

#code3
1npm install @next/mdx @mdx-js/loader

### mdx-components.jsxの追加

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

mdx-components.jsx#code4
1export const useMDXComponents = (components) => ({ ...components });

### next.config.mjsの変更

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

next.config.mjs#code5
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.mdx#code6
1# MDXのサンプル23これは**MDXのサンプル**です。

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

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

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

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

page.mdx#code7
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.mjs の remarkPlugins と rehypePlugins に追加すれば動きます。

rehypePlugin.ts#code8
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:{...}, を入れます。