vivliostyle / vfm

⬇️ Open and extendable Markdown syntax and toolchain.
https://vivliostyle.github.io/vfm/#/vfm
Other
69 stars 12 forks source link

spec: Frontmatter for third party tools #141

Closed akabekobeko closed 2 years ago

akabekobeko commented 2 years ago

Goals

Provides Frontmatter properties for third party tools.

VFM's Frontmatter processes undefined properties as meta tags. In this case, the value becomes a string and the type information is lost.

For example, in vivliostyle-sitegen, want to handle various types of data such as dates and tags in Markdown units. Therefore, I want to maintain non-string types.

The root property is a single property, and arbitrary structures can be defined within it. The property name is tools because we assume third-party tools.

---
tools:
  - date: "20220119"
  - categories: ["Development"]
  - tags: ["Markdown", "Frontmatter"]
---
akabekobeko commented 2 years ago

この値があると VFM の readMetadata と組み合わせたプリプロセス的な処理に役立つ。例えば date へ日時を定義しておき、それに基づいて記事一覧をソートするなど。

開発中の vivliostyle-sitegen では HTML テンプレートに EJS を採用している。このツールではプロパティーに基づく分岐やループも可能なので、この値を受け渡すことで Markdown 単位の柔軟なカスタマイズも可能。vivliostyle.org の記事、例えばブログのタグなどを vivliostyle-sitegen で実現するためにも必要な機能となる。

MurakamiShinyu commented 2 years ago

プロパティ名が tools なら、その値はツール名がキーになっているのが自然な気がします。つまり、


---
tools:
  vivliostyle-sitegen:
    date: "20220119"
    categories: ["Development"]
    tags: ["Markdown", "Frontmatter"]
---
akabekobeko commented 2 years ago

確かにそうですね。実際には複数ツールから同時にホストされるのは稀だとは思いますが、そのようにしておくと宣言そのものが自己言及的になる点も好ましいですね。

デメリットとしては階層が深くなること。しかし同一ツールで複数 Markdown を書くなら定形をコピペして書き換える運用が想定されるため、あまり気にしなくてもよさそうではあります。

akabekobeko commented 2 years ago

プロパティー階層を単一にするなら...

という感じですかね。

akabekobeko commented 2 years ago

宣言のわかりやすさだと村上さん案がよさそう。

実際には複数ツールから同時にホストされるのは稀だとは思いますが

と書いたが、もし複数ツール間で共有したいという要望があっても満たせるし、単一ツール内でもバージョン処理に有用そう。例えば foobar というツールについてメタデータの破壊的な変更があった場合、過去の foobar をそのままに foobar-v2 を追加することで両バージョンへ同時に対応可能となる。

akabekobeko commented 2 years ago

接頭辞案でも同様の対応は可能だが、プロパティー名の中に入れ子構造があるというのは直感的ではない。

MurakamiShinyu commented 2 years ago

もうひとつの考え:

現状のvfmで次のFrontmatterのとき、

---
date: "20220119"
categories: ["Development"]
tags: ["Markdown", "Frontmatter"]
---

次の出力になります:

<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="date" content="20220119">
    <meta name="categories" content="Development">
    <meta name="tags" content="Markdown,Frontmatter">
  </head>
  <body></body>
</html>

このようにmetaタグに出力されても、とくに困ることはないようにも思えます。 ツール側は、HTMLのmetaタグに出力されるのとは関係なしに、Frontmatterの情報を使うことができるでしょう。 値がobjectの場合(現状ではmetaタグには [object Object] のように出力される)はmetaタグには出力しないほうがよさそう。

あるいは、ツールの側からvfmに、ツールが特別扱いするキーを指定できるようにして、そのキーはmetaタグに出力しないというのでもよいかも。

akabekobeko commented 2 years ago

vivliostyle-sitegen としては当初、まさに meta をそのまま利用するつもりでした。しかし [object Object] 問題と Array がカンマ区切り文字列となることで、それをツール側で split しなければならないのが面倒で本件を提案してみました。レアケースですがカンマ区切りの要素にカンマが含まれる場合のエスケープも検討する必要があります (曲名や書籍名などをタグにすると意外に遭遇しそう)。

ただ meta としてそのまま処理する案も <meta> としてメタデータ出力されることが逆にメリットとも言えそうで捨てがたいのですよね。

あるいは、ツールの側からvfmに、ツールが特別扱いするキーを指定できるようにして、そのキーはmetaタグに出力しないというのでもよいかも。

この方法もよいですね。ツールとしては自身の名前をそのまま除外指定して

---
 vivliostyle-sitegen:
  date: "20220119"
  categories: ["Development"]
  tags: ["Markdown", "Frontmatter"]
---

とするか、標準プロパティーと衝突しないのを前提として個別に除外指定して

---
date: "20220119"
categories: ["Development"]
tags: ["Markdown", "Frontmatter"]
---

ともできます。↑については他の Markdown 対応 SSG ツールから移行する際にもよさそうです。

akabekobeko commented 2 years ago

もし

あるいは、ツールの側からvfmに、ツールが特別扱いするキーを指定できるようにして、そのキーはmetaタグに出力しないというのでもよいかも。

へ対応するとしたら用途的にツール (プログラム) 想定なので VFM としてのオプションではなく readMetadata に限定したものとしたほうがエンド ユーザーを混乱させることもなくよさそう。

akabekobeko commented 2 years ago

影響範囲やドキュメント整備などを考慮して、本件は村上さん案の「meta として無視するキー指定」を採用することにした。

readMetadata API に meta 処理から除外するキー名コレクションを指定可能として、それに該当するものが検出されたら remark-frontmatter が処理した JavaScript のデータをそのまま維持する。VFM の処理は

  1. metadata.tsreadMetadataremark-frontmatter の結果を HTML 出力用のデータ型に整形
  2. document.tscreateHTML とそのサブルーチンで 1 の値をキー毎に HTML 化

となっている。1 で未知のキーは meta として処理されるのを除外指定でスキップすればよい。2 では想定されるキーのみ処理されるため、未知側がどのようになっていても影響しない。

akabekobeko commented 2 years ago

Metadata[key: string]: any; を追加することで無視した任意の値をキーにより代入可能となる。ただしこの方法だと既知のプロパティー限定チェックが効かなくなってしまうため、

export type Metadata = {
  // ...前略

  excludes?: {
    [key: string]: any;
  }
}

として excludes に除外したものをまとめることにした。readMetadata で除外指定するための引数も同名で対応づけられる。

akabekobeko commented 2 years ago

VFM v1.2.0 へ反映したので close します