VuePress でテンプレートから記事生成

最近 VuePress で[個人ブログの運用をはじめました](https://openjny.github.io/)。

ですが、VuePress 単体では記事の Markdown を自動で生成できないことに悩んでいたので、[{{ mustache }}](https://mustache.github.io/) を使った簡易的な記事生成スクリプトを作りました。

イメージ的には `hexo` の [new コマンド](https://hexo.io/docs/commands.html#new) のような感じです。

# 登場人物

`new-post.js` と `templates/*.mustache` を新規に追加します。

```
$ tree vuepress
.
├── new-post.js <------------------------- スクリプト本体
├── package.json
├── docs <-------------------------------- VuePress のプロジェクトフォルダ
│   ├── .vuepress
│   │   └── config.yml 
│   └── _posts
│       └── 2019-12-31-newpost.md <------- 生成される markdown
└── templates <--------------------------- テンプレート フォルタ
    ├── base.mustache
    └── header.mustache
```

# スクリプトの実装

まずは、スクリプトです。`mustache` を使うのでパッケージを入れておきます。

```bash
$ yarn add --dev mustache
```

[スクリプト本体は git で管理](https://github.com/OpenJNY/openjny.github.io/blob/vuepress/new-post.js) してますがここにも記載しておきます。そんな大したことはしてません。

機能：

- 引数で次の 3 つを指定
    - ベースとなるテンプレート
    - 記事のタイトル
    - (optional) markdown のファイル名
- ベースとなるテンプレートに「タイトル」と「日付」を埋め込んで出力
- 出力するファイルは `2019-12-13-lower-case-title.md` のような名前

```js:new-post.js
'use strict';

const fs = require("fs");
const path = require('path');
const mustache = require('mustache');

function formatedateDate(date, includesDatetime) {
    const dateStr = [
        date.getFullYear(),
        ("0" + (date.getMonth() + 1)).slice(-2),
        ("0" + date.getDate()).slice(-2)
    ].join("-");
    const datetimeStr = [
        ("0" + date.getHours()).slice(-2),
        ("0" + date.getMinutes()).slice(-2),
        ("0" + date.getSeconds()).slice(-2),
    ].join(":");

    return includesDatetime ? dateStr + " " + datetimeStr : dateStr;
}

function sanitizeTitle(title) {
    return title.replace(/[ ?!@;'\\.]/g, '-').toLowerCase().replace(/^[ -]+|[ -]+$/g, "");
}

/** Parameters */
const postDir = path.join("docs", "_posts");
const templateEngine = {
    name: "Mustache",
    dir: "templates",
    extension: ".mustache",
};

/** Parse arguments */

const usage = "Usage: node new-post.js <template-name> <title> [<title-for-filename>]";
const args = process.argv.slice(2, process.argv.length);
if (args.length != 2 && args.length != 3) {
    console.error("Invalid arguments")
    console.error(usage);
    process.exit(1);
}

const templatePath = path.join(templateEngine.dir, args[0] + templateEngine.extension);
const title = args[1];
const filetitle = args[2] ? sanitizeTitle(args[2]) : sanitizeTitle(title);
const postFilename = formatedateDate(new Date(), false) + "-" + filetitle + ".md";
const postPath = path.join(postDir, postFilename);

/** Main */

fs.readFile(templatePath, 'utf8', function (err, templateData) {
    if (err) throw err;

    // render the template with mustache.
    const renderedData = mustache.render(templateData, {
        title: title,
        date: formatedateDate(new Date(), true),
        overview: title + "についての記事です。",
    });

    // write a markdown file out.
    fs.writeFile(postPath, renderedData, {
        flag: 'wx'
    }, function (err) {
        if (err) {
            throw err;
        }
        console.log("Created a new post at: " + postPath)
    });
});
```

# テンプレートの実装

`templates/<template-name>.mustache` にテンプレートを置いていきます。

```mustache:base.mustache
---
title: "{{ title }}"
date: {{ date }}
category: その他
tags:
  - タグ
---

{{ overview }}

<!-- more -->

# {{ title }}

[[toc]]

## はじめに
```

`new-post.js` 内の以下の部分が、テンプレートに値を代入して markdown をレンダリングする処理に相当します。

```js
    // render the template with mustache.
    const renderedData = mustache.render(templateData, {
        title: title,
        date: formatedateDate(new Date(), true),
        overview: title + "についての記事です。",
    });
```

# 使い方


```bash
$ node new-post.js <template-name> <title> [<title-for-filename>]

# e.g.
$ node new-post.js base "はじめての Node.js" getting-started-with-nodejs
```