以前nuxt/content の記事の日付を Git ベースにするという記事で、Generateする際に Git のログを使って記事の日付を管理するようにしました。
この際に使用していた SpawnSync というのは、OSコマンドをNode.jsから実行するためのものです。(Syncとついているほうは同期的で、ついてない非同期のものもあります)
つまり、前回のようにコミットの日付だけでなく、コミットメッセージとかハッシュなんかもとれるんじゃないか、と。
差分までとる必要はないかもしれないですが、いつアップデートしたか、ログツリーを見せられたらおもしろそうだな、ということでやってみることにしました。
content.ts の拡張
前回の /server/plugins/content.ts
に追記します。
場所は、前回の updatedAt 用の try - catch
のあととかでいいんじゃないでしょうか。
例によってエラーハンドリングは割愛します。
try {
document.commitHistory = spawnSync(
'git',
['log', '--format=%ct-:-:-%s', path.basename(filePath)],
{ cwd: path.dirname(filePath) }
).stdout.toString('utf-8')
} catch (e) { /* todo: add handling */ }
%ct
は前回と同じ、UNIXタイムスタンプによるコミット日時です。%s
は件名です。Subjectかな?-:-:-
はあとで処理しやすいようにするための区切り文字です。
あとで String.prototype.split
で分割する際に separator
に指定しますので、ここでしか使わない特別な記号の並びとかにしておくと良いとおもいます。
これで useContent コンポーザブルで page を取得すると、新たに commitHistory というキーでコミットログが書き込まれるようになります。
で、その page.commitHistory
はこんな感じになります。
1683604318-:-:-update post 2023041701\n1681691984-:-:-add post 2023041701\n
\n
は改行コードですね。git log
したときにコミットごとに改行がはいってるのでそれです。
なので、まずはこれを使って分割すると、1コミットごとの Array になるので、その中でさらに先ほどの区切り文字で分割してあげることで日付とメッセージに分割することができます。
表示用コンポーネントの作成
適当なコンポーネントをつくって page.commitHistory
を扱いやすい形に整えます。
const h = ref()
const tmp: { date: string; msg: string; }[] = []
history.split('\n').forEach((line) => {
if (line !== '') {
const log = line.split('-:-:-')
tmp.push({
date: dayjs(parseFloat(log[0]) * 1000).format(),
msg: log[1]
})
}
})
h.value = tmp
この時点で h.value
はこうなるはずです
const h = [
{
"date": "2023-05-09T12:51:58+09:00",
"msg": "update post 2023041701"
},
{
"date": "2023-04-17T09:39:44+09:00",
"msg": "add post 2023041701"
}
]
なので、あとはこれを v-for
で回すコンポーネントを作れば完成です。
ログがとれない、修正したのにフォーマットが変わらない、等
Nitro プラグイン側での処理はキャッシュされるようで、フォーマットを変更した場合とかにうまく反映されない場合があります。
そういう場合は、以下のコマンドでキャッシュをクリアすることで解消できるはずです。
npx nuxi cleanup
dev server を起動したまま実行すると 500 エラーになりますが、 dev server を立ち上げ直してリロードすれば治るはずです。
応用
たとえば、これを使って、コミットログがない状態のときは _draft
の値を true
に上書きするようにすれば、各記事の中でドラフトフラグを使わないでもコミットされた記事のみが公開されるようにすることもできそうです。
try {
document.commitHistory = spawnSync(
'git',
['log', '--format=%ct-:-:-%s', path.basename(filePath)],
{ cwd: path.dirname(filePath) }
).stdout.toString('utf-8')
+ if (document.commitHistory.length === 0) {
+ document.untracked = true
+ document._draft = true
+ } else {
+ document.untracked = false
+ }
} catch (e) { /* todo: add handling */ }
たとえば上記の場合だと、コミットがない場合1は強制的に _draft
の値を true
に書き換えます。
なので、generate
するときに除外したり、開発モードで[DRAFT]
表示をさせたりできます。
もっとも、GitHub Actions 等を使ってオンラインで generate するのであればコミットしてない下書き投稿は push されないため、記事HTMLの生成されないはずなのでそこまで気にしなくていい気もしますが。
ローカルから generate する場合はうっかり公開とかを防げるかもしれませんね。
あと、ついでに untracked
というカスタムフラグもつけてみました。
通常は_draft
フラグと食い合うのであまり意味がないんですが、別々に取得しておくことで、
公開後に非公開にしたつまり、コミット履歴があるけど非公開にするために _draft
を markdown ファイル側に手動で付け加えた記事がわかるようになりますので、まぁいつか役に立つかもしれません。
Footnotes
git log
コマンドは履歴がないと空文字になるのでそれを利用してコミット済みかを判定しています ↩