Skip to content

Instantly share code, notes, and snippets.

@tomcam

tomcam/g.go Secret

Last active July 26, 2022 04:05
Show Gist options
  • Save tomcam/a1c8fbe27a335164add3bc2b1d92b204 to your computer and use it in GitHub Desktop.
Save tomcam/a1c8fbe27a335164add3bc2b1d92b204 to your computer and use it in GitHub Desktop.
Goldmark demo with App object Markdown to HTML conversion, code highlighting, YAML support, simple template support
// g.5:
// Demonstrates
// 1. The goldmark Markdown to HTML converter using an App object
// 2. Code highlighting.
// 3. Extracting YAML front matter
// 4. Executing a template to replace front matter metadata with its evaluated result
// Displays results to standard output
// $ mkdir ~/g
// $ cd ~/g
// $ go mod init example.com/g # example.com is OK to use in this quick & dirty example
// $ go fmt
// $ go mod tidy
// $ go run g.go
// $ go run g.go > foobar.html
// $ open foobar.html
package main
import (
"bytes"
"fmt"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark-highlighting"
"github.com/yuin/goldmark-meta"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/renderer/html"
"os"
"text/template"
)
const frontMatter1 = `---
Title: goldmark-meta
Theme: wide
Summary: Add YAML metadata to the document
Tags:
- markdown
- goldmark
---
## Title?
{{ .Title }}
`
const codeFence1 = "# Example with code fence and code highlighting:\n" +
"hello, world.\n\nCode fence example:\n" +
"```js\n" +
"console.log('hi')\n" +
"```\n" + `
`
const template1 = `
## Add template:
This is template1
Title: {{ .Title }}
`
type App struct {
mdParser goldmark.Markdown
mdParserCtx parser.Context
// YAML front matter
metaData map[string]interface{}
}
// newGoldmark returns the a goldmark object with a parser and renderer.
func (app *App) newGoldmark() goldmark.Markdown {
exts := []goldmark.Extender{
meta.New(
meta.WithStoresInDocument(),
),
// Support GitHub tables & other extensions
extension.Table,
extension.GFM,
extension.DefinitionList,
extension.Footnote,
highlighting.NewHighlighting(
highlighting.WithStyle("github"),
highlighting.WithFormatOptions()),
}
parserOpts := []parser.Option{
parser.WithAttribute(),
parser.WithAutoHeadingID()}
renderOpts := []renderer.Option{
// WithUnsafe is required for HTML templates to work properly
html.WithUnsafe(),
html.WithXHTML(),
}
return goldmark.New(
goldmark.WithExtensions(exts...),
goldmark.WithParserOptions(parserOpts...),
goldmark.WithRendererOptions(renderOpts...),
)
}
func NewApp() *App {
app := App{}
app.mdParser = app.newGoldmark()
app.mdParserCtx = parser.NewContext()
return &app
}
// mdYAMLtoHTML converts a Markdown document with optional
// YAML front matter to HTML. YAML is written to app.metaData
// Returns a byte slice containing the HTML source.
// Pre: parser.NewContext() has already been called on app.parserCtx
func (app *App) mdYAMLtoHTML(source []byte) ([]byte, error) {
var buf bytes.Buffer
// Convert Markdown source to HTML and deposit in buf.Bytes().
if err := app.mdParser.Convert(source, &buf, parser.WithContext(app.mdParserCtx)); err != nil {
return []byte{}, err
}
// Obtain YAML front matter from document.
app.metaData = meta.Get(app.mdParserCtx)
return buf.Bytes(), nil
}
// mdtoHTML converts a Markdown document to HTML.
// YAML front matter should not be present.
// Returns a byte slice containing the HTML source.
// Pre: parser.NewContext() has already been called on app.parserCtx
func (app *App) mdToHTML(source []byte) ([]byte, error) {
var buf bytes.Buffer
// Convert Markdown source to HTML and deposit in buf.Bytes().
if err := app.mdParser.Convert(source, &buf, parser.WithContext(app.mdParserCtx)); err != nil {
return []byte{}, err
}
return buf.Bytes(), nil
}
func (app *App) doTemplate(templateName string, source string /* funcMap template.FuncMap */) string {
if templateName == "" {
templateName = "Metabuzz"
}
tmpl, err := template.New(templateName).Parse(source)
if err != nil {
quit(err, 1)
}
//err = tmpl.Execute(os.Stdout, app.metaData)
buf := new(bytes.Buffer)
err = tmpl.Execute(buf, app.metaData)
if err != nil {
quit(err, 1)
}
return buf.String()
}
func main() {
var app = NewApp()
app.mdParserCtx = parser.NewContext()
var b []byte
var err error
// Do a simple markdown > HTML conversion
// without front matter being parsed
source := []byte(frontMatter1 + codeFence1)
if b, err = app.mdToHTML(source); err != nil {
quit(err, 1)
} else {
fmt.Println("\nmdToHTML() results:\n" + string(b))
//fmt.Printf("Raw YAML:\n%+v", app.metaData)
}
// Convert Markdown to HTML and parse front matter.
source = []byte(frontMatter1 + codeFence1)
if b, err = app.mdYAMLtoHTML(source); err != nil {
quit(err, 1)
} else {
fmt.Println("\nmdYAMLtoHTML() results:\n" + string(b))
//fmt.Printf("\nRaw YAML:\n%+v\n", app.metaData)
}
source = []byte(frontMatter1 + codeFence1 + template1)
if b, err = app.mdYAMLtoHTML(source); err != nil {
quit(err, 1)
}
t := app.doTemplate("METABUZZ", string(b))
fmt.Println(t)
}
func quit(err error, exitCode int) {
if err != nil {
fmt.Println(err)
}
os.Exit(exitCode)
}
@tomcam
Copy link
Author

tomcam commented Jul 26, 2022

updated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment