markdown

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 5 Imported by: 0

README

markdown

Markdown document reader, frontmatter parser and section updater. Zero dependencies.

The package does no I/O of its own: the caller injects the read and write functions, so the same Doc works over the filesystem, an embed.FS, or an in-memory buffer.

import "github.com/tinywasm/markdown"

Doc

New(rootDir, destination, writeFile) returns a Doc. Set its input with one of:

Input Use for
InputPath(path, readFile) a file on disk
InputByte(content) markdown already in memory
InputEmbed(path, readFile) a path inside an embed.FS (or any reader)

Extract fenced code blocks

Extract(outputFile) concatenates every fenced block whose language matches the output file's extension and writes the result to destination/outputFile. Blocks of any other language are ignored.

Extension Fence language
.go ```go
.js ```javascript
.css ```css
code := ""
capture := func(name string, data []byte) error { code = string(data); return nil }

err := markdown.New("", ".", capture).
    InputByte(template).
    Extract("main.go")

The output file is left untouched when the extracted code is identical, so regenerating does not bump its mtime and does not retrigger file watchers.

Frontmatter

ParseFrontmatter(content) returns the leading --- block as a map[string]string.

meta, err := markdown.New(".", "", nil).
    InputPath("docs/PLAN.md", os.ReadFile).
    Frontmatter()

meta["tag"] // "v0.2.0"

Rules: the block must start at byte 0 with --- and close at the next ---. Each line is a key: value pair split on the first colon (so a URL value keeps its own colons); lines without a colon are skipped; surrounding quotes are stripped; CRLF is tolerated.

Which keys are required is not this package's business — that belongs to whoever defines the document's schema. An empty block is valid and parses to an empty map. The only errors are structural:

Error When
ErrFrontmatterMissing the document does not open with ---
ErrFrontmatterUnclosed the opening --- has no closing ---

Update a delimited section

UpdateSection(id, content, afterLine...) creates or replaces the block between <!-- START_SECTION:id --> and <!-- END_SECTION:id -->, writing the document back.

err := markdown.New(".", ".", writeFile).
    InputPath("README.md", os.ReadFile).
    UpdateSection("BADGES_SECTION", badges, "1") // place it after line 1
  • Duplicate sections with the same id are collapsed into one.
  • afterLine is a 1-based line number used when the section does not exist yet, or to relocate it; without it, a new section is appended at the end.
  • The document is left untouched when the section is already up to date.

License

MIT

Documentation

Overview

Package markdown reads markdown documents, extracts fenced code blocks, parses frontmatter and updates delimited sections in place.

It has zero dependencies and does no I/O of its own: the caller injects the read and write functions, so the same Doc works over the filesystem, an embed.FS, or an in-memory buffer.

Index

Constants

View Source
const Fence = "---"

Fence opens and closes a frontmatter block.

Variables

View Source
var (
	// ErrFrontmatterMissing: the document does not open with a Fence line.
	ErrFrontmatterMissing = errors.New("frontmatter: file must start with a '---' line")
	// ErrFrontmatterUnclosed: the opening Fence has no matching closing Fence.
	ErrFrontmatterUnclosed = errors.New("frontmatter: opening '---' has no matching closing '---'")
)
View Source
var ErrNoDestination = fmt.Errorf("markdown: destination not set; provide it to New(rootDir, destination, writeFile)")

ErrNoDestination is returned by Extract when the Doc has no output directory.

View Source
var ErrNoReader = errors.New("markdown: no input configured; call InputPath, InputByte or InputEmbed")

ErrNoReader is returned when a Doc is used before an input source is set.

View Source
var ErrSectionArgs = errors.New("markdown: sectionID and content are required")

ErrSectionArgs is returned by UpdateSection when sectionID or content is empty.

Functions

func ParseFrontmatter added in v0.0.2

func ParseFrontmatter(content string) (map[string]string, error)

ParseFrontmatter parses the leading frontmatter block of content and returns its key/value pairs.

Rules: the block must start at byte 0 with a "---" line and close at the next "---" line. Between them, each non-empty line is a "key: value" pair split on the first ':'; lines without a ':' are skipped; surrounding single or double quotes are stripped from values. CRLF endings are tolerated.

Which keys are required is NOT this package's business — the caller decides. An empty block parses to an empty (non-nil) map with no error.

Types

type Doc added in v0.0.2

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

Doc is a markdown document with a configured input source and output target.

func New

func New(rootDir, destination string, writeFile WriteFunc) *Doc

New creates a Doc rooted at rootDir that writes its output under destination. The input source must be set with InputPath, InputByte or InputEmbed.

func (*Doc) Extract added in v0.0.2

func (d *Doc) Extract(outputFile string) error

Extract concatenates every fenced code block of the language implied by outputFile's extension and writes the result to destination/outputFile. The file is left untouched when the extracted code is already identical.

func (*Doc) Frontmatter added in v0.0.2

func (d *Doc) Frontmatter() (map[string]string, error)

Frontmatter reads the configured input and parses its frontmatter block.

func (*Doc) InputByte added in v0.0.2

func (d *Doc) InputByte(content []byte) *Doc

InputByte sets the input to an in-memory markdown document.

func (*Doc) InputEmbed added in v0.0.2

func (d *Doc) InputEmbed(path string, readFile ReadFunc) *Doc

InputEmbed sets the input to a path inside any reader (e.g. an embed.FS).

func (*Doc) InputPath added in v0.0.2

func (d *Doc) InputPath(pathFile string, readFile ReadFunc) *Doc

InputPath sets the input to a file path read through readFile.

func (*Doc) SetLog added in v0.0.2

func (d *Doc) SetLog(fn func(...any))

SetLog sets a custom logger. A nil fn is ignored.

func (*Doc) UpdateSection added in v0.0.2

func (d *Doc) UpdateSection(sectionID, content string, afterLine ...string) error

UpdateSection creates or replaces the block delimited by <!-- START_SECTION:id --> ... <!-- END_SECTION:id --> in the input document, writing it back through the Doc's write function.

Duplicate sections with the same id are collapsed into one. afterLine is an optional 1-based line number telling where to place the section when it does not exist yet; without it, a new section is appended at the end. The document is left untouched when the section is already up to date.

type ReadFunc added in v0.0.2

type ReadFunc func(name string) ([]byte, error)

ReadFunc reads the named document. WriteFunc persists data under name.

type WriteFunc added in v0.0.2

type WriteFunc func(name string, data []byte) error

ReadFunc reads the named document. WriteFunc persists data under name.

Jump to

Keyboard shortcuts

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