metadata.test.ts
The metadata rule, held to the yaml package: every block markz accepts without a warning gives
the object YAML 1.2 gives. The checks catch what writers get wrong (no, 1.10, Issue #42),
and every number form YAML reads (1e3, 0x1F) is one of them (syntax.md: Metadata).
import { describe, expect, it } from "vite-plus/test";
import { parse as yaml } from "yaml";
import { parse } from "./index";
const block = (body: string) => `---\n${body}\n---\n`;
describe("metadata", () => {
it.each([
["empty", "image:"],
["null", "image: null"],
["booleans", "draft: false\npublished: true"],
["numbers", "order: 2\nscale: -1.5\nzero: 0\nversion: 1.25"],
["plain strings", "title: Sales Report\ndate: 2026-09-26\nnote: C# notes\nhash: a#b"],
["double quotes", 'summary: "Make it yours: \\"quoted\\" \\u00e9"\ntitle: "Issue #42"'],
["single quotes", "summary: 'Make it yours: it''s done'"],
["quoted look-alikes", 'a: "true"\nb: \'42\'\nc: "no"\nd: "1.10"'],
["lists", 'tags: [svelte, vite]\nmixed: [a, 2, "b, c", true, null]\nnone: []'],
// oxfmt's output for a list too long for one line.
[
"a list wrapped by the formatter",
"nav:\n [\n usage.md,\n syntax.md,\n development.md,\n ]\ntitle: Docs",
],
["a list wrapped after its first item", 'tags: [a,\n "b, c",\n 3]'],
["a comma after the last item", "tags: [a, b,]"],
["comment lines", "# a comment\ntitle: Hi"],
["blank lines", "a: 1\n\nb: 2"],
["dots in keys", "deploy.name: my-site\na..b: 1\nx.y.z: true"],
])("%s", (_, body) => {
const doc = parse(block(body));
expect(doc.warnings).toEqual([]);
expect(doc.metadata).toEqual(yaml(body));
});
it.each([
"True",
"FALSE",
"~",
"Null",
"yes",
"No",
"off",
"ON",
"+1",
".5",
"01",
"1.",
"1.10",
"1.0",
"1e3",
"0x1F",
"0o7",
".inf",
".nan",
"-.Inf",
])("rejects the look-alike %s", (value) => {
const doc = parse(block(`a: ${value}\nb: 1`));
expect(doc.metadata).toEqual({ b: 1 });
expect(doc.warnings.map((w) => w.code)).toEqual(["metadata-value"]);
});
it.each(["Issue #42", "a: b", "note:", "[Issue #42, x]"])(
"keeps %s as written, with a warning, where YAML would cut it short or reject it",
(value) => {
const doc = parse(block(`a: ${value}`));
expect(doc.metadata).toEqual({ a: value.startsWith("[") ? ["Issue #42", "x"] : value });
expect(doc.warnings.map((w) => w.code)).toEqual(["metadata-value"]);
},
);
it.each([
["an indented list", "tags:\n - a\n - b"],
["a nested map", "a:\n b: 1"],
["a block scalar", "a: |"],
["a flow map", "a: {b: 1}"],
["an anchor", "a: &x 1"],
["a comment after a quoted value", 'a: "x" # note'],
["a nested list", "a: [[1]]"],
["a wrapped list that never closes", "a: [x,\n y"],
["indented lines that aren't a list", "a:\n x, y"],
["an unclosed quote", 'a: "open'],
])("rejects %s and skips its key", (_, body) => {
const doc = parse(block(`${body}\nkeep: 1`));
expect(doc.metadata).toEqual({ keep: 1 });
expect(doc.warnings.length).toBeGreaterThan(0);
});
it("keeps the first of two duplicate keys", () => {
const doc = parse(block("a: 1\na: 2"));
expect(doc.metadata).toEqual({ a: 1 });
expect(doc.warnings.map((w) => w.code)).toEqual(["metadata-duplicate-key"]);
});
it.each(["null", "[x]", "x"])("skips a `__proto__` key (%s) and keeps the rest", (value) => {
const doc = parse(block(`__proto__: ${value}\ntitle: Hi`));
expect(doc.metadata).toEqual({ title: "Hi" });
expect(Object.getPrototypeOf(doc.metadata)).toBe(Object.prototype);
expect(doc.warnings.map((w) => w.code)).toEqual(["metadata-line"]);
});
it("sets inherited names as keys of its own", () => {
const doc = parse(block("constructor: 1\ntoString: x"));
expect(doc.warnings).toEqual([]);
expect(doc.metadata).toEqual({ constructor: 1, toString: "x" });
});
it.each([".a", "1a", "a b"])("reads %s as no key", (key) => {
const doc = parse(block(`${key}: 1\nkeep: 1`));
expect(doc.metadata).toEqual({ keep: 1 });
expect(doc.warnings.map((w) => w.code)).toEqual(["metadata-line"]);
});
it("records the block and its body ranges", () => {
const source = block("a: 1");
const doc = parse(source);
const node = doc.firstChild(doc.root);
expect(doc.type(node)).toBe("metadata");
expect([doc.start(node), doc.end(node)]).toEqual([0, source.length - 1]);
const { range } = doc.data(node, "metadata");
expect(source.slice(range.start, range.end)).toBe("a: 1");
});
it("is metadata whatever it holds, and reports the lines it cannot read", () => {
const doc = parse("---\nhello\ntitle: x\n---\n");
expect(doc.metadata).toEqual({ title: "x" });
expect(doc.warnings.map((w) => w.code)).toEqual(["metadata-line"]);
});
it("skips a key whose value continues on later lines", () => {
const doc = parse("---\none:\n- 2\nb: {\nc: 1\n}\nd: 4\n---\n");
expect(doc.metadata).toEqual({ d: 4 });
});
it("is only metadata at the very start, and only when closed", () => {
expect(parse("\n---\na: 1\n---\n").metadata).toBeUndefined();
const unclosed = parse("---\na: 1\n");
expect(unclosed.metadata).toBeUndefined();
expect(unclosed.type(unclosed.firstChild(unclosed.root))).toBe("thematicBreak");
});
it.each(["a: 1", "deploy.1: x", "a..b: y"])(
"reports an unclosed block whose first line is the key line %s",
(line) => {
const codes = parse(`---\n${line}\n`).warnings.map((w) => w.code);
expect(codes).toContain("metadata-unclosed");
},
);
});