markdown

package
v0.0.0-...-374cbe0 Latest Latest
Warning

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

Go to latest
Published: Jun 11, 2026 License: MIT Imports: 16 Imported by: 0

README

pkg/markdown

Functional core for the markdown (alias md) command. All exports are pure functions; the only I/O lives in cmd/markdown.go (imperative shell).

Pipeline

src bytes
  └─ StripFrontmatter ─▶ ExtractTitle ─▶ RenderMarkdown ─▶ ChromaCSS ─▶ BuildPage ─▶ html string
                      └─ ExtractLinks ─▶ LinksFooter ──────────────────────┘

Files

File Exports Role
paths.go ResolveOutputPath, OutputTarget, ResolveOutputTarget Compute the output destination. OutputTarget{Path, Temp} names either a concrete path or an os.CreateTemp pattern. ResolveOutputTarget applies precedence: --output wins and is never temporary; --open alone yields a temp pattern <base>-*.html (nameless/dotfile inputs fall back to "markdown"); otherwise delegates to the unchanged sibling rule in ResolveOutputPath. The directory portion of the input path is stripped from the temp pattern.
types.go RenderConfig, NewRenderConfig Validated configuration record: InputPath string, Output OutputTarget, Open bool. Open drives the browser-open step; Output.Temp only selects the destination. Built from CLI args by NewRenderConfig.
frontmatter.go StripFrontmatter, ExtractTitle Strip a leading ----delimited YAML block. Title comes from the first non-empty ATX H1; falls back to the supplied default when none is found.
convert.go RenderMarkdown goldmark + GFM with a custom code-block renderer registered at priority 100 (beats the default 1000). mermaid fences pass through as <pre class="mermaid"> (with util.EscapeHTML on the source); all other fences run through chroma.
chroma.go ChromaCSS Emit the class-based chroma stylesheet for the tokyonight-night style.
links.go Link, ExtractLinks, LinksFooter Walk the goldmark+GFM AST to collect external (http/https) inline links, reference links, autolinks, and images; deduplicated by URL, first occurrence wins, document order. Code fences produce no link nodes. LinksFooter renders a <footer class="links"> with a numbered <ol>; label falls back to URL; images are marked <em>(image)</em>; returns "" when there are no links so the placeholder collapses.
page.go BuildPage Replace {{TITLE}}, {{PAGE_CSS}}, {{CHROMA_CSS}}, {{BODY}}, {{LINKS}} in template.html in a single strings.NewReplacer pass.
template.html (embedded via //go:embed) HTML scaffold with the mermaid.js <script type="module"> block.
styles.css (embedded via //go:embed) Tokyonight-night palette, monospace body, heading colour ramp, yellow inline code, mermaid block frame, links footer (top border, dim heading, smaller font, word-break on URLs), wide media (tables, standalone images, and mermaid blocks may grow past the 96ch text column up to --wide: min(140ch, 100vw - 3rem), centered on the column; inline images stay inline).

Notes

  • The if !entering { return ast.WalkContinue, nil } guard in renderFencedCodeBlock must remain. goldmark's ast.Walk still fires the exit pass for code blocks regardless of WalkSkipChildren, so the guard prevents emitting the block twice. (Reviewers occasionally flag it as dead code — it isn't.)
  • RenderMarkdown enables goldmarkhtml.WithUnsafe() so the <pre class="mermaid"> output reaches the page unescaped.
  • Mermaid is loaded from https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js at view time; diagram rendering needs network access.
  • mermaid.initialize sets useMaxWidth: false per diagram type so each SVG gets its natural pixel width. The pre.mermaid frame (width: fit-content, capped at --wide) then tracks the diagram instead of mermaid scaling it down to the text column; diagrams wider than the cap scroll inside the frame.
  • The chroma style name is the single constant chromaStyleName = "tokyonight-night" in chroma.go; change it there to retheme highlighted code.

Tests

Each *.go file has a sibling *_test.go covering the pure function it exports. Run them with:

go test ./pkg/markdown

See also: cmd/README.md — the command-level perspective (flags, output rules, end-to-end behaviour).

Documentation

Overview

Copyright © 2024 Guzmán Monné guzman.monne@cloudbridge.com.uy

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildPage

func BuildPage(body, title, chromaCSS, linksHTML string) string

BuildPage assembles a complete HTML document from a rendered body fragment, a page title, a generated chroma stylesheet, and an optional links footer (pass "" for documents without external links).

func ChromaCSS

func ChromaCSS() (string, error)

ChromaCSS generates the class-based stylesheet for highlighted code fences.

func ExtractTitle

func ExtractTitle(src []byte, fallback string) string

ExtractTitle returns the text of the first ATX H1 heading ("# ..."). Without an H1, the fallback is returned.

func LinksFooter

func LinksFooter(links []Link) string

LinksFooter renders the Links footer section for a page. An empty slice yields an empty string so the template placeholder collapses cleanly.

func RenderMarkdown

func RenderMarkdown(src []byte) (string, error)

RenderMarkdown converts Markdown source into an HTML body fragment.

func ResolveOutputPath

func ResolveOutputPath(inputPath, outputFlag string) string

ResolveOutputPath determines where the HTML output is written. An empty outputFlag swaps the input file's extension for ".html"; an extensionless input gains ".html". A set outputFlag is returned verbatim.

func StripFrontmatter

func StripFrontmatter(src []byte) []byte

StripFrontmatter removes a leading YAML frontmatter block delimited by "---" lines. Input without a complete frontmatter block is returned unchanged.

Types

type Link struct {
	Text    string
	URL     string
	IsImage bool
}

Link is one external reference found in a Markdown document.

func ExtractLinks(src []byte) []Link

ExtractLinks parses src with the same GFM parser used for rendering and returns every external (http/https) link, autolink, and image, in document order, deduplicated by URL with the first occurrence winning (a URL appearing as both link and image keeps only its first form). Code fences produce no link nodes, so their contents are ignored by construction.

type OutputTarget

type OutputTarget struct {
	Path string
	Temp bool
}

OutputTarget says where the rendered HTML goes. Temp=false: Path is the concrete output path. Temp=true: Path is an os.CreateTemp pattern like "doc-*.html"; the imperative shell turns it into a real file in the OS temp directory.

func ResolveOutputTarget

func ResolveOutputTarget(inputPath, outputFlag string, open bool) OutputTarget

ResolveOutputTarget decides the output destination. An explicit outputFlag always wins and is never temporary. Without it, open renders to a temporary file so the source directory stays clean; otherwise the sibling rule from ResolveOutputPath applies.

When constructing the temp pattern the directory portion of inputPath is stripped — only the basename without its extension feeds the pattern. Nameless inputs (empty string, dotfiles) fall back to "markdown".

type RenderConfig

type RenderConfig struct {
	InputPath string
	Output    OutputTarget
	Open      bool
}

RenderConfig holds the fully resolved configuration for one render run. Open drives the browser-open step in the shell; it is distinct from Output.Temp, which only selects the destination.

func NewRenderConfig

func NewRenderConfig(inputPath, outputFlag string, open bool) RenderConfig

NewRenderConfig resolves raw CLI inputs into a valid RenderConfig. The output destination is decided once, at the boundary, so the rest of the program holds only valid state.

Jump to

Keyboard shortcuts

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