Skip to content

Supported formats reference

manni meta reads metadata from a document using a per-format extractor. The extractor is chosen from the file extension, or forced with --as <format>. Every extractor below is implemented and counts toward directory and glob walks.

Format (--as name) Extensions Metadata source fill can write
markdown .md, .markdown Leading fenced frontmatter: YAML (--- … ---), TOML (+++ … +++), or JSON (;;; … ;;;). Yes
mdx .mdx Leading fenced frontmatter: YAML, TOML, or JSON. Yes
asciidoc .adoc, .asciidoc Fenced frontmatter, or the native header: = Title plus :key: value attributes. Only into an existing fenced block
rst .rst Fenced frontmatter, or the native section title plus :key: value docinfo fields. Only into an existing fenced block
xml .xml, .dita, .ditamap Root-element attributes, plus element metadata; for DITA, also <othermeta> and the typed <prolog>/<topicmeta> elements. Yes
html .html, .htm <title> plus <meta name="…" content="…"> tags, plus element metadata from <head>. Yes

Writing metadata back is a harder problem than reading it. fill will only write a format it can round-trip without disturbing the rest of the document.

Fenced frontmatter round-trips cleanly. Only the characters between the fences are replaced. The body, the fence style, and (for YAML) comments and quoting survive untouched.

The native syntaxes do not. AsciiDoc attributes and reStructuredText docinfo fields are read through a YAML scalar parse that loses the original spelling. An rst title is synthesized from the section heading rather than stored as a field. Writing it back would mean rewriting the underline adornment or adding a :title: field that shadows the heading. Nor can manni meta simply add a fenced block to those files. A bare --- is a transition in reStructuredText and an open-block delimiter in AsciiDoc. Inventing one would change how the page renders.

HTML and XML are both written the same way, by replacing one character range and leaving every other byte alone. For HTML the parser hands those ranges over directly. For XML it does not. The parser reports only where each attribute starts, as a line and column, so manni meta rebuilds the range from that position.

DITA is written differently from plain XML, and not for a mechanical reason. Its metadata belongs in a <prolog> element, so adding it to the root element would produce a topic that its DTD rejects. The prolog is written instead.

fill writes HTML and XML the same way it writes fenced frontmatter, by replacing one range and leaving every other byte alone. Entity spelling, attribute quoting, void elements and the doctype all survive. For XML that matters most with the DTD-declared entities common in DITA-adjacent content: re-serializing would turn &nbsp; into &amp;nbsp;, and splicing cannot. A new <meta> tag is inserted just inside <head>, indented to match its neighbours. A new XML attribute is added to the root element’s start tag. A page with no <head> of its own is refused rather than guessed at.

Run manni meta schemas to see the current writability of each format. A file manni meta cannot write is reported as a per-file error, and the rest of the run continues. Examples are an .rst with no fenced block, or an HTML page with no <head>.

Some formats keep metadata in more than one place. HTML can carry a title in both <title> and a <meta name="title">. fill writes back to whichever one manni meta read the value from. That matters because fill also corrects values that are present but invalid. Writing the correction anywhere else would leave the wrong value in the tag the page actually uses. The report would still call the file fixed.

The --as name is the extractor name in the first column. The extension match is case-insensitive.

Wherever manni meta reads frontmatter, it accepts three interchangeable flavors. That covers Markdown, MDX, and the fenced-block path of AsciiDoc and reStructuredText. Vale uses the same convention. The flavor is auto-detected from the opening fence, so no flag or --as name is needed:

Flavor Fence Inner syntax
YAML ------ (or ... close) YAML
TOML ++++++ TOML
JSON ;;;;;; a JSON object

YAML additionally accepts the conventional ... document-end marker as a closing fence; TOML and JSON close on a repeat of their own opening fence.

All three are fenced blocks at the very top of the file. The opening fence sits on its own line, the content beneath, and a matching closing fence below that. A fence that never closes is not treated as frontmatter. For AsciiDoc and rst the native header is read instead. For Markdown and MDX, which have no native fallback, the metadata is reported as absent rather than as an error. A malformed block, whether invalid YAML, TOML, or JSON, is a per-file parse failure with exit code 1. The rest of the run continues. See Output & exit codes.

Error annotations point at the offending field’s own source line. For TOML this is best-effort. Bare and simply-quoted top-level keys (key = …, "key" = …) map to their line. Dotted keys, keys nested under a [table], and quoted keys containing escape sequences fall back to the block’s opening fence line.

Both read a leading fenced frontmatter block in any of the three flavors. MDX uses the same logic as Markdown; export const meta = {…} is not read. A file with no frontmatter reports its metadata as not present.

The three blocks below are equivalent:

---
type: guide
title: Getting started
tags: [setup, onboarding]
---
+++
type = "guide"
title = "Getting started"
tags = ["setup", "onboarding"]
+++
;;;
{
"type": "guide",
"title": "Getting started",
"tags": ["setup", "onboarding"]
}
;;;

AsciiDoc accepts two metadata styles. If the file opens with a complete fenced frontmatter block (YAML, TOML, or JSON), that block is used. Otherwise manni meta reads the native document header: the lines from the top of the file down to the first blank line:

  • A leading = Title line becomes title.
  • Each :name: value line becomes a name key. A :name: with no value is true; an unset attribute (:!name: or :name!:) is false.
  • Other header lines (such as author or revision lines) are ignored.
= Getting started
:type: guide
:draft: false

reStructuredText also accepts two styles. A complete leading fenced frontmatter block (YAML, TOML, or JSON) is used when present (as some MyST setups produce it). Otherwise manni meta reads the native page metadata:

  • A leading section title (a line underlined, and optionally overlined, with punctuation) becomes title.
  • The docinfo field list that follows, a run of :name: value fields, becomes the remaining keys. A :name: with no value is true. An explicit :title: field takes precedence over the heading.
Getting started
===============
:type: guide
:tags: [setup, onboarding]

XML metadata comes from the attributes of the root element. Namespace declarations (xmlns and xmlns:*) are dropped as transport noise.

<document type="concept" version="2" />

This yields type: "concept" and version: 2. Malformed XML is reported as a per-file parse error. An entity manni meta cannot resolve is not malformed XML. No external DTD is ever fetched. A reference such as &nbsp; is left as written, and the file is still read.

DITA topics (.dita) and maps (.ditamap) are read by the same extractor, so their root-element attributes are the metadata:

<concept id="metadata-overview" type="concept" xml:lang="en-us">

This yields id, type, and xml:lang; the DOCTYPE declaration is skipped. The DTD-declared entities common in DITA content (&nbsp;, &mdash;) do not fail the file. See above.

Elements are read too, not just attributes. The root’s direct text-bearing children become <root>.<child> keys, so <article><byline> is article.byline. An elements: path reaches deeper. HTML gains head.title beside the flat title it has always produced. See Element metadata for the naming rule and what a write will and will not do.

DITA also carries metadata in the place the standard sets aside for it, and manni meta reads that too:

<prolog>
<metadata>
<othermeta name="audience" content="developer"/>
</metadata>
</prolog>

A map uses <topicmeta> in place of <prolog><metadata>. Where a key appears both as a root attribute and as an <othermeta>, the <othermeta> wins. It is the explicit metadata channel, while root attributes are largely structural.

manni meta also reads the typed prolog elements <author>, <critdates>, <audience> and <permissions>, as prolog.author, critdates.created and so on. Those and the <othermeta> keys are separate keys, so a topic carrying the same fact in both channels has both validated. The DITA schema describes them.

fill writes DITA into that same channel. A key already present keeps its place. A root attribute is corrected as a root attribute, and an <othermeta> as an <othermeta>. Only a key that appears in neither is added, as a new <othermeta>. <metadata> and <prolog> are created if the topic lacks them. A new <prolog> is placed where the content model requires: after the title, titlealts, shortdesc and abstract, and before the body.

Keeping an existing key in its own tag is what makes the write safe. fill corrects values that are present but invalid, not only missing ones. Adding an <othermeta> beside a stale root attribute would leave a stale value in place. That is the value a DITA processor actually reads, and the report would still call the file fixed. It is also why the write cannot break DTD validity: updating an attribute that is already there cannot make it un-declared.

A file counts as DITA in three cases. Its DOCTYPE names a DITA DTD. Its root element carries a DITA class or DITAArchVersion attribute. Or a .dita / .ditamap extension is paired with a DITA root element name. A root element merely named map or task in a .xml file is not enough.

HTML metadata comes from the document head:

  • <title>…</title> becomes title (the first <title> wins; its text is kept verbatim).
  • <meta name="X" content="Y"> becomes X: Y. property="X" is accepted in place of name for OpenGraph-style tags.
  • <meta> tags with neither name nor property (such as charset or http-equiv) carry no metadata and are skipped. For duplicate keys, the last tag wins.
  • A <meta name="title"> outranks the <title> element, whichever comes first in the document. This matters when filling: fill writes title back into the <meta> tag when the page has one, because that is the value manni meta reads.
<title>Getting started</title>
<meta name="type" content="guide">
<meta name="draft" content="false">

HTML parsing recovers from malformed markup, so extraction does not throw a parse error.

fill can write HTML. An existing tag has only its value replaced, so the surrounding quoting and spelling are preserved. A key with no tag yet gets a new <meta> just inside <head>, indented to match its neighbours. A document whose <head> is implied rather than written out, an HTML fragment for instance, is refused. There is no head to write into.

Each value on the native metadata paths is parsed as a YAML scalar. Those paths are the AsciiDoc header, the reStructuredText docinfo field list, XML attributes, and HTML <meta>/<title>. This means string-looking inputs are coerced to their natural types:

Raw value Becomes Type
2 2 number
true true boolean
[a, b] ["a", "b"] array

An explicitly empty value (title="" in XML, content="" in HTML) stays the empty string rather than becoming null.

Fenced frontmatter carries its own native types instead, with no per-value scalar re-parsing is applied. YAML types come from normal YAML rules, and TOML and JSON from their own type systems. So version = 2 and "version": 2 are both the number 2, and a TOML/JSON array is a list.

Because the value’s type is decided by how it’s written, quoting is a real authoring choice in every flavor. A quoted "2", or YAML version: "2", is the string "2". It will not satisfy a schema field typed integer.