Stringify SDK

Builder Primitives

Type-safe functions for building Markdown strings block by block.

The datamark/stringify subpath exports builder primitives — pure functions that take data and return Markdown strings. Use them instead of raw string concatenation to keep serialization readable, type-safe, and free of escaping bugs.

All builders are pure functions. They return strings, not AST nodes. Compose them with join("\n\n") or template literals.

Block builders

frontmatter(data)

Build a YAML frontmatter fence from a data object.

import { frontmatter } from "datamark/stringify";

frontmatter({ title: "Hello", tags: ["a", "b"] })
// "---\ntitle: Hello\ntags:\n  - a\n  - b\n---\n"

heading(text, depth?)

Build a Markdown heading. Depth defaults to 1 and is clamped to 1–6.

import { heading } from "datamark/stringify";

heading("Title")       // "# Title"
heading("Subtitle", 2) // "## Subtitle"

paragraph(text)

Build a paragraph. Returns the text as-is.

import { paragraph } from "datamark/stringify";

paragraph("This is a paragraph.") // "This is a paragraph."

codeBlock(code, lang?)

Build a fenced code block.

import { codeBlock } from "datamark/stringify";

codeBlock("const x = 1;", "typescript")
// "```typescript\nconst x = 1;\n```"

list(items, ordered?, start?)

Build a list from an array of item strings.

import { list } from "datamark/stringify";

list(["First", "Second"])      // "- First\n- Second"
list(["A", "B"], true)         // "1. A\n2. B"
list(["C", "D"], true, 3)      // "3. C\n4. D"

Multi-line items are indented correctly:

import { list } from "datamark/stringify";

list(["First line\nSecond line", "Another"])
// "- First line\n  Second line\n- Another"

blockquote(text)

Build a blockquote.

import { blockquote } from "datamark/stringify";

blockquote("A wise quote.")
// "> A wise quote."

Multi-line text gets a > prefix on every line:

import { blockquote } from "datamark/stringify";

blockquote("Line one\nLine two")
// "> Line one\n> Line two"

horizontalRule()

Build a horizontal rule.

import { horizontalRule } from "datamark/stringify";

horizontalRule() // "---"

Inline builders

strong(text)

Wrap text in bold.

import { strong } from "datamark/stringify";

strong("bold") // "**bold**"

em(text)

Wrap text in italics.

import { em } from "datamark/stringify";

em("italic") // "*italic*"

codeSpan(text)

Wrap text in inline code.

import { codeSpan } from "datamark/stringify";

codeSpan("foo") // "`foo`"

link(text, href, title?)

Build a Markdown link.

import { link } from "datamark/stringify";

link("Docs", "https://example.com")
// "[Docs](https://example.com)"

link("Docs", "https://example.com", "API Docs")
// "[Docs](https://example.com \"API Docs\")"

image(alt, src, title?)

Build a Markdown image.

import { image } from "datamark/stringify";

image("Logo", "/logo.png")
// "![Logo](/logo.png)"

image("Logo", "/logo.png", "Company logo")
// "![Logo](/logo.png \"Company logo\")"

strikethrough(text)

Build strikethrough text.

import { strikethrough } from "datamark/stringify";

strikethrough("deleted") // "~~deleted~~"

Composing a full document

Builder primitives are pure functions that return strings. Compose them with Array.join or template literals:

import {
  frontmatter,
  heading,
  paragraph,
  codeBlock,
  list,
  strong,
} from "datamark/stringify";

const markdown = [
  frontmatter({ title: "API Guide", version: "1.0" }),
  heading("Authentication", 2),
  paragraph(`Use ${strong("Bearer tokens")} for all requests.`),
  codeBlock("fetch('/api', { headers: { Authorization: 'Bearer ...' } });", "javascript"),
  heading("Endpoints", 2),
  list(["GET /users", "POST /users", "DELETE /users/:id"]),
].join("\n\n");

This produces clean, well-formatted Markdown without manual escaping or fence counting.

On this page