Gatsby
のBLOGでは設定のようなものを各Markdown
ファイルのヘッダにYAML
と言われる形式で記述する必要がある。
これをフロントマター
と呼ぶ(Front-matter
)
例えばこの記事では次のように入力している。
---
templateKey: blog-post
title: '【Gatsby.js】CMSを使わずにMarkdownファイルを作ってくれるツール'
date: 2020-05-30
description: 'CMSを使わないでGatsbyのBLOG記事を書く時に面倒だったヘッダとファイル名を自動でいい感じにしてくれるツールをVue.jsで作成'
draft: false
featuredimage:
author: CrypticToilet
tags:
- Gatsby.js
- Vue.js
---
とにかくコレが非常に面倒。
このYAML
をヘッダにする形式は調べた限りR
という言語(統計学をやる人には定番の言語)を使ってドキュメントを書く「R Markdown
」の記事しかヒットせず、BLOG界隈でもかなりマニアックな印象を受けた。
ともかくYAML
とは「データを表現するためのフォーマットの一つ」である。
YAML
のメリットは視認性が良いという理由から設定ファイル用のデータ形式に採用されているパターンが多い。
Gatsby
のMarkdown
ではこの表記法を使ってタイトルや日付を入れている。
良い。
ぶっちゃけ、それで良い。
必要なところを入力していけば良い。
ただ、どうしても日付の手入力、tag
のインデントミス、author
は一つしか選択できないのに2つ記入・・・などのミスが目出つ。
エンジニア的感覚だと、こういうヒューマンエラー因子を取り除きながらも1秒でも早く作業を終わらせられる仕組みは歓迎である。
通常BLOGというのはCMS
で書いてファイル名もCMS
が自動で出力してくれるものである。
ファイル名を日本語にするかIDのような物にするかは賛否ありどちらが良いという定説はない。
わざわざファイルを保存して、一回閉じて(閉じないとファイル名が変えられない)、タイトルと日付を入れて・・・これもまた面倒であり、ミスの温床である。
というわけで次のようなものを作った。
本体も公開しておくのでサンプルとしてor利用価値がありそうなら利用してもらいたい。
動画サンプル
日付、タイトル、説明、tag、著者を入力するとYAML
部分が入力済みのmd
ファイルをファイル名をいい感じにした状態でDL出来る。この動画のバージョンには無いが、後ほどファイル名に日付が入るよう変更した。
さすがに本文まで編集する機能はない。今使っているMarkdown
エディタのTypora
が高機能なのでここは自前で用意する必要はない。
特別難しいことはしていないので一部面白いところだけ紹介する。
const regex = /[~=+;,!$'`\\^&\\@\\{\\}\\[\]\\#\\%\\.\\*<>:\\"/\\|?*]/g;
const file_title = this.title.replace(regex, "_");
console.log(this.title);
タイトルを直接ファイル名にするがそれだとファイル名に使用できない文字が含まれることが有るので正規表現で除去している。
ここではファイル名に使える記号も除去しているが「なんとなく見栄えが悪くなるから」という理由だけである。
ファイル名は直接URLとして動作するためより厳格なルールが必要そうにみえるが、URI文字にエンコードされるため日本語名なども普通に使える。あまり難しく考える必要はない。
これもなんだかよく使うコード。
せっかくなのでメモ書き程度にここに置いておく。
出力したいテキストはoutput
という変数に入っている。
//ファイルでDL
let blob = new Blob([output], { type: "text/plain" });
let link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = `${file_title}.md`;
link.click();
ダウンロードしたいデータ本体とファイル名以外は基本コピペで良い。データ形式がテキストではない場合だけ調べて変更。
既に述べたがMarkdown
本文のエディタ機能はない。
また、私のYAML
データ専用であるため汎用性がない。
Gatsby
ユーザー全体に対応する方法は調べていない(各ユーザの実装によって設定項目が変わる)のでもし需要がありそうだと判断した場合は作り直して公開したい。
あくまで各自で自前で用意したいという需要があれば参考にしてもらいたい。
Vuetify
を使っている以外は単一のコンポーネント。
プロジェクトを新規作成してVuetify
をadd
したらHelloWorld
にコピペすれば動くはずだ。余計なものもインストールしていない。
<template>
<v-container>
<v-row justify="center" align="center">
<!-- 入力不要
<v-col cols="6">
<v-text-field label="Template Key" v-model="template_key" disabled></v-text-field>
</v-col>
<v-col cols="2">
<v-switch v-model="draft_mode" class="ma-2" label="Draft Mode"></v-switch>
</v-col>
<v-col cols="10">
<v-text-field label="featuredimage" v-model="featuredimage"></v-text-field>
</v-col>-->
<v-col cols="12" >
<v-row justify="center" align="end">
<v-date-picker v-model="publish_date"></v-date-picker>
<div class="ml-4">
<v-text-field label="Publish Date" v-model="publish_date" disabled></v-text-field>
<v-btn small class="mt-4" @click="caliculate_date()">Now</v-btn>
</div>
</v-row>
</v-col>
<v-col cols="12">
<v-text-field label="Title" v-model="title"></v-text-field>
</v-col>
<v-col cols="12">
<v-text-field label="Description" v-model="description"></v-text-field>
</v-col>
<v-col cols="6">
<v-text-field label="Tags" v-model="tag"></v-text-field>
</v-col>
<v-col cols="1">
<v-btn @click="addTag(tag)">add</v-btn>
</v-col>
<v-col cols="5">
<v-select :items="authors" v-model="author" label="Author"></v-select>
</v-col>
<v-col cols="12">
<v-chip
v-for="(tag,index) in tags"
:key="index"
class="ma-2"
close
color="green"
outlined
@click:close="deleteTag(index)"
>{{tag}}</v-chip>
</v-col>
<v-btn color="success" class="ma-4" x-large @click="download()">download</v-btn>
</v-row>
</v-container>
</template>
<script>
export default {
name: "HelloWorld",
data: () => ({
template_key: "blog-post",
title: "",
publish_date: "",
description: "",
featuredimage: "",
tags: [],
tag: "",
draft_mode: false,
author: "CrypticToilet",
authors: ["CrypticToilet", "Neve1074"]
}),
methods: {
download() {
const regex = /[~=+;,!$'`\\^&\\@\\{\\}\\[\]\\#\\%\\.\\*<>:\\"/\\|?*]/g;
const file_title = this.title.replace(regex, "_");
console.log(this.title);
let output = `---
templateKey: ${this.template_key}
title: '${this.title}'
date: ${this.publish_date}
description: '${this.description}'
draft: ${this.draft_mode}
featuredimage: ${this.featuredimage}
author: ${this.author}
tags:\n`;
for (let tag of this.tags) {
output += ` - ${tag}\n`;
}
output += `---\n`;
console.log(output);
//ファイルでDL
let blob = new Blob([output], { type: "text/plain" });
let link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.download = `${this.publish_date}-${file_title}.md`;
link.click();
},
caliculate_date() {
const date = new Date();
this.publish_date = date.toISOString();
},
addTag(tag) {
this.tags.push(tag);
this.tag = "";
},
deleteTag(index) {
//対象の配列を消去
this.tags.splice(index, 1);
}
}
};
</script>