class

Hwaro::Services::FrontmatterConverter

Inherits Reference < Object

Frontmatter Converter converts between YAML and TOML formats

Constants

NULL_OMITTED_KEYS = Set {"title", "description", "image", "date", "updated", "expires", "slug", "path", "aliases", "transparent", "generate_feeds", "paginate", "paginate_by", "pagination_enabled", "sort_by", "reverse", "page_template", "paginate_path", "redirect_to", "weight", "categories", "series", "series_weight", "cascade", "menus", "menu", "translation_key"}

Top-level front-matter keys whose null is written to TOML by OMITTING the key. TOML has no null and the default spelling, "", is a SET value for these: the build reads a null title/slug/image as unset ("Untitled", slug from the filename, no og:image) but slug = "" collapsed the page onto its section index URL and dropped it. It is Processors::Markdown::KNOWN_FRONT_MATTER_KEYS minus the build's ParseContent::CASCADABLE_KEYS: for a cascadable key a null still DECLARES the key and blocks a section's [cascade] (draft: ~ keeps a page out of a cascaded draft), which "" preserves and omission would not. Custom/extra keys and nested members keep "" too — the build turns their null into "" with the key present. Pinned against both lists by frontmatter_converter_spec.

TOML_DELIMITER = "+++"
TOML_FRONTMATTER_RE = /\A\+\+\+\s*\n(.*?\n?)^\+\+\+[ \t]*(?:\r?\n|\z)(.*)\z/m
TOML_OPENER_RE = /\A\+\+\+[ \t]*\r?\n/

Opening delimiter matchers. The build parser accepts trailing horizontal whitespace after the delimiter (\A---\s*\n in FrontmatterScanner), so detection must too — a --- \n file used to be skipped as "no frontmatter" while hwaro build parsed it fine.

YAML_DELIMITER = "---"
YAML_FRONTMATTER_RE = /\A---\s*\n(.*?\n?)^---[ \t]*(?:\r?\n|\z)(.*)\z/m

Frontmatter splitters. The closing-delimiter clause matches trailing horizontal whitespace and exactly one newline (or end of file) — not \s*$\n?, which would silently swallow a blank line between the delimiter and the body. The body capture keeps any blank line as a leading \n, so round-tripping TOML↔YAML preserves the author's original spacing (regression: blank line was lost).

YAML_OPENER_RE = /\A---[ \t]*\r?\n/

Constructors

new(content_dir : String = "content", dry_run : Bool = false)

A dry run performs the full detection/conversion pipeline — including the dropped-comment warnings, which are the main reason to preview an in-place bulk rewrite — but never writes a file back.

Source

Class methods

serialize_time(time : Time) : String

Serialize a frontmatter date/time value without corrupting the calendar day.

Frontmatter dates are commonly written as TOML/YAML local dates such as 2026-05-20, which parse to midnight in the local time zone. Rendering those through to_rfc3339 (always UTC) rolls the day back in any positive-offset zone — e.g. in KST 2026-05-20 becomes 2026-05-19T15:00:00Z, silently shifting the post's date on a format round-trip. When the value carries no time-of-day we emit a bare YYYY-MM-DD date and keep the day; genuine timestamps round-trip as RFC 3339.

Source

Instance methods

convert_to_json

Convert all content files to JSON format

Source
convert_to_toml

Convert all content files to TOML format

Source
convert_to_yaml

Convert all content files to YAML format

Source
detect_format(content : String) : FrontmatterFormat

Detect the frontmatter format of a file

Source