frontmatter

package module
v0.0.0-...-90d3b75 Latest Latest
Warning

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

Go to latest
Published: Sep 13, 2023 License: MIT Imports: 10 Imported by: 1

README

goldmark-frontmatter

Go Reference CI codecov

goldmark-frontmatter is an extension for the goldmark Markdown parser that adds support for parsing YAML and TOML front matter from Markdown documents.

Features

  • Parses YAML and TOML front matter out of the box
  • Allows defining your own front matter formats
  • Exposes front matter in via a types-safe API
Demo

A web-based demonstration of the extension is available at https://abhinav.github.io/goldmark-frontmatter/demo/.

Installation

go get github.com/leonhfr/goldmark-frontmatter@latest

Usage

To use goldmark-frontmatter, import the frontmatter package.

import "github.com/leonhfr/goldmark-frontmatter"

Then include the frontmatter.Extender in the list of extensions when constructing your goldmark.Markdown.

goldmark.New(
  goldmark.WithExtensions(
    // ...
    &frontmatter.Extender{},
  ),
).Convert(src, out)

By default, this won't have any effect except stripping the front matter from the document. See Accessing front matter on how to read it.

Syntax

Front matter starts with three or more instances of a delimiter, and must be the first line of the document.

The supported delimiters are:

  • YAML: -

    For example:

    ---
    title: goldmark-frontmatter
    tags: [markdown, goldmark]
    description: |
      Adds support for parsing YAML front matter.
    ---
    
    # Heading 1
    
  • TOML: +

    For example:

    +++
    title = "goldmark-frontmatter"
    tags = ["markdown", "goldmark"]
    description = """\
      Adds support for parsing YAML front matter.\
      """
    +++
    
    # Heading 1
    

The front matter block ends with the same number of instances of the delimiter. So if the opening line used 10 occurrences, so must the closing.

---------------------------
title: goldmark-frontmatter
tags: [markdown, goldmark]
---------------------------
Accessing front matter

You can use one of two ways to access front matter parsed by goldmark-frontmatter:

Decode a struct

To decode front matter into a struct, you must pass in a parser.Context when you call Markdown.Convert or Parser.Parse.

md := goldmark.New(
  goldmark.WithExtensions(&frontmatter.Extender{}),
  // ...
)

ctx := parser.NewContext()
md.Convert(src, out, parser.WithContext(ctx))

Following that, use frontmatter.Get to access a frontmatter.Data object. Use Data.Decode to unmarshal your front matter into a data structure.

d := frontmatter.Get(ctx)

var meta struct {
  Title string   `yaml:"title"`
  Tags  []string `yaml:"tags"`
  Desc  string   `yaml:"description"`
}
if err := d.Decode(&meta); err != nil {
  // ...
}

You're not limited to structs here. You can also decode into map[string]any to access all fields.

var meta map[string]any
if err := fm.Decode(&meta); err != nil {
  // ...
}

However, if you need that, it's easier to read it from the document metadata.

Read from document metadata

You can install the extension with frontmatter.SetMetadata mode:

md := goldmark.New(
  goldmark.WithExtensions(&frontmatter.Extender{
    Mode: frontmatter.SetMetadata,
  }),
  // ...
)

In this mode, the extension will decode the front matter into a map[string]any, and set it on the Document. You can access it with the Document.Meta method.

root := md.Parser().Parse(text.NewReader(src))
doc := root.OwnerDocument()
meta := doc.Meta()

Similar projects

License

This software is made available under the MIT license.

Documentation

Overview

Package frontmatter adds support for parsing front matter in Markdown documents.

It supports YAML and TOML front matter out of the box, and can be extended to support other formats.

Index

Examples

Constants

This section is empty.

Variables

View Source
var DefaultFormats = []Format{TOML, YAML}

DefaultFormats is the list of frontmatter formats that are recognized by default.

View Source
var TOML = Format{
	Name:      "TOML",
	Delim:     '+',
	Unmarshal: toml.Unmarshal,
}

TOML provides support for frontmatter in the TOML format. Front matter in this format is expected to be delimited by three or more '+' characters.

+++
title = "Hello, world!"
tags = ["foo", "bar"]
+++
View Source
var YAML = Format{
	Name:      "YAML",
	Delim:     '-',
	Unmarshal: yaml.Unmarshal,
}

YAML provides support for frontmatter in the YAML format. Front matter in this format is expected to be delimited by three or more '-' characters.

---
title: Hello, world!
tags:
  - foo
  - bar
---

Functions

This section is empty.

Types

type Data

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

Data holds the front matter data. Use Get to retrieve the data from the parser.Context.

func Get

func Get(ctx parser.Context) *Data

Get retrieves the front matter data from the parser.Context. If the data is not present, it returns nil.

func (*Data) Decode

func (d *Data) Decode(dst any) error

Decode decodes the front matter data into the provided value. The value must be a pointer to a struct or a map.

data := frontmatter.Get(ctx)
if data == nil {
	return errors.New("no front matter")
}

var metadata struct {
	Title string
	Tags []string
}
if err := data.Decode(&metadata); err != nil {
	return err
}
Example
const src = `---
title: Foo
tags: [bar, baz]
---

This page is about foo.
`

md := goldmark.New(
	goldmark.WithExtensions(
		&frontmatter.Extender{},
	),
)

ctx := parser.NewContext()
if err := md.Convert([]byte(src), os.Stdout, parser.WithContext(ctx)); err != nil {
	log.Fatal(err)
}

fm := frontmatter.Get(ctx)
if fm == nil {
	log.Fatal("no frontmatter found")
}

var data struct {
	Title string   `yaml:"title"`
	Tags  []string `yaml:"tags"`
}
if err := fm.Decode(&data); err != nil {
	log.Fatal(err)
}

fmt.Println("---")
fmt.Println("Title: ", data.Title)
fmt.Println("Tags: ", data.Tags)
Output:

<p>This page is about foo.</p>
---
Title:  Foo
Tags:  [bar baz]

type Extender

type Extender struct {
	// Formats lists the front matter formats
	// that are supported by the extender.
	//
	// If empty, DefaultFormats is used.
	Formats []Format

	// Mode specifies the mode in which the extender operates.
	// See documentation of the Mode type for more information.
	Mode Mode
}

Extender adds support for front matter to a Goldmark Markdown parser.

Use it by installing it into the goldmark.Markdown object upon creation. For example:

goldmark.New(
	// ...
	goldmark.WithExtensions(
		// ...
		&frontmatter.Extender{},
	),
)

func (*Extender) Extend

func (e *Extender) Extend(md goldmark.Markdown)

Extend extends the provided Goldmark Markdown.

type Format

type Format struct {
	// Name is a human-readable name for the format.
	//
	// It may be used in error messages.
	Name string

	// Delim specifies the delimiter that marks front matter
	// in this format.
	//
	// There must be at least three of these in a row
	// for the front matter to be recognized.
	Delim byte

	// Unmarshal unmarshals the front matter data into the provided value.
	Unmarshal func([]byte, any) error
}

Format defines a front matter format recognized by this package.

type MetaTransformer

type MetaTransformer struct{}

MetaTransformer is an AST transformer that converts the front matter of a document into a map[string]interface{} and stores it on the document as metadata.

Access the metadata by calling the ast.Document.Meta method.

func (*MetaTransformer) Transform

func (t *MetaTransformer) Transform(node *ast.Document, _ text.Reader, pc parser.Context)

Transform fetches the front matter from the parser context and sets it as the metadata of the document.

This is a no-op if the front matter is not present in the context or it cannot be decoded.

type Mode

type Mode int

Mode specifies the mode in which the Extender operates. By default, the extender extracts the front matter from the document, but does not render it or do anything else with it.

Change the mode by setting the Mode field of the Extender object.

Example (SetMetadata)
const src = `+++
title = "Foo"
tags = ["bar", "baz"]
+++

This page is about foo.
`

md := goldmark.New(
	goldmark.WithExtensions(
		&frontmatter.Extender{
			Mode: frontmatter.SetMetadata,
		},
	),
)

doc := md.Parser().Parse(text.NewReader([]byte(src)))
meta := doc.OwnerDocument().Meta()

fmt.Println("Title: ", meta["title"])
fmt.Println("Tags: ", meta["tags"])
Output:

Title:  Foo
Tags:  [bar baz]
const (
	// SetMetadata instructs the extender to convert the front matter
	// into a map[string]interface{} and set it as the metadata
	// of the document.
	//
	// This may be accessed by calling the Document.Meta() method.
	SetMetadata Mode = 1 << iota
)

func (Mode) String

func (i Mode) String() string

type Node

type Node struct {
	ast.BaseBlock

	// Format holds the front matter format we matched.
	Format Format

	// Number of times the delimiter was repeated
	// in the opening line.
	DelimCount int

	// Segment holds the text range over which the front matter spans.
	Segment text.Segment
}

Node stores information about the parse state before the front matter is placed in the parser context.

func (*Node) Dump

func (n *Node) Dump(source []byte, level int)

Dump implements Node.Dump.

func (*Node) Kind

func (n *Node) Kind() ast.NodeKind

Kind implements Node.Kind.

type Parser

type Parser struct {
	// Formats specifies the front matter formats
	// supported by the parser.
	//
	// If Formats is empty, DefaultFormats is used.
	Formats []Format
	// contains filtered or unexported fields
}

Parser parses front matter from a Markdown document.

func (*Parser) CanAcceptIndentedLine

func (*Parser) CanAcceptIndentedLine() bool

CanAcceptIndentedLine reports that a frontmatter block cannot be indented.

This implements parser.BlockParser.

func (*Parser) CanInterruptParagraph

func (*Parser) CanInterruptParagraph() bool

CanInterruptParagraph reports that a frontmatter block cannot interrupt a paragraph.

This implements parser.BlockParser.

func (*Parser) Close

func (p *Parser) Close(node ast.Node, reader text.Reader, pc parser.Context)

Close cleans up after parsing a frontmatter block.

This implements parser.BlockParser.

func (*Parser) Continue

func (p *Parser) Continue(node ast.Node, reader text.Reader, _ parser.Context) parser.State

Continue continues parsing the following lines of a frontmatter block, transitioning to Close when the block is finished.

This implements parser.BlockParser.

func (*Parser) Open

func (p *Parser) Open(_ ast.Node, reader text.Reader, _ parser.Context) (ast.Node, parser.State)

Open begins parsing a frontmatter block, returning nil if a frontmatter block is not found.

This implements parser.BlockParser.

func (*Parser) Trigger

func (p *Parser) Trigger() []byte

Trigger returns bytes that can trigger this parser.

This implements parser.BlockParser.

Directories

Path Synopsis
demo module

Jump to

Keyboard shortcuts

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