Documentation
¶
Overview ¶
Package markdownviewer is an embeddable Markdown previewer: it parses Markdown (CommonMark + GFM + modern extensions) into a document model and renders themed, self-contained, sanitized HTML.
Quick start:
html, err := markdownviewer.Render(src)
See the document package for the AST and render/html for renderer options.
Concurrency ¶
All top-level functions in this package (Parse, ParseWith, Render, RenderTo, RenderDoc, RenderDocTo, ParseContext, RenderContext, RenderDocContext) are safe for concurrent use: they share no mutable state across calls beyond two package-level values that are constructed once and never mutated afterward — bluemonday's sanitizer Policy (its README documents Sanitize as safe to call concurrently on a constructed Policy; only construction/editing is not) and chroma's HTML formatter (its style cache is internally mutex-protected for concurrent Format calls). A *document.Document returned by Parse/ParseWith must not be mutated concurrently with a Render/RenderTo/RenderDoc/RenderDocTo call that reads it, or with another mutation — document.Document itself has no internal synchronization.
ParseContext/RenderContext/RenderDocContext extend this contract: when ctx ends before the underlying work finishes, the function returns ctx.Err() immediately, but the abandoned goroutine may still be reading src (ParseContext, RenderContext) or doc (RenderDocContext) for an unbounded window afterward — there is no signal for when it actually stops. Getting ctx.Err() back is not a guarantee that those inputs are safe to mutate or reuse; callers must treat src/doc passed to a Context variant as immutable for the lifetime of the call, and must not assume that lifetime has ended just because the call returned.
Resource exhaustion ¶
Parse/ParseWith/Render/RenderTo have no built-in wall-clock or work budget. A deeply nested list (thousands of levels) can take goldmark's parser tens of seconds even though the input itself is small, because the cost is super-quadratic in nesting depth rather than in input size. Hosts that process untrusted input should wrap these calls in their own wall-clock timeout. See SECURITY.md for measurements and the recommended pattern. RenderDoc/RenderDocTo receive an already-parsed tree and so are not subject to the parse-time cost, but rendering a pathologically deep tree built by other means can still be costly.
ParseContext/RenderContext/RenderDocContext implement exactly this timeout pattern for the caller: they honor ctx's deadline/cancellation and return promptly, but the abandoned goroutine keeps running to completion — its CPU is not reclaimed. See SECURITY.md.
Index ¶
- Constants
- func Parse(src []byte) (*document.Document, error)
- func ParseContext(ctx context.Context, src []byte) (*document.Document, error)
- func ParseWith(src []byte, cfg parser.Config) (*document.Document, error)
- func Render(src []byte, opts ...Option) ([]byte, error)
- func RenderContext(ctx context.Context, src []byte, opts ...Option) ([]byte, error)
- func RenderDoc(doc *document.Document, opts ...Option) ([]byte, error)
- func RenderDocContext(ctx context.Context, w io.Writer, doc *document.Document, opts ...Option) error
- func RenderDocTo(w io.Writer, doc *document.Document, opts ...Option) error
- func RenderTo(w io.Writer, src []byte, opts ...Option) error
- type Option
- func AllowRawHTML() Option
- func DisableHeadingAnchors() Option
- func DisableHighlighting() Option
- func DisableMath() Option
- func DisableMermaid() Option
- func Fragment() Option
- func WithCodeHeader() Option
- func WithExtraCSS(css string) Option
- func WithMaxWidth(width string) Option
- func WithParserConfig(cfg parser.Config) Option
- func WithResolver(r Resolver) Option
- func WithSourceMap() Option
- func WithStylesheet(css string) Option
- func WithTheme(name string) Option
- func WithThemeOverrides(vars map[string]string) Option
- type ResolveKind
- type Resolver
Examples ¶
Constants ¶
const ( // ResolveLink signals resolution of a standard Markdown link target. ResolveLink = resolve.ResolveLink // ResolveImage signals resolution of a Markdown image target. ResolveImage = resolve.ResolveImage // ResolveWikiLink signals resolution of a wiki-link target. ResolveWikiLink = resolve.ResolveWikiLink )
Variables ¶
This section is empty.
Functions ¶
func Parse ¶
Parse returns the document model for src.
Example ¶
Parse returns the document model directly, for callers that want to walk or otherwise process the tree themselves rather than render it to HTML.
package main
import (
"fmt"
markdownviewer "github.com/sriannamalai/markdownviewer"
"github.com/sriannamalai/markdownviewer/document"
)
func main() {
src := "# One\n\nSome text.\n\n## Two\n\nMore text.\n\n### Three\n"
doc, err := markdownviewer.Parse([]byte(src))
if err != nil {
panic(err)
}
headings := 0
document.Walk(doc, func(n document.Node, entering bool) document.WalkStatus {
if entering {
if _, ok := n.(*document.Heading); ok {
headings++
}
}
return document.Continue
})
fmt.Println(headings)
}
Output: 3
func ParseContext ¶ added in v0.2.0
ParseContext is Parse with caller-side deadline support. If ctx ends first, ParseContext returns ctx.Err() immediately — but the underlying parse continues on its goroutine until it finishes and cannot be stopped (the Markdown engine has no cancellation hooks). The guarantee is bounded caller latency, not reclaimed CPU; see SECURITY.md. The abandoned goroutine may still be reading src after this function returns, for an unbounded window — do not mutate or reuse src until you can otherwise guarantee that goroutine has finished (see the package's Concurrency section).
func Render ¶
Render parses src and renders HTML with the given options.
Example ¶
Render parses and renders Markdown to HTML. Fragment() is used here to keep the example's output small and deterministic; without it Render produces a full HTML page (doctype, head, embedded theme CSS) which is too large to pin down with an // Output: comment.
package main
import (
"fmt"
markdownviewer "github.com/sriannamalai/markdownviewer"
)
func main() {
out, err := markdownviewer.Render([]byte("# Hello\n\nWorld.\n"), markdownviewer.Fragment())
if err != nil {
panic(err)
}
fmt.Print(string(out))
}
Output: <h1 id="hello">Hello</h1> <p>World.</p>
func RenderContext ¶ added in v0.2.0
RenderContext is Render with caller-side deadline support (same abandonment semantics as ParseContext, including that src must not be mutated or reused after a cancelled return until the abandoned goroutine is otherwise known to have finished).
func RenderDoc ¶ added in v0.2.0
RenderDoc renders an already-parsed document, enabling parse-once / render-many workflows such as switching themes without re-parsing.
func RenderDocContext ¶ added in v0.2.0
func RenderDocContext(ctx context.Context, w io.Writer, doc *document.Document, opts ...Option) error
RenderDocContext is RenderDocTo with caller-side deadline support. The render happens into an internal buffer; w is written only on success, so an abandoned render never touches w after this function returns. As with ParseContext/RenderContext, the abandoned goroutine may still be reading doc for an unbounded window after a cancelled return — its CPU is not reclaimed, and doc must not be mutated or reused until you can otherwise guarantee that goroutine has finished; see SECURITY.md.
func RenderDocTo ¶ added in v0.2.0
RenderDocTo renders an already-parsed document into w.
Types ¶
type Option ¶
type Option func(*config)
Option is a functional option that modifies render configuration.
func AllowRawHTML ¶
func AllowRawHTML() Option
AllowRawHTML permits raw HTML and unsafe URL schemes in the output.
func DisableHeadingAnchors ¶ added in v0.9.0
func DisableHeadingAnchors() Option
DisableHeadingAnchors omits the slug id attributes from rendered headings (<h1 id="...">). Anchor ids are still computed at parse time (document.Heading.AnchorID is unaffected); this only stops the HTML renderer from emitting them, so intra-page #fragment links to headings stop resolving.
func DisableHighlighting ¶
func DisableHighlighting() Option
DisableHighlighting disables syntax highlighting in code blocks.
func DisableMath ¶
func DisableMath() Option
DisableMath disables KaTeX mathematical notation rendering.
func DisableMermaid ¶
func DisableMermaid() Option
DisableMermaid disables mermaid diagram rendering.
func Fragment ¶
func Fragment() Option
Fragment emits body-only HTML instead of a full page: no <html>/<head>, no theme <style>, no embedded mermaid/KaTeX <script> assets. The host owns the page and is responsible for supplying styling — see the Theming section of README.md for the CSS variables the markup expects.
Diagram and math nodes still render their markup (a <pre class="mermaid"> block, a <span>/<div class="math ...">), but as inert placeholders: with no mermaid.js/KaTeX loaded, the raw diagram/math source is what a viewer sees until the host provides those libraries itself. The same mermaid.js and KaTeX JS/CSS this package embeds for full-page output are available to fragment hosts via the assets package (assets.MermaidJS, assets.KatexJS, assets.KatexCSS) — see README.md's Theming section for the injection snippet.
Example ¶
Fragment emits body-only HTML: no <html>/<head>/<style>, just the rendered markup. Hosts embedding output into an existing page use this instead of the default full-page output.
package main
import (
"fmt"
markdownviewer "github.com/sriannamalai/markdownviewer"
)
func main() {
out, err := markdownviewer.Render([]byte("**bold**\n"), markdownviewer.Fragment())
if err != nil {
panic(err)
}
fmt.Print(string(out))
}
Output: <p><strong>bold</strong></p>
func WithCodeHeader ¶ added in v0.8.0
func WithCodeHeader() Option
WithCodeHeader wraps each rendered code block in a header row carrying a language label (the fence language, or "code" when unlabeled) and a Copy button, using the md-code / md-code-header / md-code-lang / md-code-copy classes styled by the base stylesheet. Live mermaid diagrams and math are not wrapped; their engine-disabled plain-code fallbacks are. Full pages also get a small inline clipboard script wiring the buttons; fragment hosts receive the markup and wire their own handler.
func WithExtraCSS ¶ added in v0.8.0
WithExtraCSS appends css after the page's base styling — the base+theme stylesheets by default, or the WithStylesheet replacement when one is set. Full-page rendering only, like WithStylesheet; it has no effect in fragment mode. Content is emitted into the page's <style> element; </style sequences are stripped defensively.
func WithMaxWidth ¶ added in v0.3.0
WithMaxWidth constrains the rendered page's content width, via the --md-max-width CSS custom property (theme.BaseCSS's "max-width: var(--md-max-width, none)"). width accepts any CSS length, e.g. "860px" or "70ch". The default is fluid: an empty string (or never calling this option) leaves --md-max-width unset, so the page has no max-width constraint and fills its container.
width flows into a CSS custom-property value inside the page's <style> element. As a defense-in-depth measure on top of the usual </style stripping applied to all theme override values, a width containing ';' or '}' — either of which could break out of the single declaration this option controls — is rejected outright: the option no-ops, leaving whatever width was already configured (or the fluid default) unchanged, rather than emitting a truncated or defanged value.
Implemented via the same mechanism as WithThemeOverrides (it sets the "--md-max-width" key), so calling WithThemeOverrides after WithMaxWidth replaces the whole override map, including this key; call WithMaxWidth after WithThemeOverrides (or fold "--md-max-width" into that map directly) if you need both.
func WithParserConfig ¶ added in v0.2.0
WithParserConfig selects which Markdown extensions the parser enables when Render/RenderTo (and their Context variants) parse the source. It has no effect on RenderDoc, which receives an already-parsed tree.
func WithResolver ¶
WithResolver provides a custom link/image resolution callback.
Example ¶
WithResolver installs a callback that rewrites link/image/wiki-link targets — here, routing an image through a CDN. Returning ok=false falls back to default resolution for anything the resolver doesn't handle.
package main
import (
"fmt"
markdownviewer "github.com/sriannamalai/markdownviewer"
)
func main() {
resolver := func(kind markdownviewer.ResolveKind, target string) (string, bool) {
if kind == markdownviewer.ResolveImage {
return "https://cdn.example.com/" + target, true
}
return "", false
}
out, err := markdownviewer.Render([]byte("\n"),
markdownviewer.Fragment(), markdownviewer.WithResolver(resolver))
if err != nil {
panic(err)
}
fmt.Print(string(out))
}
Output: <p><img src="https://cdn.example.com/cat.png" alt="a cat" /></p>
func WithSourceMap ¶ added in v0.2.0
func WithSourceMap() Option
WithSourceMap annotates top-level block elements with data-md-line attributes for editor↔preview scroll synchronization.
func WithStylesheet ¶
WithStylesheet replaces the base stylesheet entirely with the provided CSS. When non-empty, it overrides theme.BaseCSS() and takes precedence over theme-based styling. Content is emitted into the page's <style> element; </style sequences are stripped defensively.
func WithThemeOverrides ¶
WithThemeOverrides applies CSS custom-property overrides to the rendered theme. Keys must match the pattern --[a-zA-Z0-9_-]+; non-conforming keys are silently dropped. Overrides are emitted after the base theme in sorted key order, ensuring they win in both light and dark variants.
Security: override VALUES are host-trusted CSS, emitted into the page's <style> element essentially verbatim — only the </style sequence is stripped (so a value cannot terminate the style element itself). A value CAN still close the :root{} declaration block and inject arbitrary CSS rules, including url() fetches to attacker-controlled origins. Hosts must never echo untrusted data into override values.
type ResolveKind ¶
type ResolveKind = resolve.ResolveKind
ResolveKind identifies which kind of target a Resolver is being asked to resolve: a standard link, an image, or a wiki-link. It is an alias for resolve.ResolveKind, the renderer-agnostic home of the resolution policy shared by all renderers.
type Resolver ¶
Resolver is a function that rewrites link and image targets. It accepts the resolution kind (link, image, or wiki-link) and target URL, returning the rewritten URL and true if resolution succeeded, or false to fall back to default handling. It is an alias for resolve.Resolver.
Trust contract: URLs returned with ok=true are emitted as-is without scheme filtering. Hosts fully control resolution and must not echo untrusted targets back unexamined.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package assets exposes the vendored mermaid and KaTeX payloads that full-page rendering embeds automatically.
|
Package assets exposes the vendored mermaid and KaTeX payloads that full-page rendering embeds automatically. |
|
cmd
|
|
|
mdview
command
Command mdview renders Markdown to self-contained HTML.
|
Command mdview renders Markdown to self-contained HTML. |
|
Package document defines the stable, renderer-agnostic Markdown document model.
|
Package document defines the stable, renderer-agnostic Markdown document model. |
|
Command ffi builds libmdviewer, the C-shared FFI boundary for the markdownviewer library.
|
Command ffi builds libmdviewer, the C-shared FFI boundary for the markdownviewer library. |
|
internal
|
|
|
boundary
Package boundary implements the shared JSON boundary consumed by both exported entry points — the cgo FFI (ffi/) and the wasm build (wasm/): strict version-1 options decoding plus markdown/document/HTML conversions and the embedded static asset registry.
|
Package boundary implements the shared JSON boundary consumed by both exported entry points — the cgo FFI (ffi/) and the wasm build (wasm/): strict version-1 options decoding plus markdown/document/HTML conversions and the embedded static asset registry. |
|
Package parser converts Markdown source into the document model.
|
Package parser converts Markdown source into the document model. |
|
render
|
|
|
html
Package htmlrender renders the document model to themed HTML.
|
Package htmlrender renders the document model to themed HTML. |
|
internal/derive
Package derive holds the tiny output-shaping derivations shared by every renderer: the admonition title rule, the code-block display label, the footnote def/ref pairing walk, the raw-HTML sanitize policy, and the destination-resolution pipeline (resolver → default resolution → SafeURL filter → percent-encoding).
|
Package derive holds the tiny output-shaping derivations shared by every renderer: the admonition title rule, the code-block display label, the footnote def/ref pairing walk, the raw-HTML sanitize policy, and the destination-resolution pipeline (resolver → default resolution → SafeURL filter → percent-encoding). |
|
tree
Package tree builds the version-1 native render tree: a layout-free, fully RESOLVED semantic tree that native hosts (Flutter, SwiftUI, Compose, …) render as platform widgets, with everything policy-heavy — URL resolution and filtering, raw-HTML sanitizing, admonition titles, footnote pairing, math/mermaid fallbacks — already applied library-side by Build, through the same shared derivations the HTML renderer uses (render/internal/derive and the resolve package), so the two renderers cannot drift.
|
Package tree builds the version-1 native render tree: a layout-free, fully RESOLVED semantic tree that native hosts (Flutter, SwiftUI, Compose, …) render as platform widgets, with everything policy-heavy — URL resolution and filtering, raw-HTML sanitizing, admonition titles, footnote pairing, math/mermaid fallbacks — already applied library-side by Build, through the same shared derivations the HTML renderer uses (render/internal/derive and the resolve package), so the two renderers cannot drift. |
|
Package resolve holds the renderer-agnostic destination-resolution policy: the Resolver hook and its ResolveKind discriminants, the safeURL scheme allowlist (SafeURL), and the built-in default resolution rule (DefaultResolution, the wiki-link ".md" fallback).
|
Package resolve holds the renderer-agnostic destination-resolution policy: the Resolver hook and its ResolveKind discriminants, the safeURL scheme allowlist (SafeURL), and the built-in default resolution rule (DefaultResolution, the wiki-link ".md" fallback). |
|
scripts
|
|
|
inlinefonts
command
Command inlinefonts rewrites KaTeX's CSS to carry its woff2 fonts as data: URIs so rendered pages are fully self-contained.
|
Command inlinefonts rewrites KaTeX's CSS to carry its woff2 fonts as data: URIs so rendered pages are fully self-contained. |
|
Package theme provides the built-in visual themes as CSS custom-property sets over one base stylesheet.
|
Package theme provides the built-in visual themes as CSS custom-property sets over one base stylesheet. |
|
Command wasm builds libmdviewer for GOOS=js GOARCH=wasm.
|
Command wasm builds libmdviewer for GOOS=js GOARCH=wasm. |