QwikCityでMDXの記事を書いてみる
2023/12/17
QwikCityがMarkdownとMDXに対応しているので、この機能を利用して記事を書いてみたり、調整したり、一覧表示したりします。
MarkdownとMDX
Markdownは言わずも知れたフォーマットですが、どうやらここ1~2年でMDXなるものの勢いが増しているようです。MDXはMarkdownとjsxをかけ合わせたもののようで、Markdownの中にjsxが書けるといった特徴があります。
Qwik上で使用してみる
QwikCityではデフォルトでMarkdownとMDXに対応しているようなので、早速取り掛かってみましょう。
特に設定をしてなくても読み込めてしまうので、以下の配置にindex.mdxを設置します。
src/
└── routes/
└── blog/
└── some-article/
└── index.mdx
QwikCityはFront matterに対応しているので、下記のようにページタイトルや情報を埋め込むことが出来ます。titleやOpenGraph(og)などは自動で適切にhead要素に展開してくれますし、publishedAtやtagsのように自分で使うプロパティも追加出来ます。
そして、目玉であるjsx/tsxコンポーネントの読み込みも行えます。
---
title: 記事タイトル
description: 記事の概要
opengraph:
- title: 記事タイトル
description: true
- image: https://storage.ephy.dev/path/to/cover/image.webp
image:alt: 記事のカバー画像
publishedAt: "2023-12-17"
tags:
- Frontend
- Qwik
- MDX
---
import { SomeComponent } from "~/components/some-component.tsx"
<SomeComponent />
以降、Markdownを記述
ちょっとしたサンプルコンポーネントを埋め込んでみたり、良い感じに作った画像・動画用コンポーネントなどを定義しておくと取り回しが良いかも知れませんね。
コードブロックにテーマを当てる
QwikCityはデフォルトで3つのプラグインを利用しているようで、refractorの概要には以下のように書いてあります。
Lightweight, robust, elegant virtual syntax highlighting using Prism.
というように内部的にはPrismを使用しているようなので、Prismを追加してcssだけ利用します。
npm install prism-themes
あとはlayout.tsxや任意のコンポーネントで読み込むだけです。
今回はprism-coldark-coldのテーマを選んでみましたがPrismThemesのリポジトリにいろいろなテーマのサンプルがありますので、お好みのものを探してみてください。
import { Slot, component$, useStyles$ } from "@builder.io/qwik";
import prismStyle from "prism-themes/themes/prism-coldark-cold.min.css?inline";
export default component$(() => {
useStyles$(prismStyle);
return <Slot />;
});
記事一覧のためにMDX記事一覧を表示する
Qwik Cityのドキュメントにglob-importのページがあるので、これを参考にしてページ一覧を取得してみます。
export const useArticles = routeLoader$(async () => {
// 記事データの取得
const mdxComponents: Record<string, any> = import.meta.glob("/src/routes/blog/**/index.mdx");
// Frontmatterの抽出
const articles = await Promise.all(
Object.keys(mdxComponents).map(async (path) => {
const doc = (await mdxComponents[path]()) as DocumentHeadProps;
// 記事ディレクトリのパス抽出
const href = path.match(/\/([^/]+)\/index\.mdx$/);
const description = doc.head.meta.find((obj) => obj.name === "description");
return {
title: doc.head.title,
description: description?.content,
href: `${href ? href[1] : ""}`,
tags: doc.head.frontmatter.tags,
};
}),
);
return {
articles: articles,
};
});
export default component$(() => {
const { articles } = useArticles().value;
return <></>; // 省略
});
こんな感じのデータが返ってきますので、これを一覧に使用すれば記事一覧を作成できます。
必要に応じてFrontmatterを追加したり、記事をソートしたり、公開・非公開のフラグを追加して管理してみても良いかも知れませんね。
[
{
title: "Cloudflare R2を試してみる",
description: "Cloudflareが提供するオブジェクトストレージサービスを試してみます。",
href: "get-start-cloudflare-r2",
tags: ["Cloudflare"],
},
{
title: "QwikCity + CloudflarePages + Newtでブログを作る",
description:
"Newt公式のチュートリアルを参考にブログを作成する過程でハマったところやチュートリアル外での作業などを紹介します。",
href: "qwik-city-cloudflare-pages",
tags: ["Frontend", "Qwik", "Cloudflare"],
},
{
title: "QwikCityでMDXの記事を書いてみる",
description:
"QwikCityがMarkdownとMDXに対応しているので、この機能を利用して記事を書いてみたり、調整したり、一覧表示したり。",
href: "qwik-city-mdx-articles",
tags: ["Frontend", "Qwik", "MDX"],
},
];
諸々スタイル調整などをして、無事一覧が出来ました。
ビルド時に以下の警告が出ますが一応動作としては問題ないです。ViteがMDXを静的生成しているにもかかわらず、index.tsxのために動的読み込み(import.meta.glob)してしまっているのが原因だと思います。
[plugin:vite:reporter]
(!) /path/to/index.mdx is dynamically imported by /path/to/index.tsx
but also statically imported by /path/to/@qwik-city-plan,
dynamic import will not move module into another chunk.
index.tsx用のjsonファイルを静的生成するか、StaticGenerateHandlerあたりの活用が解決手段になると思います。
Qwik - Static Site Generation Overview
viteのビルド的には好ましくない状態ですが、対応はまた今度にしたいと思います。