サイトのMarkdown読込をRemarkにしたり

2022/11/28 23:49#hoge

社やめて少しずつやる気が出てきたので、少しずつサイト更新。
Next.jsでSSGして成果物を放り込むという運用は継続しつつ一旦壊して再構築みたいなことをしてます。

CSSツールをgooberに乗り換え

Tailwind CSSを使用していましたが、導入がやや面倒なのとcss書いているというよりtailwind書いてる感が出てきたので乗り換えたいと思っていくつか試した中でgooberを選びました。

選んだ理由が「css書きたいけどcssファイルは作りたくないし、テンプレートに直に書きたい。でもstyle属性だとちょっと不便」なので下記のような使い方になっています。

<div className={css`
    margin: 0;
    padding: 0;
`}>

テキストオンリー

以前は@mdi/jsでちょこっとアイコンを入れていました。
やる気は出てきたけど、アイコン導入は面倒になってきたので全てテキストに。

Markdown読込にRemarkを使用

以前はshowdown.jsでMarkdownをHTMLに変換したものをdangerouslySetInnerHTML描画していましたが、描画周りはReactで制御したいということでRemarkを使ってASTを取り扱うようにしました。

自前でcssファイルは置きたくなかったので、以前の構成だとtailwindでスタイルを当てるためにこのようなshowdown.jsの拡張を作成。

const classes: {[key:string]: string} = {
    h1: '',
    h2: 'my-6 text-xl md:text-2xl pb-1 border-b',
    h3: 'my-3 text-lg md:text-xl',
    h4: '',
    h5: '',
    h6: '',
    p: 'my-3 text-sm md:text-base',
    a: 'text-red-600',
    ul: 'my-3 ml-6 text-sm list-disc',
    img: 'my-3',
    video: 'my-3',
    pre: 'hljs my-3 text-sm md:text-base'
}

export default Object.keys(classes).map(key => ({
    type: 'output',
    regex: new RegExp(`<${key}(?: (.*)>|>)`, 'g'),
    replace: `<${key} class="${classes[key]}" $1>`
})).concat({
    type: 'output',
    regex: /(?<!(?:<pre(?: (.*)>|>)))(?:\n?\s*<code(?: (.*)>|>))/g,
    replace: '<code class="bg-gray-100 px-2 py-px mx-px" $1>'
})

これをもっとReactでやりたい放題したいということでRemarkに移行したあと、例えばpタグだとこのようなコンポーネントを作成。

export const AppMarkdownParagraph = ({ node }: Props) => {
    return (
        <p className={css`
            font-size: 16px;
            margin: 16px 0;
            color: ${color['grey-5']};
        `}>
            {node.children.map((node, i) => (
                <AppMarkdown
                    key={i}
                    node={node}/>
            ))}
        </p>
    )
}

同じ要領で各ノード種別毎にコンポーネントを作成してそれらをまとめたコンポーネントを作成しました。
コンポーネント未作成のノードが使用されてたら例外を投げて、ビルド時に気付くようになってます。

export const AppMarkdown = ({ node }: Props) => {
    if (node.type === 'root')
        return <AppMarkdownRoot node={node} />
    if (node.type === 'paragraph')
        return <AppMarkdownParagraph node={node} />
    if (node.type === 'text')
        return <AppMarkdownText node={node} />
    if (node.type === 'list')
        return <AppMarkdownList node={node} />
    if (node.type === 'listItem')
        return <AppMarkdownListItem node={node} />
    if (node.type === 'image')
        return <AppMarkdownImage node={node} />
    if (node.type === 'heading')
        return <AppMarkdownHeading node={node} />
    if (node.type === 'code')
        return <AppMarkdownCode node={node} />
    if (node.type === 'link')
        return <AppMarkdownLink node={node} />
    if (node.type === 'inlineCode')
        return <AppMarkdownInlineCode node={node} />
    if (node.type === 'video')
        return <AppMarkdownVideo node={node} />
    throw new Error(
        `Not something we can render: ${node.type}`
    )
}

これでMarkdownの中身をReactでやりたい放題したいという願望が叶いました。

少しずつやっていきたい

  • 投稿のコミット履歴で差分/更新履歴の表示
  • アナリティクス/SEO
  • ユニットテスト
  • 記事書く、記事書く、記事書く
  • パフォーマンス計測と改善
  • ホスティングどこ

実はhugoぐらいしか静的サイト生成ツールを試したことがないので、Gatsbyとかもお試ししつつ少しずつ。