Follow-up to #82.
Problem
Whether a front matter entry's value is known is a fact of the YAML parse,
yet markanywhere-html rebuilds it from the shape of the event stream in FrontMatterEntryReader:
text at the frontmatter root that starts with indentation continues the entry before it,
text inside an entry is its scalar value,
and a verbatim line opening with a key defines an entry.
The shape is ambiguous, so every new verbatim shape needs another special case there and in EnsureFrontmatterTitle (opensWithVerbatimLine, keepingFirstSlot).
Several review rounds on #82 kept finding new shapes rather than new classes of bug.
Proposed fix
Have YamlParser mark an entry whose value is incomplete or verbatim
(e.g. an attribute on the entry mark),
so FrontMatterEntry.isHeadMetadata becomes a plain check and the html module stops guessing.
Symptoms (on the #82 branch)
The PR for #82 closes 1–3 conservatively:
the reader treats such an entry's value as unknown (no <title> / <meta>, kept as content by ensureFrontmatterTitle) instead of reading a wrong one.
The heuristic is "text inside an entry that starts with indentation and ends with \n", so a quoted scalar of that exact shape (k: " x\n") is also read as unknown.
- A plain scalar continued on indented lines was read as its first line only:
title: A long\n title → <title>A long</title> (readers: "A long title").
- A verbatim line under a bare
key: was read as the value:
title:\n [a:, b] → <title>[a:, b]</title> (readers disagree on this shape).
- Indented verbatim lines under a bare
key: leaked their indentation into the value:
description:\n first line\n second → content=" first line\n second\n".
- Still open: the reader assumes one text event per YAML line.
After mergeAdjacentText a verbatim title: line merged into a preceding verbatim line is missed,
so ensureFrontmatterTitle derives a second title: — a duplicate key js-yaml (Eleventy, Astro, Gatsby) rejects, losing the whole front matter.
Other known gaps (smaller, same area)
opensWithVerbatimLine treats any verbatim line as blocking front matter detection, including a key-shaped one isYamlKeyLine accepts,
so ---\nTitle:\nbar: [a:, b]\n--- keeps the empty Title: for no reason.
keepingFirstSlot can move an entry left unclosed (stream truncated inside the frontmatter) to the first slot;
closed() then balances by count, nesting the following entries inside it.
closed() pops its open-mark stack on any Unmark without checking the name,
so a crossed upstream stream (mark h1, mark em, unmark strong) gets the wrong closers appended.
!opensWithVerbatimLine(emptyMap(), emptyList()) recomputes a property of the closed frontmatter on every flush;
computing it once in judgeFrontmatter would also make the rewrite condition easier to follow.
Follow-up to #82.
Problem
Whether a front matter entry's value is known is a fact of the YAML parse,
yet
markanywhere-htmlrebuilds it from the shape of the event stream inFrontMatterEntryReader:text at the frontmatter root that starts with indentation continues the entry before it,
text inside an entry is its scalar value,
and a verbatim line opening with a key defines an entry.
The shape is ambiguous, so every new verbatim shape needs another special case there and in
EnsureFrontmatterTitle(opensWithVerbatimLine,keepingFirstSlot).Several review rounds on #82 kept finding new shapes rather than new classes of bug.
Proposed fix
Have
YamlParsermark an entry whose value is incomplete or verbatim(e.g. an attribute on the
entrymark),so
FrontMatterEntry.isHeadMetadatabecomes a plain check and the html module stops guessing.Symptoms (on the #82 branch)
The PR for #82 closes 1–3 conservatively:
the reader treats such an entry's value as unknown (no
<title>/<meta>, kept as content byensureFrontmatterTitle) instead of reading a wrong one.The heuristic is "text inside an entry that starts with indentation and ends with
\n", so a quoted scalar of that exact shape (k: " x\n") is also read as unknown.title: A long\n title→<title>A long</title>(readers: "A long title").key:was read as the value:title:\n [a:, b]→<title>[a:, b]</title>(readers disagree on this shape).key:leaked their indentation into the value:description:\n first line\n second→content=" first line\n second\n".After
mergeAdjacentTexta verbatimtitle:line merged into a preceding verbatim line is missed,so
ensureFrontmatterTitlederives a secondtitle:— a duplicate key js-yaml (Eleventy, Astro, Gatsby) rejects, losing the whole front matter.Other known gaps (smaller, same area)
opensWithVerbatimLinetreats any verbatim line as blocking front matter detection, including a key-shaped oneisYamlKeyLineaccepts,so
---\nTitle:\nbar: [a:, b]\n---keeps the emptyTitle:for no reason.keepingFirstSlotcan move an entry left unclosed (stream truncated inside the frontmatter) to the first slot;closed()then balances by count, nesting the following entries inside it.closed()pops its open-mark stack on anyUnmarkwithout checking the name,so a crossed upstream stream (
mark h1, mark em, unmark strong) gets the wrong closers appended.!opensWithVerbatimLine(emptyMap(), emptyList())recomputes a property of the closed frontmatter on every flush;computing it once in
judgeFrontmatterwould also make the rewrite condition easier to follow.