DocShell.Ast (DocShell v0.3.0)

Copy Markdown View Source

Parses Markdown into the recursive, JSON-safe node shape renderers consume.

DocShell preserves source structure without choosing a renderer. The AST can contain raw HTML tags and unsafe URL schemes; consumers must validate tags, attributes, and URLs and escape text for their rendering context. Every source — module documentation, guides, notebooks — is parsed once, here, into a tree of plain maps:

%{
  "tag" => "p",
  "attrs" => %{},
  "content" => ["Some ", %{"tag" => "code", ...}],
  "meta" => %{}
}

Text nodes are bare strings; everything else is a four-key map. That is the entire grammar. A renderer writes one recursive function over it and decides for itself whether an h2 is a heading component or an anchor target.

All four keys are always present, even when empty, so consumers can pattern match without Map.get/3 guards. Keys and attribute names are strings rather than atoms because the tree is destined for JSON and atoms would be created from untrusted document content on the way back in.

Errors

Earmark is forgiving and parses most malformed Markdown into something. When it does report problems, from_markdown/1 returns {:error, %{partial_ast: nodes, messages: messages}} — the nodes it managed to parse alongside the diagnostics — rather than discarding the work. Callers in DocShell.Generate treat that as a build failure, because silently publishing a partially-parsed document is worse than a failed build.

Examples

iex> DocShell.Ast.from_markdown("# Title")
{:ok, [%{"tag" => "h1", "attrs" => %{}, "content" => ["Title"], "meta" => %{}}]}

Summary

Types

One node in the documentation tree.

An element with required string keys tag, attrs, content, and meta.

Functions

Parses a Markdown string into renderer-neutral nodes.

Checks a list of recursive AST nodes without coercing malformed content.

Types

ast_node()

@type ast_node() :: String.t() | element()

One node in the documentation tree.

Either a bare string of text, or a map with tag, attrs, content, and meta, where content holds child nodes of the same shape.

element()

@type element() :: %{required(String.t()) => String.t() | map() | [ast_node()]}

An element with required string keys tag, attrs, content, and meta.

Functions

from_markdown(markdown)

@spec from_markdown(String.t()) :: {:ok, [ast_node()]} | {:error, term()}

Parses a Markdown string into renderer-neutral nodes.

See the module documentation for the node shape and the partial-parse error contract.

valid?(nodes)

@spec valid?(term()) :: boolean()

Checks a list of recursive AST nodes without coercing malformed content.