2015-10-06 21:41:55 +00:00
|
|
|
// +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{}
|
2015-10-07 02:41:20 +00:00
|
|
|
site.Nav.elem = sitemap
|
2015-10-06 21:41:55 +00:00
|
|
|
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()
|
|
|
|
|
2015-10-07 02:41:20 +00:00
|
|
|
// 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()
|
|
|
|
|
2015-10-06 21:41:55 +00:00
|
|
|
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
|
2015-10-07 02:41:20 +00:00
|
|
|
elem *goquery.Selection
|
2015-10-06 21:41:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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")
|
2015-10-07 18:35:35 +00:00
|
|
|
if !ok || href == "#" {
|
2015-10-06 21:41:55 +00:00
|
|
|
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
|
|
|
|
}
|