AstroJavaScriptMarkdoc

Next.jsで作ったサイトをAstroに変えた際の、Mardocの取り扱い

hidetaka.devという個人のポートフォリオサイトを運用しており、Next.jsとNetlifyで公開していたが、SSRへの移行を検討。CloudflareとAstro採用し、MarkdocからNext.jsにカスタマイズ追加。Astroではastro.config.mjsに設定記述。取得処理やAstroコンポーネント実装も実施。Next.jsからAstroに移行し、getStaticProps内のカスタマイズもAstroコンポーネント側に移行。最終的にCloudflare Pagesでfsが使えないことが判明し、Markdocでのコンテンツ管理諦めたが、Astroの使用を今後他の環境でも検討。

広告ここから
広告ここまで

https://hidetaka.dev という個人のポートフォリオサイトを運用しています。そのホスティング環境やフレームワークを変えたので、その時の覚書を整理しました。

背景

もともとSSGとしてNext.jsを利用し、Netlifyでお手軽に公開していたサイトでした。ただ、npmモジュールやWordPressプラグインなどの情報もポートフォリオへ含めようとした際、ウェブフックイベントなどがこれらには存在しませんでした。そのため更新情報の反映がSSGでは難しくなってきたことから、SSRできる環境への移行を検討し始めました。

その中で今後使うことの増えそうな予感がしたCloudflareとAstroを使うことにしました。

Markdocのレンダー処理を変える

Markdownで書いていたコンテンツは、Markdocを使って描画していました。そして外部リンクのようなMarkdown単独では表現しにくい部分に、独自タグのレンダリング用に入れていたカスタマイズを、Next.jsに追加していました。

import { parse, transform } from '@markdoc/markdoc'
import { readFileSync } from 'fs'
import { join } from 'path'

export const loadMarkdownFile = (path: string) => {
  const content = readFileSync(join(process.cwd(), path), 'utf-8')
  return transform(parse(content), {
    tags: {
      externalLink: {
        render: 'ExternalLink',
        attributes: {
          className: {
            type: String,
          },
          href: {
            type: String,
          },
          label: {
            type: String,
          },
        },
      },
    },
  })
}

この設定からカスタムのReactコンポーネントを描画するためのrender処理を入れるところまでをやっていました。

import { renderers } from '@markdoc/markdoc'
import React, { FC } from 'react'
import { ExternalLink } from './ExternalLink'

export const MarkdocContent: FC<{
  content: string
}> = ({ content }) => {
  return (
    <>
      {renderers.react(JSON.parse(content), React, {
        components: {
          ExternalLink,
        },
      })}
    </>
  )
}

Astroの場合は、astro.config.mjsにConfigを書くことになります。

import { defineConfig } from 'astro/config';
import markdoc from "@astrojs/markdoc";

// https://astro.build/config
export default defineConfig({
  integrations: [markdoc({
    tags: {
      externalLink: {
        render: 'ExternalLink',
        attributes: {
          class: {
            type: String,
          },
          href: {
            type: String,
          },
          lebel: {
            type: String,
          }
        }
      },
    },
  })]
});

あとは取得処理などをアイランド内に記述し、Astroコンポーネントとして実装しています。

---
import { getEntryBySlug } from 'astro:content';
import ExternalLink from '../components/markdoc/ExternalLink.astro';

const lang = getLanguageFromURL(new URL(Astro.request.url).pathname) || 'en-US'

const entry = await getEntryBySlug('profiles', `hero${/ja/.test(lang) ? '-ja' : ''}`);
const speakerProfile = await getEntryBySlug('profiles', `speaker-profile${/ja/.test(lang) ? '-ja' : ''}`);
const { Content } = entry ? await entry.render() : { Content: null};
const { Content: SpeakerProfile } = speakerProfile ? await speakerProfile.render() : {Content: null}

---
{Content ? (
    <Content
        components={{ ExternalLink }}
    />
): null}

{SpeakerProfile ? (
    <SpeakerProfile
        components={{ ExternalLink }}
    />
): null}

Next.jsの場合、いくつかgetStaticProps内で見た目のカスタマイズを入れていたため、この辺りについてもAstroのコンポーネント側に移すことができました。


export const getStaticProps: GetStaticProps<{
  profiles: {
    hero: string
    speakerBio: string
  }
}> = async (context) => {
  const heroArticle = loadMarkdownFile('contents/profiles/hero.md')
  const heroContent = (heroArticle as any).children
  heroContent[0].attributes = {
    ...heroContent[0].attributes,
    className: 'mt-3 text-base text-gray-500 sm:mt-5 sm:text-xl lg:text-lg xl:text-xl',
  }
  const speakerBioFileName = isJapanese(context.locale)
    ? 'speakerProfile.ja.md'
    : 'speakerProfile.md'
  const speakerBio = loadMarkdownFile(`contents/profiles/${speakerBioFileName}`)
  const profiles = {
    hero: JSON.stringify(heroContent),
    speakerBio: JSON.stringify(speakerBio),
  }

  return {
    props: {
      profiles,
    },
    revalidate: 1 * 60 * 60,
  }
}

やってみての感想

この後、Cloudflare Pagesでfsが使えないことが判明し、Markdocでのコンテンツ管理は結局諦めました。しかしどちらのやり方もざっと触ることができたので、今後他の環境でAstroを使うことになった際は使ってみようかと思います。

ブックマークや限定記事(予定)など

WP Kyotoサポーター募集中

WordPressやフロントエンドアプリのホスティング、Algolia・AWSなどのサービス利用料を支援する「WP Kyotoサポーター」を募集しています。
月額または年額の有料プランを契約すると、ブックマーク機能などのサポーター限定機能がご利用いただけます。

14日間のトライアルも用意しておりますので、「このサイトよく見るな」という方はぜひご検討ください。

広告ここから
広告ここまで

Related Category posts