package docsite import ( "bytes" "fmt" "github.com/yuin/goldmark" "github.com/yuin/goldmark/ast" "github.com/yuin/goldmark/extension" "github.com/yuin/goldmark/parser" "github.com/yuin/goldmark/text" "github.com/yuin/goldmark/util" ) // newMarkdown returns the one goldmark pipeline every page goes through: // CommonMark plus GFM, heading IDs, and the page transformers. The html // renderer keeps its default safe mode, so raw HTML in Markdown is never // passed through. func newMarkdown() goldmark.Markdown { return goldmark.New( goldmark.WithExtensions(extension.GFM), goldmark.WithParserOptions( parser.WithAutoHeadingID(), parser.WithASTTransformers( util.Prioritized(h1Stripper{}, 100), ), ), ) } // h1Stripper removes the page's leading "# Title" heading; the template // renders the title once. type h1Stripper struct{} func (h1Stripper) Transform(doc *ast.Document, _ text.Reader, _ parser.Context) { if h, ok := doc.FirstChild().(*ast.Heading); ok && h.Level == 1 { doc.RemoveChild(doc, h) } } // renderedPage is one page after parsing and rendering. type renderedPage struct { html []byte } // renderPage parses and renders a page body to HTML. func (s *site) renderPage(md goldmark.Markdown, p *Page) (renderedPage, error) { ctx := parser.NewContext() doc := md.Parser().Parse(text.NewReader(p.Body), parser.WithContext(ctx)) var buf bytes.Buffer if err := md.Renderer().Render(&buf, p.Body, doc); err != nil { return renderedPage{}, fmt.Errorf("docsite: render %s: %w", p.Source, err) } return renderedPage{html: buf.Bytes()}, nil }