eyemono.log

AstroでURL末尾に.mdを付けた時にMarkdownを表示させる

Created At: 2025/05/03 23:59
Updated At: 2025/05/04 18:19

Twitterで見かけたやつを本ブログに実装しました。

なるほど確かに、今後LLMに読んでもらうことを考えると、プレーンテキストを返すのは良いアイデアですね(QiitaのこれはAIとかよりも遥か昔からあった上に大抵のAIサービスはURLを与えると勝手にhtml-to-textしてくれると思いますが)。LLMに限らず普通にコピペするときとかも便利そうです。

ということで本ブログでも実装してみました。

実装方法

本ブログのコンテンツはMarkdownで管理しており、ビルド時に生Markdownを取得可能なため、Astroの静的ファイルエンドポイントとして実装しました。

src/pages/api/posts/[id].md.ts
import { join } from "node:path";
import type { APIRoute } from "astro";
import { stringify } from "yaml";
import type { AstroPostEntry } from "../../content.config";
import { getPosts } from "../../utils/getPost";
export async function getStaticPaths() {
const blogEntries = await getPosts();
return blogEntries.map((entry) => ({
params: { id: entry.id },
props: { entry },
}));
}
export const GET: APIRoute<{ entry: AstroPostEntry }> = async ({
props,
url,
}) => {
const md = props.entry.body ?? "";
// ローカル画像参照箇所をフルパスに変換
const localImagePaths =
(props.entry.rendered?.metadata?.localImagePaths as string[] | undefined) ??
[];
const localPathsMap = new Map(
localImagePaths.map((path) => {
const localFullPath = join(props.entry.filePath ?? "", path).replace(
"src/public",
"",
);
const remoteUrl = new URL(localFullPath, url);
return [path, remoteUrl.toString()];
}),
);
const replaced = md.replace(
/!\[([^\]]*)\]\(([^)]+)\)/g,
(match, alt, path) => {
const replacedPath = localPathsMap.get(path);
if (replacedPath) {
return `![${alt}](${replacedPath})`;
}
return match;
},
);
// frontmatterをyamlに変換して先頭に結合
const frontmatter = stringify(props.entry.rendered?.metadata?.frontmatter);
const joined = `---\n${frontmatter}---\n${replaced}`;
return new Response(joined, {
headers: {
"Content-Type": "text/markdown; charset=utf-8",
"Access-Control-Allow-Origin": "*",
},
});
};

やっていることは以下の通りです。

  1. getStaticPathsで全てのブログ記事を取得
  2. 生Markdownを取得
  3. Markdown内のローカル画像参照をフルパスに変換
  4. FrontmatterをYAMLに変換してMarkdownの先頭に結合
  5. text/markdownとしてレスポンスを返す
    • 参考元のQiitaではtext/x-markdownを返しているようですが、2016年3月にtext/markdownRFC 7763として登録されています。

案外スッと実装できました。これでURL末尾に.mdをつけるとMarkdownが返ってきます。 ローカル画像パスの変換処理周り見落としがありそうなのでちゃんとテストしないといけなさそうですが一旦動いているのでヨシ!