gomark

package module
v0.1.22 Latest Latest
Warning

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

Go to latest
Published: Jun 7, 2026 License: MIT Imports: 29 Imported by: 0

README

gomark

Go Reference Go Report Card CI

GoMark splash

Docs for Go libraries where every example actually runs.

GoMark turns a folder of markdown into a real documentation site — and the Go code blocks in your docs run live, right in the reader's browser. No playground server, no backend, no infrastructure: execution happens client-side via a WebAssembly build of the yaegi interpreter, so your examples stay honest and your servers stay boring.

package main

func main() {
	println("Hello, Gophers!") // edit me and hit Run
}

The block above is runnable on gomark.dev — a site built with GoMark itself. Try editing it and clicking Run.

Point it at your markdown and ship. Routing, navigation, search, sitemap, robots, and the in-browser runner are all built in.

Read the docs at gomark.dev.

Install

go install github.com/arivictor/gomark/cmd/gomark@latest

Quick start

Create a content directory and add markdown files. The file structure maps to the URL structure — content/docs/hello.md is served at /docs/hello, and index.md files serve at their folder path.

# Hello, World!

Welcome to my docs site.

Preview it locally with live reload, then build a static site:

# Dev server: renders live and auto-reloads the browser as you edit
gomark serve ./content --live

# Production: render to a static site you can host anywhere
gomark build ./content ./dist --url https://docs.example.com

The output of gomark build is plain HTML/CSS/JS that runs on any static host — GitHub Pages, Netlify, S3, nginx. There is no server to run in production, and the Go runner executes entirely in the reader's browser. See the deployment guide for GitHub Pages, container, and other host recipes.

Configure

Site title, logo, SEO (Open Graph / Twitter / description), navigation, social links, analytics, and build options live in an optional gomark.yaml that both build and serve read. It's auto-discovered in your project (or pass --config):

title: My Docs
url: https://docs.example.com
logo:
  light: /logo-light.png
  dark: /logo-dark.png
seo:
  description: Docs for my Go library.
  twitter_site: "@myhandle"
nav:
  - label: GitHub
    url: https://github.com/me/my-docs
analytics:
  provider: plausible
  id: docs.example.com

CLI flags override environment variables, which override the file, which overrides the defaults. There are no custom layouts or CSS — every site uses the built-in theme. See the configuration guide for the full schema.

Use it as a library

GoMark is also a single importable package, github.com/arivictor/gomark, if you'd rather drive it from Go:

package main

import (
	"log"

	gm "github.com/arivictor/gomark"
)

func main() {
	s := gm.NewSite(
		gm.WithSiteTitle("My Docs"),
		gm.WithSiteContentDir("content"),
	)

	// Build a static site...
	if err := s.Export("dist"); err != nil {
		log.Fatal(err)
	}
	// ...or run the dev server: s.Serve(":8080", true)
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrInvalidMarkdownPath = errors.New("invalid markdown path")
	ErrMarkdownNotFound    = errors.New("markdown file not found")
)

Functions

func DiscoverConfigFile added in v0.1.13

func DiscoverConfigFile(dirs ...string) string

DiscoverConfigFile returns the path to the first gomark.yaml (or gomark.yml) found in the given directories, or "" if none exists.

func LoggingMiddleware

func LoggingMiddleware(next http.Handler) http.Handler

Types

type AnalyticsConfig added in v0.1.13

type AnalyticsConfig struct {
	Provider string `yaml:"provider"`
	ID       string `yaml:"id"`
}

AnalyticsConfig configures an optional analytics snippet. Provider is one of "ga4", "gtm", or "plausible"; ID is the measurement/container/domain id.

type App

type App struct {
	Title string
	// Lang sets the <html lang> attribute (default "en").
	Lang string
	// ThemeColor sets the <meta name="theme-color"> value. When empty the
	// built-in light/dark defaults (#ffffff / #000000) are used.
	ThemeColor string
	// Description is the default meta/Open Graph description used for pages
	// that do not set one via frontmatter.
	Description string
	// LogoLight and LogoDark are the brand logo URLs for the light and dark
	// themes. When both are set the page swaps between them with the theme;
	// when neither is set the bundled logo is used.
	LogoLight string
	LogoDark  string
	// SEO image metadata.
	OGImagePath      string
	TwitterImagePath string
	TwitterSite      string // @handle for twitter:site
	TwitterCreator   string // @handle for twitter:creator
	ImageAlt         string // og:image:alt (defaults to the site title)
	// Footer is optional free text shown in the site footer.
	Footer string
	// NavLinks are top-of-page navigation links; SocialLinks render in the
	// footer. Both are fully configurable and replace nothing derived from the
	// content tree.
	NavLinks    []ConfigLink
	SocialLinks []ConfigLink
	// Analytics configures an optional analytics snippet injected on every page.
	Analytics    AnalyticsConfig
	ContentDir   string
	SidebarDepth int
	SiteURL      string
	ExportDir    string
	// PublicDir is an optional on-disk directory whose files are overlaid on
	// top of the bundled assets: a file present here wins over the built-in one
	// of the same name, and new files are served/exported alongside them. Use it
	// to supply your own favicons, og-image, logos, or site.webmanifest without
	// forking gomark. When empty only the embedded assets are used.
	PublicDir string
	Mode      RenderMode
	// DisableRunner turns off the in-browser Go runner. Execution is
	// client-side (a WebAssembly build of the yaegi interpreter), so the
	// runner is on by default and needs no external service; set this to hide
	// the "Run" controls entirely.
	DisableRunner bool
	// DisableSitemap and DisableRobots suppress generation of sitemap.xml and
	// robots.txt respectively. Both are generated by default.
	DisableSitemap bool
	DisableRobots  bool
}

func (*App) GetRunnerEnabled

func (a *App) GetRunnerEnabled() bool

GetRunnerEnabled reports whether the client-side runner should be active. It defaults to on (execution is client-side, so there is nothing to provision) and can be turned off via WithSiteRunnerEnabled(false) or PLAYGROUND_ENABLED.

type BadRequestError

type BadRequestError struct {
	Message string
}

func (*BadRequestError) Error

func (e *BadRequestError) Error() string

type BuildConfig added in v0.1.13

type BuildConfig struct {
	ContentDir   string `yaml:"content_dir"`
	OutputDir    string `yaml:"output_dir"`
	PublicDir    string `yaml:"public_dir"`
	SidebarDepth int    `yaml:"sidebar_depth"`
	Runner       *bool  `yaml:"runner"`
	Sitemap      *bool  `yaml:"sitemap"`
	Robots       *bool  `yaml:"robots"`
}

BuildConfig holds build/render behaviour. Pointer bools distinguish "unset" (use the default) from an explicit true/false.

type ConfigLink struct {
	Label string `yaml:"label"`
	URL   string `yaml:"url"`
	Icon  string `yaml:"icon"`
}

ConfigLink is a labelled hyperlink used for the top navigation and footer social links. Icon is an optional lucide icon name (rendered for social links).

type ContentIndex

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

ContentIndex holds every page, built once at startup, and renders the position-relative sidebar for a given route.

func BuildContentIndex

func BuildContentIndex(dir string) (*ContentIndex, error)

BuildContentIndex walks the content dir and records each page's title (from frontmatter) and position, so the sidebar can be built without re-reading files.

func (*ContentIndex) Siblings added in v0.1.22

func (idx *ContentIndex) Siblings(route string) (prev, next *NavLink)

Siblings returns the "previous" and "next" entries for route, drawn from its sibling pages (same parent folder), in the same order the sidebar lists them (frontmatter "order", falling back to NavTitle — nav_title when set, else title — to match the sidebar). Either may be nil at the ends of the list, or if the route isn't a recognized page.

func (*ContentIndex) Sidebar

func (idx *ContentIndex) Sidebar(currentRoute string, depth int) (string, []NavNode)

Sidebar returns the heading and node tree anchored at content root for every route, so navigating into a folder keeps the same overall accordion tree.

func (*ContentIndex) TopNav

func (idx *ContentIndex) TopNav() []NavLink

TopNav returns the content root's top-level entries for the header: each top-level folder that has an index.md plus any root-level pages. The root index.md itself is omitted (it's the brand/home link).

type ErrorResponder

type ErrorResponder interface {
	Handle(http.ResponseWriter, *http.Request, error)
}

type FileConfig added in v0.1.13

type FileConfig struct {
	Title      string          `yaml:"title"`
	URL        string          `yaml:"url"`
	Lang       string          `yaml:"lang"`
	ThemeColor string          `yaml:"theme_color"`
	Footer     string          `yaml:"footer"`
	SEO        SEOConfig       `yaml:"seo"`
	Build      BuildConfig     `yaml:"build"`
	Nav        []ConfigLink    `yaml:"nav"`
	Social     []ConfigLink    `yaml:"social"`
	Analytics  AnalyticsConfig `yaml:"analytics"`
}

FileConfig is the declarative YAML configuration schema (gomark.yaml). It is an optional alternative to the WithSite* options: the `gomark` CLI loads it for both `build` and `serve`. Custom layouts and CSS are intentionally not configurable — every site uses the built-in theme.

Resolution precedence, highest first:

CLI flag > environment variable > gomark.yaml > built-in default

func LoadConfigFile added in v0.1.13

func LoadConfigFile(path string) (*FileConfig, error)

LoadConfigFile reads and parses a gomark.yaml file. Unknown keys (usually a typo such as `tittle:`) are reported as warnings rather than silently ignored, then the file is parsed leniently so the remaining valid keys still apply.

func (*FileConfig) Options added in v0.1.13

func (c *FileConfig) Options() []SiteOption

Options converts the file config into SiteOptions. Only fields that are set produce an option, so a FileConfig overrides defaults but leaves room for higher-precedence options (env, CLI flags) appended after it.

type FileTemplateRenderer

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

func NewFileTemplateRenderer

func NewFileTemplateRenderer(layoutPath, pageGlob string) (*FileTemplateRenderer, error)

func (*FileTemplateRenderer) Render

func (r *FileTemplateRenderer) Render(w http.ResponseWriter, name string, data PageData) error

func (*FileTemplateRenderer) RenderStatus

func (r *FileTemplateRenderer) RenderStatus(w http.ResponseWriter, status int, name string, data PageData) error

func (*FileTemplateRenderer) RenderTo added in v0.1.12

func (r *FileTemplateRenderer) RenderTo(w io.Writer, name string, data PageData) error

RenderTo writes the named page (wrapped in the layout) to any io.Writer. It is the shared rendering primitive behind both the HTTP handler and the static exporter, so served and exported HTML are identical.

type HTMLErrorResponder

type HTMLErrorResponder struct {
	Renderer         TemplateRenderer
	TopNav           []NavLink
	SiteName         string
	Lang             string
	ThemeColor       string
	LogoLight        string
	LogoDark         string
	SiteURL          string
	OGImagePath      string
	TwitterImagePath string
	TwitterSite      string
	TwitterCreator   string
	ImageAlt         string
	Footer           string
	NavLinks         []ConfigLink
	SocialLinks      []ConfigLink
	Analytics        AnalyticsConfig
	Logger           *log.Logger
}

func (HTMLErrorResponder) Handle

func (r HTMLErrorResponder) Handle(w http.ResponseWriter, req *http.Request, err error)

type HTTPError

type HTTPError struct {
	Status  int
	Message string
}

func (*HTTPError) Error

func (e *HTTPError) Error() string

type HandlerFunc

type HandlerFunc func(http.ResponseWriter, *http.Request) error

type Heading

type Heading struct {
	Level int
	Text  string
	ID    string
}

Heading is a single in-page heading collected during rendering, used to build the on-page table of contents.

type LogoConfig added in v0.1.13

type LogoConfig struct {
	Light string `yaml:"light"`
	Dark  string `yaml:"dark"`
}

LogoConfig holds per-theme brand logo URLs.

type MarkdownRenderer

type MarkdownRenderer interface {
	Render(markdown string) (html string, headings []Heading)
}

type MarkdownService

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

func NewMarkdownService

func NewMarkdownService(renderer MarkdownRenderer, contentDir string) MarkdownService

func (MarkdownService) LoadAndRender

func (s MarkdownService) LoadAndRender(slug string) (RenderedPage, error)

type Middleware

type Middleware func(http.Handler) http.Handler

func CSRFProtectionMiddleware added in v0.1.10

func CSRFProtectionMiddleware(siteURL string) Middleware

CSRFProtectionMiddleware blocks unsafe browser requests unless the request presents the matching token and comes from the same origin as the site.

type NavLink struct {
	Title  string
	Path   string
	Active bool
}

NavLink is a single entry in the header's top-level nav.

type NavNode struct {
	Title    string
	Path     string // link target; empty for a folder without an index.md
	Icon     string // optional Lucide icon name from frontmatter
	NodeID   string // stable id for folder accordion controls
	Folder   bool   // a directory (styled as a header) vs. a leaf page
	Active   bool
	Open     bool
	Children []NavNode
}

NavNode is one entry in the sidebar tree. Folders may carry Children; pages are leaves. Active marks the exact current page; Open marks an ancestor of it.

type PageData

type PageData struct {
	StatusCode      int
	Title           string
	Description     string
	SiteName        string
	Lang            string
	ThemeColor      string
	LogoLightURL    string
	LogoDarkURL     string
	CanonicalURL    string
	OGImageURL      string
	TwitterImageURL string
	TwitterSite     string
	TwitterCreator  string
	ImageAlt        string
	FooterText      string
	NavLinks        []ConfigLink
	SocialLinks     []ConfigLink
	Analytics       AnalyticsConfig
	RunnerEnabled   bool
	Robots          string
	Time            string
	CSRFToken       string
	MarkdownFile    string
	BodyHTML        template.HTML
	Headings        []Heading
	HideTOC         bool
	HideNav         bool
	NavTitle        string
	Nav             []NavNode
	TopNav          []NavLink
	CurrentPath     string
	StaticBuild     bool
	PrevPage        *NavLink
	NextPage        *NavLink
}

type RenderMode

type RenderMode string
const (
	LiveRender RenderMode = "live_render"
	PreRender  RenderMode = "pre_render"
)

func ParseRenderMode

func ParseRenderMode(raw string) RenderMode

type RenderedPage

type RenderedPage struct {
	Path        string
	HTML        string
	Title       string
	Description string
	Headings    []Heading
	HideTOC     bool
	HideNav     bool
}

RenderedPage is the result of loading a markdown file: its resolved path, the rendered HTML body, and metadata pulled from optional YAML frontmatter.

type SEOConfig added in v0.1.13

type SEOConfig struct {
	Description    string `yaml:"description"`
	OGImage        string `yaml:"og_image"`
	TwitterImage   string `yaml:"twitter_image"`
	TwitterSite    string `yaml:"twitter_site"`
	TwitterCreator string `yaml:"twitter_creator"`
	ImageAlt       string `yaml:"image_alt"`
}

SEOConfig holds the site-wide SEO defaults (Open Graph, Twitter cards, …).

type SearchEntry added in v0.1.12

type SearchEntry struct {
	Title string `json:"title"`
	Path  string `json:"path"`
	Body  string `json:"body"`
}

SearchEntry is one document in the static search index (search-index.json), consumed by the client-side search used in exported static builds.

type SearchIndex

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

SearchIndex is an in-memory index of markdown content.

func BuildSearchIndex

func BuildSearchIndex(contentDir string) (*SearchIndex, error)

BuildSearchIndex walks markdown files under contentDir and builds a search index.

func (*SearchIndex) Entries added in v0.1.12

func (idx *SearchIndex) Entries() []SearchEntry

Entries returns every indexed document for serialization into a static search index. The body is the same normalized text the server-side query scores on.

func (*SearchIndex) Query

func (idx *SearchIndex) Query(q string, limit int) []SearchResult

Query finds markdown pages matching q, ordered by a simple relevance score.

type SearchResult

type SearchResult struct {
	Title   string `json:"title"`
	Path    string `json:"path"`
	Snippet string `json:"snippet,omitempty"`
}

SearchResult is one matched markdown page returned by the search API.

type Server

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

func NewServer

func NewServer(errorResponder ErrorResponder) *Server

func (*Server) Handle

func (s *Server) Handle(method, path string, h HandlerFunc)

func (*Server) Run

func (s *Server) Run(addr string) error

func (*Server) Use

func (s *Server) Use(mw Middleware)

type Site

type Site struct {
	App App
	// contains filtered or unexported fields
}

func NewSite

func NewSite(options ...SiteOption) *Site

func (*Site) Export added in v0.1.12

func (s *Site) Export(outputDir string) error

Export pre-renders every content page to static HTML under outputDir, copies the public assets, and writes sitemap.xml, robots.txt, and search-index.json. The result is a self-contained static site that needs no Go server: any static host (GitHub Pages, S3, nginx) can serve it, and it works offline.

Because there is no HTTP request, absolute URLs (canonical, Open Graph) come from the configured site URL — set WithSiteURL for correct SEO metadata.

func (*Site) Serve added in v0.1.12

func (s *Site) Serve(addr string, live bool) error

Serve runs the local development server on addr. When live is true it renders each page on every request and live-reloads connected browsers as files under the content dir change. Serve is a development tool — production deployments build a static site with Export (see the `gomark build` CLI command).

func (*Site) Start

func (s *Site) Start() error

type SiteOption

type SiteOption func(*Site)

func WithSiteAddress

func WithSiteAddress(addr string) SiteOption

func WithSiteAnalytics added in v0.1.13

func WithSiteAnalytics(provider, id string) SiteOption

func WithSiteContentDir

func WithSiteContentDir(dir string) SiteOption

func WithSiteDescription added in v0.1.13

func WithSiteDescription(description string) SiteOption

func WithSiteExportDir added in v0.1.12

func WithSiteExportDir(dir string) SiteOption

WithSiteExportDir makes Start() export a static build into dir (instead of serving) — convenient for CI. Equivalent to setting the EXPORT_DIR env var or calling Site.Export(dir) directly.

func WithSiteFooter added in v0.1.13

func WithSiteFooter(footer string) SiteOption

func WithSiteImageAlt added in v0.1.13

func WithSiteImageAlt(alt string) SiteOption

func WithSiteLang added in v0.1.13

func WithSiteLang(lang string) SiteOption
func WithSiteLogo(logoURL string) SiteOption

WithSiteLogo sets a single brand logo used in both light and dark themes. Use WithSiteLogoLight / WithSiteLogoDark to vary the logo per theme.

func WithSiteLogoDark added in v0.1.13

func WithSiteLogoDark(logoURL string) SiteOption

func WithSiteLogoLight added in v0.1.13

func WithSiteLogoLight(logoURL string) SiteOption

func WithSiteMode

func WithSiteMode(mode RenderMode) SiteOption
func WithSiteNavLinks(links ...ConfigLink) SiteOption

func WithSiteOGImage added in v0.1.1

func WithSiteOGImage(imagePath string) SiteOption

func WithSitePublicDir

func WithSitePublicDir(dir string) SiteOption

WithSitePublicDir overlays an on-disk directory on top of the bundled static assets. Files in dir override the built-in asset of the same name (favicons, og-image, logos, site.webmanifest, …) and any extra files are served and exported too. This is how a site supplies its own SEO images and logos.

func WithSiteRobotsEnabled added in v0.1.13

func WithSiteRobotsEnabled(enabled bool) SiteOption

WithSiteRobotsEnabled toggles generation of robots.txt (enabled by default).

func WithSiteRunnerEnabled

func WithSiteRunnerEnabled(enabled bool) SiteOption

WithSiteRunnerEnabled toggles the in-browser Go runner. It is enabled by default; pass false to hide the "Run" controls across the site.

func WithSiteSidebarDepth

func WithSiteSidebarDepth(depth int) SiteOption

func WithSiteSitemapEnabled added in v0.1.13

func WithSiteSitemapEnabled(enabled bool) SiteOption

WithSiteSitemapEnabled toggles generation of sitemap.xml (enabled by default).

func WithSiteSocialLinks(links ...ConfigLink) SiteOption

func WithSiteThemeColor added in v0.1.13

func WithSiteThemeColor(color string) SiteOption

func WithSiteTitle

func WithSiteTitle(title string) SiteOption

func WithSiteTwitterCreator added in v0.1.13

func WithSiteTwitterCreator(handle string) SiteOption

func WithSiteTwitterImage added in v0.1.1

func WithSiteTwitterImage(imagePath string) SiteOption

func WithSiteTwitterSite added in v0.1.13

func WithSiteTwitterSite(handle string) SiteOption

func WithSiteURL

func WithSiteURL(siteURL string) SiteOption

type StdlibMarkdownRenderer

type StdlibMarkdownRenderer struct {
	RunnerEnabled bool
}

func (StdlibMarkdownRenderer) Render

func (s StdlibMarkdownRenderer) Render(markdown string) (string, []Heading)

type TemplateRenderer

type TemplateRenderer interface {
	Render(http.ResponseWriter, string, PageData) error
	RenderStatus(http.ResponseWriter, int, string, PageData) error
}

Directories

Path Synopsis
cmd
gomark command
Command gomark builds and previews markdown-powered documentation sites.
Command gomark builds and previews markdown-powered documentation sites.
wasm command
Command wasm is the client-side Go runner.
Command wasm is the client-side Go runner.

Jump to

Keyboard shortcuts

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