記事を書き始めるとき、毎回「タイトル」「タグ」「見出し構成」を手入力するのは地味に面倒です。特に下書き用のMarkdownを別の場所で作っていると、Qiitaへ投稿する前に形式を整える作業が発生します。
そこで今回は、Markdownファイルを読み込み、Qiita向けの記事テンプレートを生成するCLIをNode.jsで作ります。外部パッケージは使わず、タイトルとタグを補いながら、Qiitaへ貼り付けやすいMarkdownを出力します。
結論として、入力ファイルを用意して1コマンド実行するだけで、次のような形式の記事を生成できるようにします。
TITLE: 記事タイトル
TAGS: Node.js, Markdown, CLI
---
本文
作るCLIの仕様
CLIが入力Markdownを読み込み、タイトルとタグを整形して出力するまでの流れは次の通りです。
今回のCLIでは、次の入力を受け取ります。
- Markdownファイルのパス
-
--titleで指定する記事タイトル -
--tagsで指定するQiitaタグ -
--outで指定する出力先
タイトルを指定しなかった場合は、入力Markdownの最初にあるH1見出しを利用します。タグを指定しない場合は、空のタグ欄を出力します。
入力ファイルを次のように作成します。
# MarkdownからCLIを作る
Markdownファイルを読み込み、Qiita向けの記事形式に変換します。
## 背景
記事の下書きを毎回手作業で整形していました。
## 実装
Node.jsの標準APIだけで実装します。
生成後は、先頭のH1をタイトルとして扱い、本文からは取り除きます。Qiita用のメタ情報と本文を分離するため、区切り文字には --- を使います。
プロジェクトを作成する
Node.jsがインストールされている環境で、プロジェクトを作成します。今回はNode.js 20で動作確認しました。
mkdir qiita-template-cli
cd qiita-template-cli
npm init -y
mkdir bin
package.json にCLIの実行ファイルを登録します。
{
"name": "qiita-template-cli",
"version": "1.0.0",
"type": "module",
"bin": {
"qiita-template": "./bin/qiita-template.mjs"
}
}
ローカルでコマンドとして実行できるよう、次のコマンドを実行します。
npm link
Windows環境では、権限によってシンボリックリンクの作成に失敗する場合があります。その場合は、まず生成スクリプトを node bin/qiita-template.mjs として直接実行すれば動作を確認できます。
CLIを実装する
bin/qiita-template.mjs を作成します。ファイルの読み込みには fs/promises、引数の解析には標準の process.argv を使います。
#!/usr/bin/env node
import { readFile, writeFile } from "node:fs/promises";
import path from "node:path";
function printUsage() {
console.log(`
Usage:
qiita-template <input.md> [options]
Options:
--title <title> 記事タイトル
--tags <tags> カンマ区切りのタグ
--out <file> 出力ファイル
--help ヘルプを表示
`);
}
function getOption(args, name) {
const index = args.indexOf(name);
return index >= 0 ? args[index + 1] : undefined;
}
const args = process.argv.slice(2);
if (args.includes("--help") || args.length === 0) {
printUsage();
process.exit(args.length === 0 ? 1 : 0);
}
const inputPath = args.find((arg) => !arg.startsWith("--"));
const titleOption = getOption(args, "--title");
const tagsOption = getOption(args, "--tags");
const outputPath = getOption(args, "--out");
if (!inputPath) {
console.error("入力Markdownファイルを指定してください");
process.exit(1);
}
try {
const source = await readFile(inputPath, "utf8");
const lines = source.split(/\r?\n/);
const h1Index = lines.findIndex((line) => /^#\s+/.test(line));
const detectedTitle =
h1Index >= 0 ? lines[h1Index].replace(/^#\s+/, "").trim() : "";
const title = titleOption || detectedTitle;
if (!title) {
throw new Error(
"タイトルが見つかりません。--title またはH1見出しを指定してください"
);
}
const bodyLines =
h1Index >= 0
? lines.filter((_, index) => index !== h1Index)
: lines;
const body = bodyLines.join("\n").trim();
const tags = (tagsOption || "")
.split(",")
.map((tag) => tag.trim())
.filter(Boolean)
.join(", ");
const result = `TITLE: ${title}
TAGS: ${tags}
---
${body}
`;
if (outputPath) {
await writeFile(outputPath, result, "utf8");
console.log(`生成しました: ${path.resolve(outputPath)}`);
} else {
process.stdout.write(result);
}
} catch (error) {
console.error(`エラー: ${error.message}`);
process.exit(1);
}
この実装では、最初のH1だけをタイトルとして使用します。本文中にあるH2以下の見出しやコードブロックは、そのまま保持されます。
--title を指定した場合は、Markdown内のH1よりもコマンドライン引数を優先します。記事を再利用する場合に、タイトルだけ差し替えられるようにするためです。
実際に使ってみる
先ほどのMarkdownを draft.md として保存し、CLIを実行します。
qiita-template draft.md \
--tags "Node.js,Markdown,CLI,Qiita" \
--out article.md
生成された article.md は次のようになります。
TITLE: MarkdownからCLIを作る
TAGS: Node.js, Markdown, CLI, Qiita
---
Markdownファイルを読み込み、Qiita向けの記事形式に変換します。
## 背景
記事の下書きを毎回手作業で整形していました。
## 実装
Node.jsの標準APIだけで実装します。
出力先を指定しなければ、標準出力に表示できます。
qiita-template draft.md --tags "Node.js,Markdown" > article.md
タイトルを上書きしたい場合は、--title を追加します。
qiita-template draft.md \
--title "MarkdownからQiita記事を生成する" \
--tags "Node.js,Markdown" \
--out article.md
検証で確認したこと
手元では、次のケースを確認しました。
まず、H1がある通常のMarkdownでは、H1だけがメタ情報のタイトルに移動し、本文から削除されます。H2以下の見出し、箇条書き、リンク、コードブロックは変更されません。
次に、H1がないMarkdownを試しました。
本文だけの下書きです。
この場合はタイトルを推測できないため、エラーになります。
エラー: タイトルが見つかりません。--title またはH1見出しを指定してください
タイトルを引数で渡せば、H1がなくても生成できます。
qiita-template notes.md \
--title "標準APIだけでMarkdownを処理する" \
--tags "Node.js,Markdown"
また、タグに余分な空白がある場合も確認しました。
qiita-template draft.md \
--tags " Node.js, Markdown , CLI "
出力時にはタグの前後の空白を削除し、次のように整形されます。
TAGS: Node.js, Markdown, CLI
一方で、Qiitaへの投稿そのものはこのCLIでは行いません。投稿処理まで自動化すると認証情報の管理や誤投稿への対策が必要になるため、今回は「安全に下書きを生成する」範囲に絞りました。
まとめ
MarkdownからQiita向けの記事テンプレートを生成するCLIを、Node.jsの標準APIだけで作りました。
今回のポイントは次の3つです。
- MarkdownのH1から記事タイトルを自動抽出する
-
--tagsと--outで投稿用の形式を整える - Qiitaへの投稿は行わず、確認可能なファイルとして出力する
記事を書くたびに同じ形式を手作業で準備しているなら、数十行のCLIでも十分に効果があります。今後拡張するなら、文字数チェック、見出し構成の検証、テンプレートファイルの差し替えなどを追加すると、より実用的な執筆補助ツールにできます。