Help us shape the future of Go! Take the 2026 Go Developer Survey

petroglyph

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2026 License: MIT Imports: 21 Imported by: 0

README

petroglyph

A Go Markdown-to-HTML renderer built on goldmark. One function call turns an untrusted Markdown document into sanitized HTML with GitHub-style extras. No configuration required.

Features

  • GitHub-flavored Markdown: tables, strikethrough, task lists, autolinks, footnotes, and emoji (:tada:).
  • Heading ids from GitHub's anchor rule, with permalink anchors. A ## Heading {#custom-id} suffix is text, the way GitHub reads it; keep an old fragment alive with <a name="x">.
  • GitHub alerts: > [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION], with titles.
  • Code fences highlighted by chroma with CSS classes, using the github style.
  • Mermaid fences rendered as <pre class="mermaid"> for a client-side renderer: Mermaid or Pele.
  • Math with $...$ and $$...$$, the two forms GitHub reads, converted to MathJax delimiters.
  • YAML front matter rendered as a table above the document.
  • Sized images: ![alt](https://example.com/img.png =800x600).
  • Raw HTML passes through a sanitizer policy modeled on GitHub's subset.

Install

go get github.com/brutasse/petroglyph

Usage

import "github.com/brutasse/petroglyph"

html, flags := petroglyph.Convert(source)

Convert renders the whole document: optional front matter, body, sanitized raw HTML, wrapped tables, and alert titles. On a render failure it returns the source as escaped <pre> text instead of failing.

The returned Flags tell you which optional scripts the page needs:

if flags.Math {
    // load MathJax
}
if flags.Mermaid {
    // load a mermaid renderer: Mermaid or Pele
}

This keeps pages free of JavaScript they do not use.

Lower-level API
  • petroglyph.GFM is a goldmark.Extender. Compose it into your own goldmark pipeline when you want control over parsing or rendering. You then sanitize the output yourself.
  • petroglyph.New() returns a goldmark instance with the extension applied. Use it with petroglyph.Needs(doc, source) when you already hold the parsed document.
  • petroglyph.DisplayPolicy() returns the bluemonday policy Convert applies. Reuse it to sanitize other markup the same way.

Styling the output

Convert emits these classes. Your stylesheet decides how they look; the library ships no CSS.

Markup Meaning
.table-wrap wrapper div around every table, for overflow-x: auto
.frontmatter table generated from YAML front matter
.codeblock wrapper div around a highlighted code fence
.chroma chroma-generated code markup (class-based)
.mermaid <pre> holding a mermaid diagram source: the container a client-side renderer draws into
.markdown-alert .markdown-alert-note and friends alert blockquotes
.markdown-alert-title title paragraph inside an alert
.heading-anchor the # permalink link inside a heading
.fallback <pre> used when rendering failed

Generate the chroma stylesheet once with the same style the formatter uses:

f := chromahtml.New(chromahtml.WithClasses(true))
css, _ := os.Create("chroma.css")
defer css.Close()
f.WriteCSS(css, styles.Get("github"))

showcase.md exercises every feature. Render it with Convert to inspect the full markup, or use it as a fixture in your own stylesheet work.

Math and diagrams

Math output uses the \(...\) and \[...\] delimiters, which MathJax activates by default. A page shows math by loading MathJax when Flags.Math is set.

A ```mermaid fence becomes <pre class="mermaid"> and sets Flags.Mermaid. petroglyph ships no diagram code. The page picks a client-side renderer, and that renderer replaces the contents of each pre.mermaid with its own SVG. Load one renderer per page: Mermaid and Pele both read the diagram source from the <pre> element, so the second one draws over the first.

Mermaid

Mermaid scans pre.mermaid elements by itself, so startOnLoad is the only call a static page needs:

<script type="module">
  import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@12/dist/mermaid.esm.min.mjs';
  mermaid.initialize({ startOnLoad: true });
</script>

Call mermaid.run() yourself when parts of the page arrive after load. securityLevel defaults to strict: Mermaid escapes HTML in diagram text and disables click handlers. Keep that level for documents you do not trust. Themes come from mermaid.initialize (theme, themeVariables), and Mermaid adds its own stylesheet rules to the page. Bundle the library with npm install mermaid instead of the CDN.

Pele

Pele renders a diagram when you call mount(). The browser script defines the global pele:

<script src="https://cdn.jsdelivr.net/npm/pele@1/dist/pele.global.js"></script>
<script>
  for (const el of document.querySelectorAll('pre.mermaid'))
      pele.mount(el, el.textContent, { links: false, images: false });
</script>

mount() needs no initialization call, sizes each diagram to its container, redraws on container resize and on web-font load, and shows a syntax error in place instead of throwing. Diagram colors come from the --pele-* CSS variables, set on .pele or on any ancestor element.

The links and images options carry weight for untrusted documents. Pele allows http, https, mailto, and tel links and http and https images by default, and setting both options to false removes them. Bundle the library with npm install pele and import { mount } from 'pele' instead of the CDN.

Safety

Convert renders raw HTML (goldmark unsafe mode) and then filters the result through DisplayPolicy. The policy extends bluemonday's UGC policy with the classes, ids, checkbox inputs, and <a name> anchors this renderer generates or documents. Treat the output as display-safe, not script-safe: it can carry class and id attributes, so do not serve your own trust-critical DOM ids that a document could collide with.

License

MIT. See LICENSE.

Documentation

Overview

Package petroglyph is a Markdown-to-HTML stack: a zero-config goldmark extension assembling GitHub-flavored Markdown (tables, strikethrough, task lists, autolinks, footnotes, emoji) plus heading ids built by GitHub's anchor rule with permalink anchors, GitHub alerts, `![alt](url =WxH)` image sizing, chroma-highlighted and mermaid fences, YAML front matter, and MathJax math in $...$ and $$...$$ delimiters. Raw HTML passes through the renderer for DisplayPolicy to gate, GitHub-style.

Convert is the intended entry point. The GFM extender alone composes into a custom goldmark pipeline, but then raw-HTML rendering and sanitizing are the caller's business.

Index

Constants

This section is empty.

Variables

View Source
var GFM goldmark.Extender = gfmExt{}

GFM extends a goldmark instance with the full feature set and no configuration.

Functions

func AddAlertTitles

func AddAlertTitles(html string) string

AddAlertTitles injects the GitHub title paragraph (octicon + label) as the first child of every alert blockquote. Callers apply it to sanitized output: the markup is this file's, never content's, so the sanitizer's GitHub raw-HTML subset — which rejects svg — stays intact.

func DisplayPolicy

func DisplayPolicy() *bluemonday.Policy

DisplayPolicy renders untrusted content safely. Chroma emits class-named spans/tables and mermaid fences need a class; UGC strips class by default, so allow it back — no user-controlled CSS is ever served, classes only map to the fixed stylesheets the app ships.

func New

func New() goldmark.Markdown

New returns a goldmark instance with the GFM extension applied.

func SplitFrontMatter

func SplitFrontMatter(src string) (string, string)

SplitFrontMatter consumes a leading YAML front-matter block ("---" line to "---" or "..." line). It returns the table markup plus remaining source; when the block is absent or the top level is not a scalar-keyed mapping, the source passes through untouched with an empty table. Values that are nested collections render as compact inline values instead of a rejection.

This runs as a source pass before goldmark, not as an extension: an invalid front-matter block must fall through to normal Markdown parsing, and by the time any transformer or block parser could inspect it, the "---" delimiters are already thematic breaks or setext heading underlines. Convert applies it; callers running the GFM extender on their own pipeline must call it first, or front matter parses as a setext heading.

Types

type Flags

type Flags struct {
	Math    bool // TeX math present: load MathJax
	Mermaid bool // mermaid fence present: load a client-side renderer
}

Flags tells the page which optional payloads the rendered HTML needs: the template loads only the vendored JS actually required.

func Convert

func Convert(src string) (string, Flags)

Convert renders a whole Markdown document: the optional YAML front matter table, then the body with raw HTML gated by DisplayPolicy, every table wrapped for horizontal scrolling, and alert titles injected (trusted markup, post-sanitize). On render failure it returns the source as escaped preformatted text.

func Needs

func Needs(doc ast.Node, source []byte) Flags

Needs reports whether the parsed document contains math or mermaid.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL