goja-text markdown user guide

A guided introduction to parsing, traversing, and rendering Markdown from JavaScript.

Sections

Terminology & Glossary
πŸ“– Documentation
Navigation
11 sectionsv0.1
πŸ“„ goja-text markdown user guide β€” glaze help goja-text-markdown-user-guide
goja-text-markdown-user-guide

goja-text markdown user guide

A guided introduction to parsing, traversing, and rendering Markdown from JavaScript.

Tutorialgoja-textmarkdownguidejavascriptgoja-text evalgoja-text rungoja-text markdowngoja-text sanitizegoja-text extract

The markdown module exists for scripts that need to understand Markdown as a document, not merely transform it as text. A Markdown file contains structure: headings introduce sections, links point outside the document, images carry destinations and alternate text, and code fences often contain data that another tool wants to inspect. require("markdown") gives JavaScript access to that structure while keeping the parser and the domain objects in Go.

The central idea is simple: parse once, then ask specific questions by walking the tree. The module does not try to predict every question a script might ask. Instead, it exposes a reliable traversal primitive, walk(), and lets JavaScript express the document query that matters for the current task.

The first parse

Start by loading the module and parsing a string. The result is a Go-backed MarkdownNode, so JavaScript reads fields with exported Go names such as Type, Children, and Level.

const markdown = require("markdown");

const ast = markdown.parse(`# Title

See [the docs](https://example.com/docs).
`);

console.log(ast.Type);             // "document"
console.log(ast.Children[0].Type); // "heading"
console.log(ast.Children[0].Level); // 1

This PascalCase shape is intentional. The same object can cross back into Go functions such as textContent() and walk(), where Go can validate that it is receiving a real Markdown node rather than a JavaScript object that merely looks similar.

Turning structure into a useful query

Most scripts should follow a two-step pattern. First parse the document. Then use walk() to collect exactly the facts the script needs.

const headings = [];
const links = [];

markdown.walk(ast, (node, ctx) => {
  if (node.Type === "heading") {
    headings.push({
      level: node.Level,
      text: markdown.textContent(node),
      depth: ctx.Depth,
    });
  }

  if (node.Type === "link") {
    links.push({
      text: markdown.textContent(node),
      destination: node.Destination,
      title: node.Title,
    });
  }
});

console.log(JSON.stringify({ headings, links }, null, 2));

This pattern is more flexible than a collection of narrow Go helpers. A table-of-contents script, a link checker, and a documentation linter all need the same parser, but they ask different questions. walk() keeps the Go API small while giving JavaScript room to describe the policy.

Controlling traversal

A visitor can return a small control value. Return "stop" when the script has found enough information, or return "skip" when the current subtree is not relevant.

let firstExternalLink = null;

markdown.walk(ast, (node) => {
  if (node.Type === "link" && /^https?:/.test(node.Destination || "")) {
    firstExternalLink = node.Destination;
    return "stop";
  }
});

The control values are deliberately plain JavaScript values:

  • undefined or true means traversal continues normally.
  • false or "skip" skips the current node's children.
  • "stop" ends the traversal immediately.

Rendering is a different question

Use renderHTML() when the goal is presentation. Use parse() and walk() when the goal is analysis.

./dist/goja-text eval 'const md = require("markdown"); md.renderHTML("## Title\n\n**bold**")'

HTML is an output format. The AST is the document model. Keeping that distinction clear prevents scripts from scraping rendered HTML when the original Markdown tree already contains the needed information.

Running the included examples

The repository includes a conventional demo script and a root-mounted JavaScript verb command source. The demo is useful when you want to see console-oriented JavaScript. The jsverb is useful when you want Glazed to render structured rows.

./dist/goja-text run examples/js/markdown-demo.js
./dist/goja-text markdown headings examples/markdown/sample.md

The markdown headings verb reads a file with the host fs module, parses it, walks the AST, and returns rows containing heading level, text, and source depth. That is the same pattern shown above, packaged as a command.

Key points

  • The Markdown AST is Go-backed so Go can validate nodes when JavaScript passes them back into module functions.
  • JavaScript reads exported fields with PascalCase names such as Type, Children, Level, and Destination.
  • walk() is the primary extension point. Write document-specific queries in JavaScript instead of waiting for a new Go helper.
  • Use renderHTML() for presentation and parse() for structure.