woodpecker/contrib/generate-docs.go
2015-10-07 11:35:35 -07:00

174 lines
4 KiB
Go

// +build ignore
// This program generates converts our markdown
// documentation to an html website.
package main
import (
"bytes"
"flag"
"html/template"
"io/ioutil"
"log"
"os"
"path/filepath"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/eknkc/amber"
"github.com/russross/blackfriday"
)
var (
input = flag.String("input", "docs/README.md", "path to site index")
output = flag.String("output", "_site/", "path to outpute website")
html = flag.String("template", "", "path to documentation template")
name = flag.String("name", "", "name of the site")
)
func main() {
flag.Parse()
// read the markdown file into a document element.
document, err := toDocument(*input)
if err != nil {
log.Fatalf("Error opening %s. %s", *input, err)
}
// we assume this file is the sitemap, so we'll look
// for the first ul element, and assume that contains
// the site hierarchy.
sitemap := document.Find("ul").First()
site := Site{}
site.Name = *name
site.base = filepath.Dir(*input)
// for each link in the sitemap we should attempt to
// read the markdown file and add to our list of pages
// to generate.
sitemap.Find("li > a").EachWithBreak(func(i int, s *goquery.Selection) bool {
page, err := toPage(&site, s)
if err != nil {
log.Fatalf("Error following link %s. %s", s.Text(), err)
}
if page != nil {
site.Pages = append(site.Pages, page)
}
return true
})
site.Nav = &Nav{}
site.Nav.elem = sitemap
site.Nav.html, err = sitemap.Html()
if err != nil {
log.Fatal(err)
}
// compiles our template which is in amber/jade format
templ := amber.MustCompileFile(*html, amber.DefaultOptions)
// for each page in the sitemap we generate a
// corresponding html file using the above template.
for _, page := range site.Pages {
path := filepath.Join(*output, page.Href)
f, err := os.Create(path)
if err != nil {
log.Fatalf("Error creating file %s. %s", path, err)
}
defer f.Close()
// correctly make the active page in the
// navigation html snippet
site.Nav.elem.Find("li > a").EachWithBreak(func(i int, s *goquery.Selection) bool {
href, _ := s.Attr("href")
if href == page.Href {
s.Parent().AddClass("active")
} else {
s.Parent().RemoveClass("active")
}
return true
})
site.Nav.html, _ = site.Nav.elem.Html()
data := map[string]interface{}{
"Site": site,
"Page": page,
}
err = templ.Execute(f, &data)
if err != nil {
log.Fatalf("Error generating template %s. %s", path, err)
}
}
}
type Site struct {
Nav *Nav
Pages []*Page
Name string
base string
}
type Nav struct {
html string
elem *goquery.Selection
}
func (n *Nav) HTML() template.HTML {
return template.HTML(n.html)
}
type Page struct {
Name string
Href string
html string
}
func (p *Page) HTML() template.HTML {
return template.HTML(p.html)
}
// toDocument is a helper function that parses a
// markdown file, converts to html markup, and returns
// a document element.
func toDocument(filename string) (*goquery.Document, error) {
raw, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
raw = blackfriday.MarkdownCommon(raw)
buf := bytes.NewBuffer(raw)
return goquery.NewDocumentFromReader(buf)
}
// toPage is a helper function that accepts an anchor
// tag referencing a markdown file, parsing the markdown
// file and returning a page to be included in our docs.
func toPage(site *Site, el *goquery.Selection) (*Page, error) {
// follow the link to see if this is a page
// that should be added to our documentation.
href, ok := el.Attr("href")
if !ok || href == "#" {
return nil, nil
}
// read the markdown file, convert to html and
// read into a dom element.
doc, err := toDocument(filepath.Join(site.base, href))
if err != nil {
return nil, err
}
// convert the extension from markdown to
// html, in preparation for type conversion.
href = strings.Replace(href, ".md", ".html", -1)
el.SetAttr("href", href)
page := &Page{}
page.Href = href
page.html, err = doc.Html()
return page, err
}