parser

package
v2.0.4 Latest Latest
Warning

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

Go to latest
Published: Jan 18, 2026 License: GPL-3.0 Imports: 16 Imported by: 0

Documentation

Overview

Package parser reads markdown files with YAML frontmatter and converts them to Post objects for the GoBlog system.

The parser uses goldmark for markdown processing and supports:

  • YAML frontmatter for post metadata
  • Syntax highlighting for code blocks
  • Footnotes
  • Auto-generated heading IDs
  • HTML sanitization

Basic usage:

import (
	"os"
	"github.com/harrydayexe/GoBlog/pkg/parser"
)

// Parse a single file
p := parser.New()
post, err := p.ParseFile(os.DirFS("/path/to/posts"), "my-post.md")
if err != nil {
	// handle error
}

// Parse all markdown files in a directory
posts, err := p.ParseDirectory(os.DirFS("/path/to/posts"))
if err != nil {
	// handle error - may be ParseErrors with partial results
}

Configuration

The parser can be customized using functional options:

// Disable code highlighting
p := parser.New(parser.WithCodeHighlighting(false))

// Change syntax highlighting style
p := parser.New(parser.WithCodeHighlightingStyle("dracula"))

// Enable footnote support
p := parser.New(parser.WithFootnote())

// Combine multiple options
p := parser.New(
    parser.WithCodeHighlightingStyle("monokai"),
    parser.WithFootnote(),
)

See the Option functions for all available configuration options.

A Parser is safe for concurrent use by multiple goroutines after creation.

Example
p := New()

fsys := os.DirFS("testdata")
post, err := p.ParseFile(context.Background(), fsys, "valid-post.md")
if err != nil {
	fmt.Println("Error:", err)
	return
}

fmt.Println(post.Title)
Output:

A Valid Blog Post
Example (WithOptions)
// Configure parser with custom options
p := New(
	WithCodeHighlightingStyle("dracula"),
	WithFootnote(),
)

fsys := os.DirFS("testdata")
post, err := p.ParseFile(context.Background(), fsys, "with-code.md")
if err != nil {
	fmt.Println("Error:", err)
	return
}

fmt.Println("Configured parser")
fmt.Println("Has content:", len(post.Content) > 0)
Output:

Configured parser
Has content: true

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config added in v2.0.3

type Config struct {
	// EnableCodeHighlighting controls whether ringfenced code blocks should be
	// highlighted or not
	EnableCodeHighlighting bool
	// CodeHighlightingStyle to use for ringfenced code blocks.
	//
	// Note that currently Chroma is used so you should refer to the styles in
	// their [documentation] and [source code]
	//
	// [documentation]: https://pkg.go.dev/github.com/alecthomas/chroma
	// [source code]: https://github.com/alecthomas/chroma/tree/master/styles
	CodeHighlightingStyle string

	// EnableFootnote controls whether the parser should allow the use of PHP
	// Markdown Extra Footnotes.
	EnableFootnote bool
}

Config contains all the options for the Parser to use when reading and parsing markdown files.

type FileError

type FileError struct {
	Path string // Path to the file that caused the error
	Err  error  // The underlying error
}

FileError represents an error that occurred while parsing a specific file.

func (FileError) Error

func (fe FileError) Error() string

Error implements the error interface.

func (FileError) Unwrap

func (fe FileError) Unwrap() error

Unwrap returns the underlying error for error wrapping support.

type Option

type Option func(*Config)

Option is a function which can update the parser config

func WithCodeHighlighting

func WithCodeHighlighting(enable bool) Option

WithCodeHighlighting enables the highlighting of ringfenced code blocks

Note that currently Chroma is used under-the-hood, so you should refer to the styles in their documentation and source code

Example
// Disable code highlighting
p := New(WithCodeHighlighting(false))

fsys := os.DirFS("testdata")
post, err := p.ParseFile(context.Background(), fsys, "with-code.md")
if err != nil {
	fmt.Println("Error:", err)
	return
}

fmt.Println("Highlighting disabled")
fmt.Println("Post parsed:", post.Title != "")
Output:

Highlighting disabled
Post parsed: true

func WithCodeHighlightingStyle

func WithCodeHighlightingStyle(style string) Option

WithCodeHighlightingStyle sets the code highlighting style for ringfenced code blocks and enables code highlighting if it wasn't already

Note that currently Chroma is used under-the-hood, so you should refer to the styles in their documentation and source code

Example
// Use a different syntax highlighting style
p := New(WithCodeHighlightingStyle("dracula"))

fsys := os.DirFS("testdata")
post, err := p.ParseFile(context.Background(), fsys, "with-code.md")
if err != nil {
	fmt.Println("Error:", err)
	return
}

fmt.Println("Custom highlighting style")
fmt.Println("Post parsed:", post.Title != "")
Output:

Custom highlighting style
Post parsed: true

func WithFootnote

func WithFootnote() Option

WithFootnote enables the use of PHP Markdown Extra Footnotes.

Footnotes allow you to add references and notes without cluttering the main text. Use [^1] in your text and define footnotes with [^1]: Your footnote text.

See the PHP Markdown Extra documentation for syntax details: https://michelf.ca/projects/php-markdown/extra/#footnotes

Example
// Enable PHP Markdown Extra footnotes
p := New(WithFootnote())

fsys := os.DirFS("testdata")
post, err := p.ParseFile(context.Background(), fsys, "with-footnotes.md")
if err != nil {
	fmt.Println("Error:", err)
	return
}

fmt.Println("Footnotes enabled")
fmt.Println("Post parsed:", post.Title != "")
Output:

Footnotes enabled
Post parsed: true

type ParseErrors

type ParseErrors struct {
	Errors []FileError
}

ParseErrors aggregates multiple parsing errors encountered during directory-wide parsing operations.

ParseErrors is returned when one or more files fail to parse, but other files may have been successfully parsed. Check the Errors slice to see all individual failures.

func (ParseErrors) Error

func (pe ParseErrors) Error() string

Error implements the error interface, returning a formatted multi-line error message listing all file errors.

func (ParseErrors) HasErrors

func (pe ParseErrors) HasErrors() bool

HasErrors returns true if there are any errors in the collection.

type Parser

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

Parser reads markdown files and converts them to Post objects. A Parser is safe for concurrent use after creation.

func New

func New(opts ...Option) *Parser

New creates a new Parser with the specified options. The parser is configured with: - YAML frontmatter parsing - Syntax highlighting for code blocks (enabled by default, use WithCodeHighlighting to disable) - Optional footnote support (disabled by default, use WithFootnote to enable) - Auto-generated heading IDs - HTML sanitization (unsafe HTML disabled by default)

func NewWithConfig added in v2.0.3

func NewWithConfig(config *Config) *Parser

func (*Parser) ParseDirectory

func (p *Parser) ParseDirectory(ctx context.Context, fsys fs.FS) (models.PostList, error)

ParseDirectory walks the filesystem and parses all .md files found. It returns a PostList containing all successfully parsed posts, sorted by date (newest first).

If any files fail to parse, ParseDirectory continues processing remaining files and returns both the valid posts and a ParseErrors containing all failures. Callers can check the error and decide whether to proceed with partial results or fail entirely.

Only files with .md or .markdown extensions are processed. Other files and directories are silently skipped.

Example
p := New(WithFootnote())
fsys := os.DirFS("testdata")

posts, err := p.ParseDirectory(context.Background(), fsys)

// Partial results may be returned with errors
if err != nil {
	if parseErrs, ok := err.(ParseErrors); ok {
		fmt.Printf("Parsed with some errors\n")
		fmt.Printf("Valid posts: %d\n", len(posts))
		fmt.Printf("Errors: %d\n", len(parseErrs.Errors))
		return
	}
}

fmt.Printf("Valid posts: %d\n", len(posts))
Output:

Parsed with some errors
Valid posts: 3
Errors: 4

func (*Parser) ParseFile

func (p *Parser) ParseFile(ctx context.Context, fsys fs.FS, path string) (*models.Post, error)

ParseFile reads and parses a single markdown file from the filesystem. It extracts frontmatter metadata, validates required fields, renders the markdown content to HTML, and returns a fully populated Post.

The file must contain YAML frontmatter with at minimum: title, date, and description. The markdown body is rendered to HTML with syntax highlighting and footnote support.

Returns an error if the file cannot be read, frontmatter is invalid, required fields are missing, or markdown rendering fails.

Example
p := New()
fsys := os.DirFS("testdata")

post, err := p.ParseFile(context.Background(), fsys, "valid-post.md")
if err != nil {
	fmt.Println("Error:", err)
	return
}

fmt.Printf("Title: %s\n", post.Title)
fmt.Printf("Tags: %d\n", len(post.Tags))
Output:

Title: A Valid Blog Post
Tags: 3

Jump to

Keyboard shortcuts

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