Closed akabekobeko closed 3 years ago
以下のドキュメントでは title
と theme
が実装され、他は未実装となっている。
Vivliostyle Flavored Markdown: Working Draft - VFM: Vivliostyle Flavored Markdown
title
と theme
に対するテストコードを追加author
と class
を実装するか検討既に create-book が生成する Markdown の雛形へ Frontmatter の title
と theme
が出力されている。本機能が有効となっている前提なのだろう。そのため優先度はあがる。
また本機能のために利用している remark-frontmatter
は remark 13 以降にも対応しており VFM v2.0 へそのまま継承可能なためリスクが小さいを判断した。
title
についてテストコードを書いて実行したところ、本機能は未実装のようだ。remark-frontmatter
は組み込まれているので読み取った結果を rehype で処理する実装が必要。
metadata.ts
に Frontmatter を処理している部分があるので console.log
を仕掛けてみた。
visit<FrontmatterContent>(tree, ['yaml'], (node) => {
const value = yaml(node.value);
console.log(value)
if (typeof value === 'object') {
console.log(file.data)
file.data = {
...file.data,
...value,
};
console.log(file.data)
}
});
テストコードに
---
title: "Title"
theme: "MyTheme"
class: "test"
---
を指定した状態で実行してみると以下の出力を得られた。
PASS tests/block/frontmatter.test.ts
● Console
console.log
{ title: 'Title', theme: 'MyTheme', class: 'test' }
at src/plugins/metadata.ts:23:13
console.log
{}
at src/plugins/metadata.ts:25:15
console.log
{ title: 'Title', theme: 'MyTheme', class: 'test' }
at src/plugins/metadata.ts:30:15
Frontmatter 解析と file.data
への設定は実装されている。よって rehype 部分にこれを伝搬して処理できれば本機能へ対応できそうだ。
metadata.ts
に hast
関数を実装して mdast
関数と対になるようメタデータを処理することにする。この関数を rehype-document
後に仕掛けて HAST を加工することで Frontmatter を HTML に反映できるはず。hast
関数に VFile
が伝搬されることも確認済み。
ここまで調べられたので Frontmatter とそのメタデータ反映は v1.0 対応としたい。
以下に公開している Frontmatter について
HTML へどのように反映させるかまとめておく。
Property | Type | HTML | 備考 |
---|---|---|---|
title | String | <title>タイトル</title> |
Frontmatter に未指定なら Markdown で最初に定義された # を参照する。 |
theme | String | 不明 | vivliostyle-theme と紐付けるためのタグを出力する予定だったらしいが、詳細不明。どのような出力にすべきか他のメンバーと検討が必要。 |
author | String | <meta name="author" content="著者名"> |
そのまま meta タグを生成すればよい。 |
class | String | <body class="my-class"> |
Markdown の対応するコンテンツ単位に CSS クラス名を出力する。 |
toc | Boolean | なし | ドキュメント未掲載だが内部処理として参照されているため、それを理解するまでは残す。 |
追加の提案として数式の書式 #37 がある。Frontmatter に math
を追加して数式の解析を制御しようというもの。
@MurakamiShinyu @yamasy1549
vivliostyle/themes に関わる Frontmatter プロパティーとして theme
があります。これが指定された場合、どのような HTML を出力すればよいでしょうか?
考えられるものとしては
<link href="theme.css" rel="stylesheet">
<body class="theme-name">
<body>
でなくともよいが、ルートに近い部分で CSS によるテーマ分岐しやすいタグでしょうか。個人的には create-book とそこから利用される cli で theme の仕組みは十分なので、VFM としてこのプロパティーに対応しなくてもよいと考えています。
個人的には create-book とそこから利用される cli で theme の仕組みは十分なので、VFM としてこのプロパティーに対応しなくてもよいと考えています。
よいと思います!
@yamasy1549 ありがとうございます。
@MurakamiShinyu @yamasy1549
補足です。Frontmatter から theme
を削除したとしても class
が Markdown 単位の <body>
に対して CSS クラスを設定する予定ですので、
class
となります。よって theme
は仕様から削除します。残したほうがよい場合は用途 (出力したい HTML) も添えてコメントをお願いします。
theme
以外は前述の表を参考にしてください。math
について #37 で結論が出たらそれも追加するかもしれません。
議論中の math
以外を手元で実装してみたが title
に問題あり。文書のタイトルは VFM のオプションからも指定可能。よってこれと Frontmatter のどちらを優先するか決める必要がある。
処理順としては
rehype-document
によってオプション指定があれば <title>
を設定title
または #
が定義されていれば <title>
を設定 となる。これに準じて Markdown 側の指定で後勝ちにしてもよさそうに思える。
Vivliostyle CLI の側では vfm の frontmatter から title と theme を取得するようになってるようです(不要になる?) 次のところあたり:
https://github.com/vivliostyle/vivliostyle-cli/blob/main/src/markdown.ts
https://github.com/vivliostyle/vivliostyle-cli/blob/main/src/config.ts#L268-L270
CLI 側でも Markdown を解析しているのですね。そうなると仕様のすり合わせが必要です。本件の実装にて VFM は以下のようにしようとしています。これを踏まえて @MurakamiShinyu さんと @spring-raining さんに意見をお願いしたいです。
title
が指定されていれば rehype-document
により <title>
を設定title
または #
が定義されていれば <title>
を設定、1 で処理されたものは上書き (後勝ち、Markdown 勝ち)Working Draft にオプションとの優先度が言及されていないため、仕様が確定 (↑でなくオプション勝ちでもよい) したら改めて記載予定。
theme
が指定されていても VFM として処理なしとりあえず PR は待機しておきます。
Frontmatter について VFM は処理しないが CLI などのために定義してもよい、とする方針もありえますね。
ただその場合でも「Markdown 書式」としての VFM ドキュメントでもある Working Draft には記載しておきたいです。記載のないものが登場するとユーザーに不安を与えますし、開発者としても考慮から外れてバグを埋め込む原因にもなりますので。
特に異論もないようですので title
の優先度は以下のようにします。
title
#
title
ドキュメント記載したものも含めて後ほど PR します。
v1.0.0-alpha.18 にて theme
と math
以外のプロパティー対応を実装しました。
math: boolean
と CLI オプション --math
(指定時 true
) を実装することになった。振る舞いは #37 のコメントを参照のこと。Frontmetter 用の Markdown 解析を実行してメタデータ取得してから、その内容を踏まえて処理するようにした。本来は MDAST として Frontmetter が最速実行され、これが生成した VFile.data
を読むようにしたかったが少なくとも remark 13 未満 (現行) の tokenizer からはこれを読めないようだ。
設計としては stringify
で処理しているが、これは VFM
へ移動したほうがよいかもしれない。
vivliostyle-cli では VFM
関数経由で VFile
を取得してメタデータを読んでいる。VFM 内でも math
処理判定のため同様の事前処理が必要となった。
これらを踏まえ Frontmetter から読み込んだメタデータを型付けして返す公開関数を用意してもよさそうだ。Processor もフル機能は要らないので Frontmetter 処理に必要な最小構成とする。とここまで書いて思い出したがメタデータ読み込みを VFM
ではなく stringify
で処理したのは Frontmetter のために Markdown が必要なためであった。よって VFM
ではこれを処理できない。するとしたら unified エコ システム内で完結する必要あり。
開発者会議 May 2021 で村上さんから挙げられた提案。
HTML のメタデータは基本的に <meta>
で指定されるのだから Frontmatter にこれとそのまま対応するプロパティーを追加するのはどうか。
---
meta:
- name: description
content: text
- name: author
content: akabeko
---
to JSON (JavaScript Object 参考用)
{
"meta": [
{
"name" : "description",
"content": "text"
},
{
"name" : "author",
"content": "akabeko"
}
]
}
to HTML
<meta name="description" content="text">
<meta name="author" content="akabeko">
利点は現在 author
を設定しているが、こうした <meta>
系をすべてこれで対応可能なため、個別に Frontmatter プロパティーを増減しなくて済む。author
を特別扱いで残すとしても、内部処理としては <meta>
を処理する関数に渡すデータにそれを含めれば独立した処理としなくてよい。
HTMLの <meta>
要素の構文のうち、MarkdownのFrontmatterで表現できる必要があるのは <meta name="..." content="...">
だけでよいと思います。したがって、nameをキーに、contentを値とするオブジェクトで表すことでより簡潔に書けると思います。
例:
lang: ja
title: 走れメロス
meta:
author: 太宰治
date: 1940-03-23
description: 古伝説とシルレルの詩から
keywords: メロス, 暴君ディオニス, セリヌンティウス
↓ HTML出力結果
<html lang="ja">
<head>
<title>走れメロス</title>
<meta name="author" content="太宰治"/>
<meta name="date" content="1940-03-23"/>
<meta name="description" content="古伝説とシルレルの詩から"/>
<meta name="keywords" content="メロス, 暴君ディオニス, セリヌンティウス"/>
著者が複数の場合など、値が配列になっている場合には、同じ name で複数の meta タグを出力するとよいでしょう:
meta:
author:
- Karl Marx
- Friedrich Engels
↓
<meta name="author" content="Karl Marx"/>
<meta name="author" content="Friedrich Engels"/>
<meta name="..." content="...">
と同様に <link rel="..." href="...">
も必要です。
(https://wiki.whatwg.org/wiki/MetaExtensions では、標準的な metadata の名前が定義されてますが、 <link rel="schema.dc" href="http://purl.org/dc/elements/1.1/">
など link 要素を合わせて使うものとされているものが多い)
例:
link:
schema.dc: "http://purl.org/dc/elements/1.1/"
stylesheet:
- "common.css"
- media: print
href: "print.css"
meta:
dc.modified: 2021-05-11
このように link 要素に name と href 以外の属性も付加する場合には、 {media: print, href: "print.css"}
のように指定できるとよいです。
root: {…}
でルート (<html>
) 要素の属性、body: {…}
で <body>
要素の属性を指定できるとよいです。
例:
root:
lang: ar
dir: rtl
class: arabic-site
body:
class: abstract
role: doc-abstract
↓ HTML出力結果は
<html lang="ar" dir="rtl" class="arabic-site">
<head>...</head>
<body class="abstract" role="doc-abstract">
Note: lang
はとても重要な属性なので root: {…}
に入れないで直接トップレベルに記述しても同じことになること。
両方に lang
があり値が合っていない場合はどちらを優先?
dir
も同様にする? RTL言語の lang を指定したら同時に dir: rtl も必須なので、その必要がありそう。
追記2021-05-14: lang, dir のほか、CSS組版スタイルシートではルートの id と class を使うのが便利なので、land, dir, id, class を root: {…}
に入れないで直接トップレベルに記述してもルートの属性になるようにしたい。
最初の提案で root-attributes
, body-attributes
としてたのは root
, body
に直しました。
Pandoc の header-includes
と同様、HTML の <head>
要素に含める HTML 要素をそのまま記述することができるようにする。
例:
header-includes: |
<style>
blockquote {
font-style: italic;
}
tr.even {
background-color: #f0f0f0;
}
td, th {
padding: 0.5em 2em 0.5em 0.5em;
}
tbody {
border-bottom: none;
}
</style>
次のように複数の HTML 要素の指定も可能に:
header-includes:
- <meta property="og:site_name" content="My Site">
- <script src="myscript.js"></script>
HTML 主体で考えると現在の math
や vivliostyle-cli が利用している theme
はルートではなく VFM の動作設定ということで
config:
math: true
theme: "@vivliostyle/theme-slide"
partial: false
disableHtmlFormat: false
のような感じで config
にまとめたいですね。これは #98 とも関係します。
Markdown 原稿を書く立場からすると、lang: …
や title: …
は Frontmatter のトップレベルに書けるのに author: …
や date: …
は meta:
のあとにインデントして書かなければならないというのは不便な感じがする。
lang: en
title: Hello World
meta:
author: Foo Baa
date: 2021-05-15
と書くよりも、
lang: en
title: Hello World
author: Foo Baa
date: 2021-05-15
のほうが、書きやすい。
そこで meta:
を書かなくてもよいように、次の規則をもうけてはどうだろうか?
meta: …
で指定されているのと同様に HTML <meta>
要素を出力する実際に採用するかは継続議論するとして、現時点で挙げられているプロパティーをまとめてみます。
Property | Type | Description |
---|---|---|
root | Object |
<html> の属性を Key/Value 形式で定義。 |
body | Object |
<body> の属性を Key/Value 形式で定義。class プロパティーはルートからこちらへ移動。 |
link | Array<Object> |
<link> の属性を Key/Value 形式で定義したものの配列。 |
script | Array<Object> |
<script> の属性を Key/Value 形式で定義したものの配列。スクリプト直書きは禁止してファイルのリンクに限定するほうがよいと考えている。 |
config | Object |
VFM 自信の設定。math と theme をこちらへ移動予定。VFM オプション disableHtmlFormat なども廃止してここで定義する方式にしたい。関連 #98 |
その他 | String |
<meta> として扱われる。Key = name 、Value = content に対応。 |
その他を <meta>
扱いすると定義済みの名前を name
とできない制限あり。
型を検討する場合に便利なサイト。JSON と YAML を相互変換してくれる。
実際に採用するかは継続議論するとして、現時点で挙げられているプロパティーをまとめてみます。
ありがとうございます。
その他を
<meta>
扱いすると定義済みの名前をname
とできない制限あり。
はい、そこで、 meta
プロパティーも残して定義済みの名前を name
とする必要があるときは meta: …
で記述できるようにするとよいと思います。
その meta
と link
について、Type を Array<Object>
とするか、それとも Object
としてその Key/Value の Value が Array の場合には同じ Key の複数の <meta>
要素を出力するようにするか、迷ったのですが、
- Frontmatter 項目として定義されていない名前が Frontmatter のトップレベルにあった場合は、
meta: …
で指定されているのと同様に HTML<meta>
要素を出力する
の場合に、同じ name の <meta>
要素を複数記述するときに、
---
author:
- Taro
- Hanako
---
と書けるようするとなると、 meta: …
を使った形式では
---
meta:
author:
- Taro
- Hanako
---
と書くのが自然です。
そこで、 Type を Array<Object>
とする案は取り下げて、Type は Object
としてその Key/Value の Value が Array の場合には同じ Key の複数の <meta>
要素を出力するとしたいと思います。
それから、 定義済みのプロパティとして https://github.com/vivliostyle/vfm/issues/76#issuecomment-838504296 の lang, dir, id, class、それから header-includes と title
指摘を受けて再定義。
Property | Type | Description |
---|---|---|
root |
Object |
<html> の属性を Key/Value 形式で定義。 |
body |
Object |
<body> の属性を Key/Value 形式で定義。従来の <body> 用 class プロパティーはここで代替。 |
link |
Object |
<link> の属性を Key/Value 形式で定義。 |
script |
Array<Object> |
<script> の属性を Key/Value 形式で定義。スクリプト直書きは禁止してファイルのリンクに限定するほうがよいと考えている。 |
lang |
String |
<html lang="..."> に対応。 |
dir |
String |
<html dir="..." に対応。"dir"ectory ではなく "dir"ection、テキストの書字方向 ltr や rtl などを定義すためのもの。 |
id |
String |
<html id="..."> に対応。 |
class |
String |
<html class=""> に対応。 |
title |
String |
<title> に対応。 |
header-includes |
String |
Pandoc の同プロパティーに対応。HTML の <head> 内を直に複数行テキストとして定義できる。 |
config |
Object |
VFM 自信の設定。math と theme をこちらへ移動予定。VFM オプション disableHtmlFormat なども廃止してここで定義する方式にしたい。関連 #98 |
meta |
Object |
プロパティーが <meta> の Key = name 、Value = content に対応。「その他」で既存のルート プロパティーと競合する場合はこちらに定義する。 |
その他 | String |
<meta> として扱われる。プロパティー名 = name 、値 = content に対応。 |
私の見解。
Frontmatter の抽象化するものがわかりにくい。プロパティーが 5 ぐらいまでなら気にする必要もなかった。しかし
<meta>
として扱うdir
や class
のように単体では所属を想像にしにくいものが追加された未定義を <meta>
として扱うなら Frontmatter は基本的に <meta>
の抽象で、例外的に title
や config
があるというのも納得できる。しかし <html>
に属するものも追加されて非直感的になった。title
など重要度や利用頻度の高いものをルートに置くというのは理解できるものの、暗黙知の複雑化を招いている。
この課題と各種命名を見て考えたこと。
Object
プロパティーにして属性や値はその子として定義するtagHtml
や tagMeta
のような命名にする
config
だけなら十分に明示的なので不要<html>
に対応するのは root
ではなく html
か tagHtml
にする
<html>
以外がルートになることもあるなら不要partial
オプションを指定した場合は <html>
ではなく部分 HTML となるがこれを想定しなくてよいのか?partial
は主に開発者が Markdown の部分 HTML 変換を試すのに使われるため想定しなくてもよいとは思う<html>
がルートにある状態なので、これを表す命名のほうが明示的で好ましいのではないか?header-includes
が相当するheader-includes
は反対
meta
などと被るので順番を決める必要がある<meta>
や <title>
) を複数の方法でサポートすることに抵抗がある複雑化、混乱を感じるのは
が要因。ルートをタグと config
のみにすることで
config
のみとなり、それ以外 (属性など) はないことが保証される
<html>
、<title>
、<meta>
、<body>
、<script>
、<link>
<body>
以上の抽象とみなせるdir
や class
のような名前があっても親が html
ないしは tagHtml
であることによりコンテキストができて、類推しやすくなるというメリットがある。デメリットは村上さん提案メリットの裏返しで lang
のような重要なものをルートに書けないため、単純なメタデータだけあればよいユーザーとしては定義しにくくなる。
ただしこのデメリットについてはリファレンスにテキストとしてサンプル掲載すればある緩和できるのではないか。typo などを考慮するとゼロから手打ちではなくリファレンスやサンプルからコピペされ、そこから差分編集されるのではなかろうか。
ルートを既定タグと confing
だけにした場合を議論するための YAML 参考例。厳し目のルールなので前述の tagNAME
にはしていない。
config
としていたが VFM 固有であることを明示するために vfm
とした。このような命名のタグが <body>
以上の層へ定義されることはないだろう。config
もないとは思うが、より安全で VFM 専用なことを明示するためそうしてみた。
html:
lang: "ja"
class: "sample"
dir: "ltr"
id: "root"
title: "Sample Document"
meta:
author: "akabeko"
sample: "contents"
link:
- rel: "stylesheet"
type: "text/css"
href: "sample.css"
- rel: "stylesheet"
type: "text/css"
href: "sample2.css"
script:
- src: "sample.js"
defer: "defar"
- src: "sample2.js"
body:
class: "sample"
vfm:
math: false
partial: false
disableHtmlFormat: true
theme: "@vivliostyle/theme-techbook"
author
や <body>
に対する class
などが階層化される点は不便になる。ただし直に vivliostyle-cli を利用するユーザーには前述のようにサンプルを提示し、create-book 想定ならテンプレートの Markdown で主要なものを事前定義しておいて穴埋め式にすることでエンド ユーザーの負担を軽減できる。
vivliostyle-cli 向けの話。↑の案でゆく場合、VFM 固有設定は vfm
、<body>
以上の既定タグについてはそれ以外を読み込んでもらえばよい。現在は独自に remark で解析して VFile
を得ているが、どの意見を採用するのであれ vivlistyle-cli など VFM を npm API として参照するもの向けに Frontmatter を TypeScript で型付けしたデータを返すreadMetadata
or readFrontmatter
関数を export
する予定。
こうすることで vivliostyle-cli の dependencies
から VFM と被る unified や remark 参照を消せる。競合による問題を避ける意味でも有用なはず。
現在の VFM には style: string: string[]
というオプションがあってこれは rehypejs/rehype-document の style
にそのまま指定される。しかし Frontmatter で link
をサポートするならここで CSS 参照を定義できるし、より詳細な属性も定義可能だから廃止してよさそう。
ここで議論している Frontmatter 拡張案は、次の両方の意味があると考えています:
文書のメタデータということを中心に考えると、言語、タイトル、著者、日付、といった情報(文書メタデータ)は、それが HTML ではどう指定するか(要素の属性か、専用の要素か、meta 要素か)に関わらず、同じ形式(Key: Value)で指定できることが望ましいです。
これらの文書メタデータは、HTML 出力だけではなく、より汎用的に、EPUB 出版物のメタデータ や Web 出版物の Publication Manifest のメタデータなどを出力するのにも使えればよいと考えています。
したがって、まず基本は「Key: Value」形式で文書メタデータを記述できるようにして、HTML 出力では次の方針とする考えです:
このために、
- HTML の要素と属性の混在
という結果になってます。
たしかに「Key: Value」の指定で、HTML の <meta>
タグで出力されるのかどうかが分かりにくいのは問題です。
そこで、
ルートをタグと
config
のみにすることで
- ルートには HTML タグと
config
のみとなり、それ以外 (属性など) はないことが保証される
の案に、 lang
, dir
, id
, class
だけを追加するのでどうでしょうか?
ルート要素の id
と class
は、その文書の ID と文書の種類ごとのクラスを指定するものです。複数の文書で同じスタイルシートを使う場合に、スタイルシートの中で特定の文書 ID や特定の文書クラスに適用するスタイルを使えるようにするために重要です。(VFM の元の Frontmatter 案の class
は <body>
の class 属性を指定するものでしたが、ルート要素の class 属性を指定できるほうが CSS 組版のためにより有用です。)
Frontmatter のルートのプロパティ(Key)として定義するは HTML タグと config
と lang
, dir
, id
, class
に制限する、そして、定義されていない Key が指定されたら HTML <meta>
タグで出力するというのがよいと思います。
ここで「HTML タグ」は以下のもの:
html
head
body
title
meta
link
base
style
(HTML の先頭部分で body 開始タグまでに出現するものをリストアップしたもの。head
は不要かもしれない)
以下はこれまで提案してなかったもの:
HTML base 要素
例:
base: "https://example.com"
↓
<base href="https://example.com"/>
base:
href: "https://example.com"
target: "_blank"
↓
<base href="https://example.com" target="_blank"/>
HTML style 要素
例:
style: |
p {
color: #26b72b;
}
↓
<style>
p {
color: #26b72b;
}
</style>
現在の VFM には
style: string: string[]
というオプションがあってこれは rehypejs/rehype-document のstyle
にそのまま指定される。しかし Frontmatter でlink
をサポートするならここで CSS 参照を定義できるし、より詳細な属性も定義可能だから廃止してよさそう。
vfm コマンドラインオプションの --style
は、Frontmatter でスタイルシートが指定されていなくてもスタイルシート指定付きの HTML を生成するために必要です。
オプションでのスタイルシート指定と Frontmatter でのスタイルシート指定の両方がある場合は、両方ともに HTML に出力し、その順番はオプションで指定されているものを先にするのがよいです。個別の原稿の Frontmatter に指定するスタイルシートは、その原稿用にカスタマイズするためのスタイルシートと考えられるためです。
Frontmatter での外部スタイルシートの指定の方法ですが、
link:
- rel: "stylesheet"
href: "style.css"
と書く必要があるとなると、この構文は複雑すぎな気がします。
しかし Frontmatter のルートのプロパティとして定義するのは HTML タグなどに制限する方針から、 stylesheet: "sample.css"
だけで指定できるようにするわけにもいきません。
また、link
での外部スタイルシートと style
での内部スタイルシートとの両方の指定があるとき、HTML 出力でそれらスタイルシートの順番を指定できないという問題もあります。たとえば、
link:
- rel: "stylesheet"
href: "style.css"
style: |
p {
color: blue;
}
と書いても、順番を逆に
style: |
p {
color: blue;
}
link:
- rel: "stylesheet"
href: "style.css"
と書いても、マップ(連想配列)の項目の順番は意味がないはずなので、HTML 出力は同じ結果になるべきでしょう。
一般に、外部スタイルシートで指定した内容を内部スタイルシートでカスタマイズすることが多いでしょうから、HTML への出力では link
で指定されたスタイルシートが先になるのがよいと思います。しかし、外部スタイルシートを内部スタイルシートのあとに指定できないという制限は不自由です。
そこで style
プロパティを内部スタイルシートだけでなく外部スタイルシートを指定するのにも使えるようにしたらよいと思います。
例:
style:
- href: "style.css"
- |
p {
color: blue;
}
- href: "print.css"
media: print
↓
<link ref="stylesheet" href="style.css"/>
<style>
p {
color: blue;
}
</style>
<link ref="stylesheet" href="print.css" media="print"/>
スタイルシートを1つだけ指定する場合は、配列にする必要はなくて次のように書けるとよいでしょう:
style:
href: "style.css"
style: |
p {
color: blue;
}
また、値が文字列である場合に、それが href の有効な指定になるものなら href (外部スタイルシート)の指定、そうでないならば内部スタイルシートの内容とみなすことができれば、
style: "style.css"
のように、よりシンプルな指定ができることになります。
内部スタイルシートの指定に属性(例: media
)を指定するにはどう記述するかは要検討です。
空文字列をキーにしてその値をスタイルシート内容にする?:
style:
media: screen
"" |
p {
color: blue;
}
":" をキーにする案(::
と書けるので書きやすい?):
style:
media: screen
:: |
p {
color: blue;
}
それとも、media の指定は次のように書けばよいのだから属性指定は不要?:
style: |
@media screen {
p {
color: blue;
}
}
間を空けてしまいましたが議論を継続しましょう。前述のように私はルート要素はタグだけとしたいのですが
author
や class
を公開しているため移動すると影響が大きそうmath
はデフォルト有効なので config
に移動しても影響は少なそうという理由から前述の村上さん案で実装してみます。
↑のように書きましたが、それでも実装を避けたいプロパティーがあります。合意、未合意を整理するため、それらを含め改めて表にまとめます。
私も賛成のもの。https://github.com/vivliostyle/vfm/issues/76#issuecomment-841824131 で挙げられていませんでしたが script
も追加してあります。
Property | Type | Parent | Description |
---|---|---|---|
vfm |
Object |
HTML とは対応しない VFM の設定。math と theme をこちらへ移動予定。関連 #98 |
|
html |
Object |
<html> の属性を Key/Value 形式で定義。lang などルートでも指定可能な属性はルート側を優先する。 |
|
lang |
String |
<html lang="..."> に対応。 |
|
dir |
String |
<html dir="..." に対応。"dir"ectory ではなく "dir"ection、テキストの書字方向 ltr や rtl などを定義すためのもの。 |
|
id |
String |
<html id="..."> に対応。 |
|
class |
String |
<html class=""> に対応。 |
|
body |
Object |
<html> |
<body> の属性を Key/Value 形式で定義。 |
title |
String |
<head> |
<title> に対応。 |
link |
Object |
<head> |
<link> の属性を Key/Value 形式で定義。順番制御のために Array<Object> としたほうがよいか。 |
script |
Array<Object> |
<head> |
<script> の属性を Key/Value 形式で定義。スクリプト直書きは禁止してファイルのリンクに限定するほうがよいと考えている。 |
base |
Object |
<head> |
<base> の属性を Key/Value 形式で定義。 |
meta |
Object |
<head> |
<meta> の name と content をそれぞれ Key/Value 形式で定義 (列挙)。「その他」で既存ルート プロパティーと競合する場合はこちらに定義する。 |
その他 | String |
<head> |
<meta> として扱われる。プロパティー名 = name 、値 = content に対応。 |
私としては反対 (未合意) のもの。
Property | Type | Parent | Description |
---|---|---|---|
head |
String |
<html> |
<head> 内容を複数行テキストとして定義。Pandoc の header-includes に相当する。 |
style |
String |
<head> |
<style> の内容を複数行テキストとして定義。 |
その理由。
head
と他の <head>
系について優先順位を決める必要があるhead
があると他の <head>
系の存在がぼやけてわかりにくいhead
は style
を兼ねられる、共に複数行テキストなので対象と目的が競合しているstyle
は link
で代替可能id
など一部プロパティー、meta
とその他の競合は優先度に影響しないため許容するが、これらは影響するうえ特に head
は他を兼ねられるのでやめたほうがいい特定のMarkdown原稿ファイルでその原稿内にだけ適用したいスタイルを定義したいという場合、Markdown原稿ファイル内にスタイルシートを書けると便利です。
現状の vfm でも(ほかの多くのMarkdown処理系でも)Markdownファイル内に <style>
要素でスタイルシートを入れることは可能で、その場合、HTML の <body>
要素内に <style>
要素が出力されることになります。HTML 的に正しくありませんが、すべてのメジャーブラウザでそのスタイルシートは有効です。Vivliostyle Coreでもそれを有効にしました (See: https://github.com/vivliostyle/vivliostyle.js/issues/655 )。
Markdown に <style>
タグでスタイルシートを入れることは、裏技的ですが、そのニーズがあって使われています。「Markdown CSS 埋め込み」でググるとそのようなノウハウが書かれた記事が見つかります。
このような裏技的な方法ではなく、HTML 的に正しく <head>
要素内に <style>
要素を出力するための frontmatter の記述方法が必要だと思います。
そのために head
(Pandoc の header-includes 相当)または style
プロパティーのどちらかがあるとよいと思います。
(まずは style
に絞ってもよいと思います)
以下、反対されている理由に対してコメントします:
head
と他の<head>
系について優先順位を決める必要がある
pandocの header-includes
と同様、head要素の最後に出力するのでよいでしょう。
head
があると他の<head>
系の存在がぼやけてわかりにくいhead
はstyle
を兼ねられる、共に複数行テキストなので対象と目的が競合している
Markdownは、HTMLコードを直接書くこともできるものを簡易記法で定義するものなので、 <head>
内のHTML要素にしてもHTMLコードを直接書く方法と、より簡潔な記法との両方があって問題はないと思います。
style
はlink
で代替可能
link
では外部スタイルシートしか指定できません。特定の原稿ファイル内だけに適用したいスタイルシートを別ファイルにしたくないケースは多いと思います。
id
など一部プロパティー、meta
とその他の競合は優先度に影響しないため許容するが、これらは影響するうえ特にhead
は他を兼ねられるのでやめたほうがいい
優先度については、前述のとおり(head
で指定されたものは最後に出力)。
たしかに style
や他の head 内 HTML 要素に対応するプロパティーがあれば head
の必要性はそれほどないかもしれません。
しかしMarkdown内にHTMLコードを直接書きたいことがあるように、<head>
内のHTMLコードをYAML形式に変換しないで直接書きたいケースはあると思います。
- 繰り返しになりますが複数行テキスト系には反対です
複数行テキスト系がダメな理由はなんでしょう?
複数行テキストに反対している理由は、それ自体というより YAML 内に HTML/CSS が直書きされるにも関わらず VFM としてはそれを検証できないためです。VFM としては YAML 処理までしか責任が持てず、もし HTML/CSS に構文エラーがあってもエラーなどは出せません。フリー スタイルで書けることによる想定外の問題も危惧しています。
...と自分で書いて思ったのですが、そもそも YAML 検証も remark プラグインに投げていますし他の処理もエラー ハンドリングを特にしていないのでユーザーの自己責任ということでよさそうですね。
ですので head
と style
間の優先順 (style
が後?) だけ決まれば合意ということで、これらも実装します。
style
対応するので link
は Array<Object>
にします。順番保証は必要なので Array
にしたいのと Object
をぶら下げることが複雑に感じられる (YAML だとそうでもないとは思いますが) なら style
で直書きしてもらう想定です。
以上を踏まえて改めて表を書き出します。これを実装の最終候補とする予定です。
Property | Type | Parent | Description |
---|---|---|---|
vfm |
Object |
HTML とは対応しない VFM の設定。math と theme をこちらへ移動予定。関連 #98 |
|
html |
Object |
<html> の属性を Key/Value 形式で定義。lang などルートでも指定可能な属性はルート側を優先する。 |
|
lang |
String |
<html lang="..."> に対応。 |
|
dir |
String |
<html dir="..." に対応。"dir"ectory ではなく "dir"ection、テキストの書字方向 ltr や rtl などを定義すためのもの。 |
|
id |
String |
<html id="..."> に対応。 |
|
class |
String |
<html class=""> に対応。 |
|
body |
Object |
<html> |
<body> の属性を Key/Value 形式で定義。 |
head |
String |
<html> |
<head> 内容を複数行テキストとして定義。Pandoc の header-includes に相当する。すべての <head> 系に対して末尾へ出力される。 |
title |
String |
<head> |
<title> に対応。 |
style |
String |
<head> |
<style> の内容を複数行テキストとして定義。すべての <head> 系に対して末尾、ただし head の直前へ出力される。 |
link |
Array<Object> |
<head> |
<link> の属性を Key/Value 形式の配列で定義。 |
script |
Array<Object> |
<head> |
<script> の属性を Key/Value 形式の配列で定義。 |
base |
Object |
<head> |
<base> の属性を Key/Value 形式で定義。 |
meta |
Object |
<head> |
<meta> の name と content をそれぞれ Key/Value 形式で定義 (列挙)。「その他」で既存ルート プロパティーと競合する場合はこちらに定義する。 |
その他 | String |
<head> |
<meta> として扱われる。プロパティー名 = name 、値 = content に対応。 |
head
と style
の順番ですが、自由に何でも書ける head
は最後にして style
はその前がよいと思います。
@MurakamiShinyu ↑↑の表に反映しました。本業多忙につき実装は今週の土日あたりからになりそうです。
またメタデータ周りの設計をけっこういじりそうなので PR が出るまで間があきそうです。そのためで表に問題などがあれば、ここで継続的に指摘や議論をお願いします。
ひととおり実装したが style
と head
について問題あり。メタデータに基づき hastscript で Markdown AST を生成しているが以下の問題あり。
AST として { type: 'html', text }
を指定すると "Cannot compile unknown node html
" というエラーになる。そのため hastscript で何らかのタグにする必要あり。
style
のインデントが無視されるh('style', text)
で読み込まれた自由書式テキストを指定してもインデントは無視され、改行のみが評価される。rehype-format を有効にしても整形の対象外。
YAML
style: |
@media screen {
p {
color: blue;
}
}
HTML
<style>@media screen {
p {
color: blue;
}
}</style>
head
を処理できないstyle
は一つの <style>
内に出力されるため、インデントの問題はあるものの対応可能。しかし head
は <head>
の末尾へそのまま出力する仕様だが、前述のようにプレーン テキスト中の HTML タグがエスケープされる問題により hastscript で単一タグとする必要がある。つまり仕様どおり複数タグ出力とはゆかない。
継続調査するが現在の仕様だと head
だけは対応できない。
以下の仕様を見て { type: 'html', text }
が通用すると考えていたが実際には "Cannot compile unknown node html" になる。
{ type: 'html', value: text }
とした場合Cannot compile unknown node `html`
at serialize (node_modules/hast-util-to-html/lib/one.js:24:11)
at all (node_modules/hast-util-to-html/lib/all.js:15:22)
at Object.serializeElement [as element] (node_modules/hast-util-to-html/lib/element.js:56:13)
at serialize (node_modules/hast-util-to-html/lib/one.js:27:24)
at all (node_modules/hast-util-to-html/lib/all.js:15:22)
at Object.serializeElement [as element] (node_modules/hast-util-to-html/lib/element.js:56:13)
at serialize (node_modules/hast-util-to-html/lib/one.js:27:24)
at Object.all [as root] (node_modules/hast-util-to-html/lib/all.js:15:22)
at serialize (node_modules/hast-util-to-html/lib/one.js:27:24)
at toHtml (node_modules/hast-util-to-html/lib/index.js:47:10)
h('', text)
とした場合<div>
扱いとなるtext
にすると AST の type: text
ではなく <text>
扱いとなる。<div>
<meta name="head1" content="head1">
<meta name="head2" content="head2">
</div>
style
の制限事項として YAML 的にインデントが必須。
style: |
@media screen {
p {
color: blue;
}
}
は OK だが
style: |
@media screen {
p {
color: blue;
}
}
はエラーになる。
YAMLException: end of the stream or a document separator is expected (30:1)
27 | - type: 'text/javascript'
28 | src: 'sample2.js'
29 | style: |
30 | @media screen {
------^
31 | p {
32 | color: blue;
106 | const mdast = () => (tree: Node, file: MetadataVFile) => {
107 | visit<FrontmatterContent>(tree, ['yaml'], (node) => {
> 108 | const value = yaml(node.value);
| ^
109 | if (typeof value === 'object') {
110 | file.data = {
111 | ...file.data,
at generateError (node_modules/js-yaml/lib/loader.js:183:10)
at throwError (node_modules/js-yaml/lib/loader.js:187:9)
at readDocument (node_modules/js-yaml/lib/loader.js:1645:5)
at loadDocuments (node_modules/js-yaml/lib/loader.js:1688:5)
at Object.load (node_modules/js-yaml/lib/loader.js:1714:19)
at src/plugins/metadata.ts:108:19
at overload (node_modules/unist-util-visit/index.js:27:12)
at visit (node_modules/unist-util-visit-parents/index.js:56:27)
at visit (node_modules/unist-util-visit-parents/index.js:67:75)
at visitParents (node_modules/unist-util-visit-parents/index.js:29:26)
@MurakamiShinyu
現状と見解をまとめます。一読の上、村上さんの意見をうかがいたいです。
CSS を定義する目的は達成可能です。ただしファイルを用意せず Frontmatter だけで簡易に CSS を定義する目的としてはイマイチだと思います。
YAML のインデントが必須であり、VS Code のような高機能エディターであっても YAML 内に CSS が記述されることを想定していないため入力補完を利用できません。
一方、ファイルであれば用意の手間こそあれど CSS ファイルとして VS Code など様々なツールの恩恵を受けられますし link
で順番保証しつつ定義可能です。本機能を議論した際にも書いていますが CSS 定義として見た場合は link
と機能が重複しますし、link
のほうが属性を自由に定義できる点で上位互換となります。
以上を踏まえると実装はしましたが採用にはあまり賛成できません。
style
と同様に YAML の制限あり<
がエスケープされて <
になる現時点では処理に成功していません。また style
と同様に Frontmatter で対応する機能としてイマイチに思えます。<head>
として必要な機能は meta
などのように個別プロパティーで対応するほうが好ましいのではないでしょうか。
複数行テキストに反対している理由は、それ自体というより YAML 内に HTML/CSS が直書きされるにも関わらず VFM としてはそれを検証できないためです。VFM としては YAML 処理までしか責任が持てず、もし HTML/CSS に構文エラーがあってもエラーなどは出せません。フリー スタイルで書けることによる想定外の問題も危惧しています。
という理由から自由書式 (複数行テキスト) に反対しており、しかし VFM としては YAML パーサーに処理を丸投げしているのでこの範疇で処理可能なら採用としました。
実際に style
は挙動と問題点がほぼ想定通りです。しかし他のプロパティーと機能的に重複しながら自由書式としての記述性と出力における再現性がイマイチな点を考慮すると、両プロパティーともやはり好ましくないと考えています。
参考としてローカルで実装したテストコード (head
を除く成功したもの) を掲載します。
it('All documents', () => {
const md = `---
id: 'my-page'
lang: 'ja'
dir: 'ltr'
class: 'my-class'
title: 'Title'
html:
data-color-mode: 'dark'
data-light-theme: 'light'
data-dark-theme: 'dark'
body:
id: 'body'
class: 'body'
base:
target: '_top'
href: 'https://www.example.com/'
meta:
sample-meta1: 'meta1'
sample-meta2: 'meta2'
link:
- rel: 'stylesheet'
href: 'sample1.css'
- rel: 'stylesheet'
href: 'sample2.css'
script:
- type: 'text/javascript'
src: 'sample1.js'
- type: 'text/javascript'
src: 'sample2.js'
style: |
@media screen {
p {
color: blue;
}
}
author: 'Author'
vfm:
math: false
theme: 'theme.css'
---
Text
`;
const received = stringify(md);
const expected = `<!doctype html>
<html data-color-mode="dark" data-light-theme="light" data-dark-theme="dark" id="my-page" lang="ja" dir="ltr" class="my-class">
<head>
<meta charset="utf-8">
<title>Title</title>
<base target="_top" href="https://www.example.com/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="sample-meta1" content="meta1">
<meta name="sample-meta2" content="meta2">
<meta name="author" content="Author">
<link rel="stylesheet" href="sample1.css">
<link rel="stylesheet" href="sample2.css">
<script type="text/javascript" src="sample1.js"></script>
<script type="text/javascript" src="sample2.js"></script>
<style>@media screen {
p {
color: blue;
}
}</style>
</head>
<body id="body" class="body">
<p>Text</p>
</body>
</html>
`;
expect(received).toBe(expected);
});
style
で p > q
のようなセレクタが使えない( p > q
のように変換されてしまう)というのが問題ですね。
head
と style
について、今回は採用しなくてもよいだろうと思います。
将来また再検討ということでよいでしょう。
HTML仕様でexternal stylesheet(<link rel=stylesheet>
)とinternal stylesheet(<style>
)の両方のしくみがあるように、一方があるからもう一方は不要、という関係ではないと思います。
承知しました。style
と head
は将来検討にします。
一方があるからもう一方は不要、という関係ではないと思います。
こちらについても了解しました。ドキュメント込みで本日中に PR を出す予定です。
I proposed it in #1, but I will reprint it because there is no individual issue.
For english:
Frontmatter
Propose Frontmatter as a method of defining metadata in Markdown (file) units.
Purpose
<body id="author">
, to make CSS selector branching easier.Format
The format follows the famous static site generator.
These are written at the top of Markdown mainly in YAML format (Hugo becomes TOML when defined with
+++
).For properties, we suggest the following: Minimal because it is a draft.
#
can be a title, but this is not always defined, so I want an explicit title.