parser

package
v2.4.0-beta3 Latest Latest
Warning

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

Go to latest
Published: May 12, 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 (
	"context"
	"os"
	"github.com/harrydayexe/GoBlog/v2/pkg/parser"
)

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

// Parse all markdown files in a directory
posts, err := p.ParseDirectory(context.Background(), 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))

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

// Combine multiple options
p := parser.New(
    parser.WithCodeHighlighting(true),
    parser.WithFootnote(),
)

See the Option functions for all available configuration options.

Syntax Highlighting CSS

The parser renders highlighted code blocks using CSS classes (via chroma's html.WithClasses option) rather than inline styles. This means the generated HTML will contain class names like .chroma, .k, .s, etc., but will not be visually styled until a matching stylesheet is included in your page.

Generate the stylesheet for a given style at startup and embed it in your templates:

import (
    chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
    "github.com/alecthomas/chroma/v2/styles"
    "strings"
)

formatter := chromahtml.New(chromahtml.WithClasses(true))
style := styles.Get("monokai")
var sb strings.Builder
formatter.WriteCSS(&sb, style)
chromaCSS := sb.String() // embed in a <style> tag in your template

The class names follow the Pygments short-name convention. A full reference is available in the chroma source: https://github.com/alecthomas/chroma/blob/master/types.go

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

Example

Example demonstrates basic usage of the parser with default configuration. The parser reads markdown files with YAML frontmatter and converts them to Post objects.

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)

Example_withOptions demonstrates configuring the parser with custom options such as enabling footnote support.

// Configure parser with custom options
p := New(
	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

	// 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 or disables the highlighting of ringfenced code blocks. Highlighting is enabled by default.

When enabled, the parser outputs CSS class names (e.g. .chroma, .k, .s) rather than inline styles. A matching chroma stylesheet must be included in your page — see the package documentation for how to generate one with chromahtml.WriteCSS.

Example

ExampleWithCodeHighlighting demonstrates disabling syntax highlighting for code blocks. By default, code highlighting is enabled with the monokai style.

// 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 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

ExampleWithFootnote demonstrates enabling PHP Markdown Extra footnote support. 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.

// 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

ExampleParser_ParseDirectory demonstrates parsing all markdown files in a directory. When errors occur during parsing, partial results may still be returned along with a ParseErrors error containing details about which files failed.

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

ExampleParser_ParseFile demonstrates parsing a single markdown file. The file must contain YAML frontmatter with required fields (title, date, description).

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