markup

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Mar 29, 2026 License: MIT Imports: 16 Imported by: 0

README

markup

A Go library for rendering markup files to HTML. Given a filename and its contents, it picks the right renderer and produces HTML.

Markdown and Org-mode are rendered natively in Go. Other formats (AsciiDoc, reStructuredText, Pod) shell out to external tools. The library has no global state, no forge-specific logic, and no opinion on post-processing.

Usage

r := markup.NewDefaultRegistry()

result, err := r.Render("README.md", content)
if err != nil {
    log.Fatal(err)
}
fmt.Println(result.HTML)
fmt.Println(result.Format) // "Markdown"

The default registry comes with all supported formats pre-configured. Native formats (Markdown, Org-mode) always work. External formats return markup.ErrToolNotFound if the required tool isn't on $PATH.

Supported formats

Format Extensions Renderer
Markdown .md, .markdown, .mdown, .mkdn, .mdn, .mdtext, .livemd goldmark (native)
Org-mode .org go-org (native)
AsciiDoc .adoc, .asciidoc, .asc asciidoctor (external)
reStructuredText .rst, .rest, .rst.txt rst2html (external)
Pod .pod pod2html (external)
Textile .textile consumer-provided (external)
MediaWiki .mediawiki, .wiki consumer-provided (external)
Creole .creole consumer-provided (external)
RDoc .rdoc consumer-provided (external)

Format detection

format := markup.Detect("README.org") // markup.FormatOrg
format.String()                       // "Org"

Custom renderers

Register your own renderer for any format:

r := markup.NewRegistry()

r.Register(markup.FormatMarkdown, markup.NewMarkdownRenderer(), ".md", ".markdown")

r.Register(markup.FormatTextile, markup.NewExternalRenderer(markup.ExternalConfig{
    Command: "pandoc",
    Args:    []string{"-f", "textile", "-t", "html"},
}), ".textile")

// Exact filename matching
r.RegisterFilename(markup.FormatMarkdown, markup.NewMarkdownRenderer(), "README")

Errors

result, err := r.Render("README.adoc", content)
if errors.Is(err, markup.ErrUnsupportedFormat) {
    // no renderer registered for this extension
}
if errors.Is(err, markup.ErrToolNotFound) {
    // renderer registered but external tool not on $PATH
}

Installing external tools

The native formats (Markdown, Org-mode) need no external tools. For the others:

Tool Install Formats
asciidoctor gem install asciidoctor AsciiDoc
rst2html pip install docutils or apt install python3-docutils reStructuredText
pod2html Included with Perl Pod

The included Dockerfile builds an image with all optional tools installed.

Benchmarks

BenchmarkMarkdownSmall     387460    3155 ns/op     7448 B/op     33 allocs/op
BenchmarkMarkdownMedium     43614   27558 ns/op    30626 B/op    196 allocs/op
BenchmarkMarkdownLarge        813 1466013 ns/op  1561079 B/op  11432 allocs/op
BenchmarkOrgSmall          104258   11548 ns/op    11897 B/op    131 allocs/op
BenchmarkOrgFixture          1729  688588 ns/op   236167 B/op   2084 allocs/op

External renderers are dominated by subprocess overhead (~30ms for pod2html, ~90ms for rst2html, ~120ms for asciidoctor).

License

MIT

Documentation

Overview

Package markup renders markup files to HTML.

Given a filename and its contents, it picks the right renderer and produces HTML. Markdown and Org-mode are rendered natively in Go. Other formats shell out to external tools (asciidoctor, rst2html, pod2html, etc).

The library has no global state. Construct a Registry, configure it, and call Render. Post-processing (sanitization, link rewriting, mention linking) is the consumer's responsibility.

Index

Constants

View Source
const DefaultMaxInputSize = 1024 * 1024

DefaultMaxInputSize is the default maximum input size (1MB).

Variables

View Source
var (
	ErrUnsupportedFormat = errors.New("unsupported markup format")
	ErrToolNotFound      = errors.New("external tool not found")
	ErrInputTooLarge     = errors.New("input exceeds maximum size")
	ErrOutputTooLarge    = errors.New("output exceeds maximum size")
)

Errors returned by Render.

Functions

This section is empty.

Types

type ExternalConfig

type ExternalConfig struct {
	Command     string        // Command to execute.
	Args        []string      // Arguments. Use {file} as a placeholder for temp file path.
	InputMode   InputMode     // How to pass content to the command.
	Timeout     time.Duration // Command timeout. Zero means 30s default.
	BodyPattern string        // Regex to extract body from full HTML output. Empty means use full output.
	TempExt     string        // File extension for temp files (e.g. ".pod"). Inferred from command if empty.
}

ExternalConfig configures an external renderer.

type ExternalRenderer

type ExternalRenderer struct {
	// contains filtered or unexported fields
}

ExternalRenderer shells out to a command to render markup.

func NewExternalRenderer

func NewExternalRenderer(config ExternalConfig) *ExternalRenderer

NewExternalRenderer creates a renderer that delegates to an external command.

func (*ExternalRenderer) Available

func (r *ExternalRenderer) Available() bool

Available returns true if the external command is on $PATH.

func (*ExternalRenderer) Render

func (r *ExternalRenderer) Render(content []byte) (string, error)

type Format

type Format int

Format represents a markup format.

const (
	FormatUnknown   Format = iota
	FormatMarkdown         // .md, .markdown, .mdown, .mkdn, .mdn, .mdtext, .livemd
	FormatOrg              // .org
	FormatAsciiDoc         // .adoc, .asciidoc, .asc
	FormatRST              // .rst, .rest, .rst.txt
	FormatTextile          // .textile
	FormatMediaWiki        // .mediawiki, .wiki
	FormatCreole           // .creole
	FormatPod              // .pod
	FormatRDoc             // .rdoc
)

func Detect

func Detect(filename string) Format

Detect identifies the markup format from a filename using the default extension mapping. This is a convenience for callers who only need format detection without a full registry.

func (Format) String

func (f Format) String() string

String returns the human-readable name for the format.

type InputMode

type InputMode int

InputMode controls how content is passed to an external command.

const (
	InputStdin    InputMode = iota // Pass content via stdin (default).
	InputTempFile                  // Write to a temp file, replace {file} in args.
)

type MarkdownRenderer

type MarkdownRenderer struct {
	// contains filtered or unexported fields
}

MarkdownRenderer renders Markdown to HTML using goldmark.

func NewMarkdownRenderer

func NewMarkdownRenderer() *MarkdownRenderer

NewMarkdownRenderer creates a Markdown renderer with GFM extensions (tables, strikethrough, task lists, autolinks) and unsafe HTML passthrough.

func (*MarkdownRenderer) Render

func (r *MarkdownRenderer) Render(content []byte) (string, error)

type OrgRenderer

type OrgRenderer struct{}

OrgRenderer renders Org-mode files to HTML using go-org.

func NewOrgRenderer

func NewOrgRenderer() *OrgRenderer

NewOrgRenderer creates an Org-mode renderer.

func (*OrgRenderer) Render

func (r *OrgRenderer) Render(content []byte) (string, error)

type RSTRenderer

type RSTRenderer struct{}

RSTRenderer renders reStructuredText by trying rst2html first, then falling back to rst2html.py.

func NewRSTRenderer

func NewRSTRenderer() *RSTRenderer

NewRSTRenderer creates a renderer for reStructuredText.

func (*RSTRenderer) Render

func (r *RSTRenderer) Render(content []byte) (string, error)

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry maps file extensions and filenames to renderers.

func NewDefaultRegistry

func NewDefaultRegistry() *Registry

NewDefaultRegistry creates a registry with all supported renderers. Native renderers (Markdown, Org-mode) are always available. External renderers (AsciiDoc, reStructuredText, Pod) are registered but return ErrToolNotFound if the required tool is not on $PATH.

func NewRegistry

func NewRegistry() *Registry

NewRegistry creates an empty registry with the default max input size (25MB).

func (*Registry) Register

func (r *Registry) Register(format Format, renderer Renderer, extensions ...string)

Register adds a renderer for the given format and file extensions. Extensions should include the leading dot (e.g. ".md").

func (*Registry) RegisterFilename

func (r *Registry) RegisterFilename(format Format, renderer Renderer, filenames ...string)

RegisterFilename adds a renderer for an exact filename match.

func (*Registry) Render

func (r *Registry) Render(filename string, content []byte) (Result, error)

Render converts markup content to HTML based on the filename. Returns ErrUnsupportedFormat if no renderer matches. Returns ErrToolNotFound if the format needs an external tool that isn't installed.

func (*Registry) SetMaxInputSize

func (r *Registry) SetMaxInputSize(n int)

SetMaxInputSize sets the maximum allowed input size in bytes. Zero disables the limit.

func (*Registry) Supported

func (r *Registry) Supported(filename string) bool

Supported returns true if the filename can be rendered by this registry, including checking that any required external tools are installed.

type Renderer

type Renderer interface {
	Render(content []byte) (string, error)
}

Renderer converts markup content to HTML.

type RendererFunc

type RendererFunc func(content []byte) (string, error)

RendererFunc adapts a function to the Renderer interface.

func (RendererFunc) Render

func (f RendererFunc) Render(content []byte) (string, error)

type Result

type Result struct {
	HTML   string
	Format Format
}

Result holds the rendered output.

Jump to

Keyboard shortcuts

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