コマンド一発でmdxファイルを作成したい


DE-TEIUです。

このブログに記事を追加する際、記事のmdxファイルの作成を毎回手動でやっていました。 しかし、ふと思った。「これ、スクリプトで自動化して、コマンド一発で生成できた方が良くね?」と。

ということで試しにやってみました。

成果物

Claude Codeと格闘しつつ微調整して最終的にできたものがこちら。

import * as fs from "fs";
import * as path from "path";

/** ブログ記事の保存先ディレクトリ */
const BLOG_DIR = "src/content/blog";

/** 記事用画像の保存先ディレクトリ */
const PUBLIC_ARTICLE_DIR = "public/article";

/**
 * 今日の日付を取得する
 * @returns yyyymmdd形式とyyyy-mm-dd形式の日付文字列
 */
function getToday(): { yyyymmdd: string; yyyy_mm_dd: string } {
  const now = new Date();
  const year = now.getFullYear();
  const month = String(now.getMonth() + 1).padStart(2, "0");
  const day = String(now.getDate()).padStart(2, "0");
  return {
    yyyymmdd: `${year}${month}${day}`,
    yyyy_mm_dd: `${year}-${month}-${day}`,
  };
}

/**
 * 利用可能なファイル名を取得する
 *
 * 指定されたベース名のファイルが既に存在する場合、
 * 連番サフィックス(_2, _3, ...)を付与して重複を回避する
 *
 * @param baseName - ファイル名のベース部分(拡張子なし)
 * @param dir - ファイルを作成するディレクトリパス
 * @returns 利用可能なファイル名(.mdx拡張子付き)
 *
 */
function getAvailableFilename(baseName: string, dir: string): string {
  const ext = ".mdx";
  let filename = `${baseName}${ext}`;
  let counter = 2;

  while (fs.existsSync(path.join(dir, filename))) {
    filename = `${baseName}_${counter}${ext}`;
    counter++;
  }

  return filename;
}

/**
 * 記事のテンプレートコンテンツを生成する
 *
 * frontmatter(タイトル、説明、公開日、ヒーロー画像、タグ)と
 * 画像インポート文を含むMDXテンプレートを返す
 *
 * @param pubDate - 公開日(yyyy-mm-dd形式)
 * @returns MDX形式の記事テンプレート文字列
 */
function createArticleContent(pubDate: string): string {
  return `---
title: ""
description: ""
pubDate: "${pubDate}"
heroImage: ""
tags: [""]
---
import { Image } from "astro:assets";
`;
}

/**
 * メイン処理
 * 今日の日付で、mdxファイルと画像を格納するディレクトリを生成する
 */
function main(): void {
  const { yyyymmdd, yyyy_mm_dd } = getToday();

  // 記事ファイル名を決定
  const filename = getAvailableFilename(yyyymmdd, BLOG_DIR);
  const filePath = path.join(BLOG_DIR, filename);

  // 画像用ディレクトリ名(拡張子なし)
  const baseName = filename.replace(".mdx", "");
  const imageDirPath = path.join(PUBLIC_ARTICLE_DIR, baseName);

  // ディレクトリ作成
  if (!fs.existsSync(BLOG_DIR)) {
    fs.mkdirSync(BLOG_DIR, { recursive: true });
  }
  if (!fs.existsSync(imageDirPath)) {
    fs.mkdirSync(imageDirPath, { recursive: true });
  }

  // 記事ファイル作成
  const content = createArticleContent(yyyy_mm_dd);
  fs.writeFileSync(filePath, content, "utf-8");

  console.info(`Created: ${filePath}`);
  console.info(`Created: ${imageDirPath}/`);
}

main();

で、package.jsonのscriptsに以下を追加。

"article": "tsx scripts/create-article.ts"

これで、以下のコマンドを実行。

$ npm run article

すると、今日の日付でmdxファイルと画像格納用ディレクトリが自動生成されるようになりました。 そしてmdxファイルの中には、必要最低限のヘッダー情報が書かれている。便利。

ちなみに、すでに同じ日付の記事がある場合は、連番サフィックス(_2, _3, …)を付与して重複を回避するようになっています。