formats/markdown/markdown.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
package markdown
import (
"bytes"
"fmt"
"os"
"path/filepath"
gotmpl "text/template"
"time"
"git.icyphox.sh/vite/config"
"git.icyphox.sh/vite/template"
"git.icyphox.sh/vite/types"
"github.com/adrg/frontmatter"
bf "git.icyphox.sh/grayfriday"
)
var (
bfFlags = bf.UseXHTML | bf.Smartypants | bf.SmartypantsFractions |
bf.SmartypantsDashes | bf.NofollowLinks | bf.FootnoteReturnLinks
bfExts = bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Autolink |
bf.Strikethrough | bf.SpaceHeadings | bf.BackslashLineBreak |
bf.AutoHeadingIDs | bf.HeadingIDs | bf.Footnotes | bf.NoEmptyLineBeforeBlock
)
type Markdown struct {
body []byte
frontmatter map[string]string
Path string
}
func (*Markdown) Ext() string { return ".md" }
func (md *Markdown) Basename() string {
return filepath.Base(md.Path)
}
// mdToHtml renders source markdown to html
func mdToHtml(source []byte) []byte {
return bf.Run(
source,
bf.WithNoExtensions(),
bf.WithRenderer(bf.NewHTMLRenderer(bf.HTMLRendererParameters{Flags: bfFlags})),
bf.WithExtensions(bfExts),
)
}
// template checks the frontmatter for a specified template or falls back
// to the default template -- to which it, well, templates whatever is in
// data and writes it to dest.
func (md *Markdown) template(dest, tmplDir string, data interface{}) error {
metaTemplate := md.frontmatter["template"]
if metaTemplate == "" {
metaTemplate = config.Config.DefaultTemplate
}
tmpl := template.NewTmpl()
tmpl.SetFuncs(gotmpl.FuncMap{
"parsedate": func(s string) time.Time {
date, _ := time.Parse("2006-01-02", s)
return date
},
})
if err := tmpl.Load(tmplDir); err != nil {
return err
}
return tmpl.Write(dest, metaTemplate, data)
}
// extract takes the source markdown page, extracts the frontmatter
// and body. The body is converted from markdown to html here.
func (md *Markdown) extractFrontmatter(source []byte) error {
r := bytes.NewReader(source)
rest, err := frontmatter.Parse(r, &md.frontmatter)
if err != nil {
return err
}
md.body = mdToHtml(rest)
return nil
}
func (md *Markdown) Frontmatter() map[string]string {
return md.frontmatter
}
func (md *Markdown) Body() string {
return string(md.body)
}
type templateData struct {
Cfg config.ConfigYaml
Meta map[string]string
Body string
Extra interface{}
}
func (md *Markdown) Render(dest string, data interface{}) error {
source, err := os.ReadFile(md.Path)
if err != nil {
return fmt.Errorf("markdown: error reading file: %w", err)
}
err = md.extractFrontmatter(source)
if err != nil {
return fmt.Errorf("markdown: error extracting frontmatter: %w", err)
}
err = md.template(dest, types.TemplatesDir, templateData{
config.Config,
md.frontmatter,
string(md.body),
data,
})
if err != nil {
return fmt.Errorf("markdown: failed to render to destination %s: %w", dest, err)
}
return nil
}
|