Closed18

Nuxt.js から Next.js にブログを移行する

モチベーション

  • 現在は Nuxt.js を使っているが、React のほうが使い慣れているので Next.js に置き換えたい
  • webp への変換を手元でがんばっているが、next/image に任せたい
  • typography.js を使ってるが、開発は止まっているようなので、tailwindcss-typography などに置き換えたい
  • TypeScript で書きたい(当時はそこまで慣れてなかったのでJSで書いた)

いままでは素の Markdown で書いていたが、 next/image などの利用を考えると MDX に変更したほうがよさそう。

https://mdxjs.com/

画面レイアウトやデザインは、基本そのまま踏襲。

Next.js とは関係ないけど、OGPの画像を自動生成するやつは別で対応したい

Lighthouse のスコアは同じレベルを維持する

Syntax Highlighting は Prism を使おうとしたが、いいテーマがなかったので、以前のブログでも使っていた highlight.js を使うことに。

rehype プラグインがあったのでそれを使えばOK

https://github.com/rehypejs/rehype-highlight

atom.xml と sitemap.xml がエラーを吐く...

{"errorType":"Runtime.ImportModuleError","errorMessage":"Error: Cannot find module 'esbuild'\nRequire stack:\n- /var/task/.netlify/functions-internal/next_atomxml/nextPage/chunks/637.js\n- /var/task/.netlify/functions-internal/next_atomxml/nextPage/pages/atom.xml.js\n- /var/task/.netlify/functions-internal/next_atomxml/next_atomxml.js\n- /var/task/next_atomxml.js\n- /var/runtime/UserFunction.js\n- /var/runtime/index.js","trace":["Runtime.ImportModuleError: Error: Cannot find module 'esbuild'","Require stack:","- /var/task/.netlify/functions-internal/next_atomxml/nextPage/chunks/637.js","- /var/task/.netlify/functions-internal/next_atomxml/nextPage/pages/atom.xml.js","- /var/task/.netlify/functions-internal/next_atomxml/next_atomxml.js","- /var/task/next_atomxml.js","- /var/runtime/UserFunction.js","- /var/runtime/index.js","    at _loadUserApp (/var/runtime/UserFunction.js:100:13)","    at Object.module.exports.load (/var/runtime/UserFunction.js:140:17)","    at Object.<anonymous> (/var/runtime/index.js:43:30)","    at Module._compile (internal/modules/cjs/loader.js:999:30)","    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)","    at Module.load (internal/modules/cjs/loader.js:863:32)","    at Function.Module._load (internal/modules/cjs/loader.js:708:14)","    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:60:12)","    at internal/main/run_main_module.js:17:47"]}

next/image をMarkdown内に書きたかったために MDX を使おうと思っていたが、 Markdown 内には <img> で書いて、React への変換時に next/image に書き換えるようにすれば、MDX にする必要がないので、次のように方針転換する。

  • markdown に JSX は含ませない。(HTMLは含ませる)
  • remark / rehype のプラグインを利用して Markdown から React への変換を行う
    • その際に img タグを next/image に置き換える
ContentBody.tsx
export const ContentBody: React.FC<Props> = ({ mdx }: Props) => {
  const processor = unified()
    .use(remarkParse) // Parse markdown to mdast(markdown AST)
    .use(remarkRehype, { allowDangerousHtml: true }) // mdast -> hast (HTML AST)
    .use(rehypeRaw) // parse hast again (and raw nodes) to deal with HTML inside the Markdown
    .use(rehypeHighlight) // syntax highlighting
    .use(rehypeReact, {
      createElement: React.createElement,
      components: {
        a: CustomLink,
        img: Image,
      },
    }); // hast -> react
  return processor.processSync(mdx).result;
};

atom.xml と sitemap.xml の生成は別のエラーでつまづいた。Netlify が自動生成する function から _post が参照できないらしい。

{"errorType":"Runtime.UnhandledPromiseRejection","errorMessage":"Error: ENOENT: no such file or directory, scandir 'src/_posts'","trace":["Runtime.UnhandledPromiseRejection: Error: ENOENT: no such file or directory, scandir 'src/_posts'","    at process.<anonymous> (/var/runtime/index.js:35:15)","    at process.emit (events.js:314:20)","    at processPromiseRejections (internal/process/promises.js:209:33)","    at processTicksAndRejections (internal/process/task_queues.js:98:32)"]}

Netlify functions のためにディレクトリ構造をいじったりするのは嫌だし、インフラに依存したコードでハマるのは嫌なので、なんとか回避策を考える。

SSR をしようとすると Netlify functions でつまずくっぽいので、SSG で回避。

  • pages/atom.xml.tsx ではなく pages/atom.tsx にする。
  • atom.tsx の SSG で public/atom.xml を自動生成
  • /atom にアクセスしたら、クライアントサイドで /atom.xml にリダイレクト
pages/atom.tsx
export const getStaticProps = async () => {
  const xml = await localPostRepository.generateAtomFeed();
  await localPostRepository.writeAtomFeed(xml);
  return {
    props: {},
  };
};

const AtomFeed: NextPage = () => {
  const router = useRouter();
  useEffect(() => {
    router.replace('/atom.xml');
  }, [router]);
  return null;
};

export default AtomFeed;
このスクラップは1ヶ月前にクローズされました
作成者以外のコメントは許可されていません