Skip to content

Commit

Permalink
create a doc generator
Browse files Browse the repository at this point in the history
  • Loading branch information
ihadeed committed Nov 11, 2019
1 parent 8aa3913 commit 010fd96
Show file tree
Hide file tree
Showing 404 changed files with 247,988 additions and 2 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.idea
.idea
zmdocs_darwin_amd64
zmdocs_linux_amd64
zmdocs_windows_amd64.exe
11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
module "github.com/zyra/docs"
module github.com/zyra/zmdocs

go 1.12

require (
github.com/russross/blackfriday v2.0.0+incompatible
github.com/sirupsen/logrus v1.4.2
github.com/urfave/cli v1.22.1
gopkg.in/yaml.v2 v2.2.5
)
30 changes: 30 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday v2.0.0+incompatible h1:cBXrhZNUf9C+La9/YpS+UHpUT8YD6Td9ZMSU9APFcsk=
github.com/russross/blackfriday v2.0.0+incompatible/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
314 changes: 314 additions & 0 deletions parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,314 @@
package zmdocs

import (
"bytes"
"fmt"
"github.com/russross/blackfriday"
"github.com/sirupsen/logrus"
"github.com/zyra/zmdocs/templates"
"gopkg.in/yaml.v2"
"html/template"
"io/ioutil"
"os"
"path/filepath"
)

type basePage struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
SourceFile string `yaml:"source"`
Template string `yaml:"template"`
AddToMenu bool `yaml:"addToMenu"`
MenuGroup string `yaml:"menuGroup"`
EditOnGithub bool `yaml:"editOnGithub"`
}

type Page struct {
basePage `yaml:",inline"`
Title string `yaml:"title,omitempty"`
}

type PagePattern struct {
basePage `yaml:",inline"`
SourceGlob string `yaml:"sourceGlob"`
Pattern string `yaml:"pattern"`
}

type Template struct {
Name string `yaml:"name"`
SourceFile string `yaml:"source"`
}

type MenuItem struct {
Name string `yaml:"name"` // name is required if group == true
Title string `yaml:"title"` // menu item title
Link string `yaml:"link"` // menu item link (optional)
Items []*MenuItem `yaml:"items"` // sub menu items (optional)
Group bool `yaml:"group"` // whether this is a group heading
Active bool `yaml:"-"`
}

type ParserConfig struct {
RootDir string `yaml:"rootDir"` // root directory of project, defaults to the config file directory if initialized with NewParserFromConfig
OutDir string `yaml:"outDir"`
Pages []*Page `yaml:"pages"` // list of pages to render
AutoPages []*PagePattern `yaml:"pagePatterns"` // list of patterns to derive pages from
Templates []*Template `yaml:"templates"` // list of template files
MenuItems []*MenuItem `yaml:"menuItems"` // menu items
SiteTitle string `yaml:"siteTitle"`
Description string `yaml:"description"`
Repo string `yaml:"repo"`
}

type File struct {
basePage
Title string
}

type Parser struct {
Config *ParserConfig
Files []*File
Logger *logrus.Logger
}

func NewParser(config *ParserConfig) *Parser {
l := logrus.New()

l.SetFormatter(&logrus.TextFormatter{
ForceColors: true,
EnvironmentOverrideColors: true,
FullTimestamp: true,
QuoteEmptyFields: true,
})

if os.Getenv("ZMDOCS_DEBUG") == "true" {
l.SetLevel(logrus.DebugLevel)
} else {
l.SetLevel(logrus.InfoLevel)
}

p := Parser{
Config: config,
Files: make([]*File, 0),
Logger: l,
}

return &p
}

func NewParserFromConfig(configPath string) (*Parser, error) {
var config ParserConfig

if data, err := ioutil.ReadFile(configPath); err != nil {
return nil, fmt.Errorf("unabel to read config file: %s", err.Error())
} else if err := yaml.Unmarshal(data, &config); err != nil {
return nil, fmt.Errorf("unable to parse config: %s", err.Error())
}

config.RootDir = filepath.Dir(configPath)
config.OutDir = filepath.Join(config.RootDir, config.OutDir)

return NewParser(&config), nil
}

func (p *Parser) LoadSourceFiles() error {
p.Logger.Info("loading source files")

p.Logger.Debug("Loading static files")
if err := p.loadStaticFiles(); err != nil {
return err
}
p.Logger.Debug("Done loading static files")

p.Logger.Debug("Loading glob files")
if err := p.loadGlobFiles(); err != nil {
return err
}
p.Logger.Debug("Done loading glob files")

return nil
}

func (p *Parser) loadStaticFiles() error {
for _, pg := range p.Config.Pages {
g := filepath.Join(p.Config.RootDir, pg.SourceFile)

file := File{
basePage: *&pg.basePage,
Title: pg.Title,
}

file.SourceFile = g

p.Files = append(p.Files, &file)
}

return nil
}

func (p *Parser) loadGlobFiles() error {
for i, ap := range p.Config.AutoPages {
g := filepath.Join(p.Config.RootDir, ap.SourceGlob)

p.Logger.WithFields(logrus.Fields{
"pattern": ap.Pattern,
"absPath": g,
}).Debug("processing page pattern")

if patternMatches, err := GetPatternMatches(g, ap.Pattern); err != nil {
return fmt.Errorf("unable to process page pattern #%d: %s", i, err.Error())
} else {
p.Logger.WithFields(logrus.Fields{
"pattern": ap.Pattern,
}).Debugf("found %d matches", len(patternMatches))

for _, pm := range patternMatches {
if file, err := GetFileForPatternMatch(ap, pm); err != nil {
return fmt.Errorf("unable to process file %s: \n\t%s", pm.Path, err.Error())
} else {
p.Files = append(p.Files, file)
}
}
}
}

return nil
}

type RenderContext struct {
Title string
SiteTitle string
Description string
MenuItems []*MenuItem
Content template.HTML
OutDir string
OutFile string
}

func (p *Parser) Render() error {
menuItems := p.Config.MenuItems
rndCtxs := make([]*RenderContext, 0)

for _, f := range p.Files {
if fc, err := ioutil.ReadFile(f.SourceFile); err != nil {
return err
} else {
exts := blackfriday.WithExtensions(blackfriday.CommonExtensions | blackfriday.AutoHeadingIDs | blackfriday.Autolink | blackfriday.Footnotes)
rndOpts := blackfriday.HTMLRendererParameters{
Flags: blackfriday.CommonHTMLFlags | blackfriday.FootnoteReturnLinks,
}
rnd := blackfriday.NewHTMLRenderer(rndOpts)

o := blackfriday.Run(fc, exts, blackfriday.WithRenderer(rnd))

if f.Title == "" {
mp := blackfriday.New(exts, blackfriday.WithRenderer(rnd))

a := mp.Parse(fc)
it := a.FirstChild
for it != nil && f.Title == "" {
if it.Type == blackfriday.Heading && it.Level == 1 && it.FirstChild != nil && it.FirstChild.Type == blackfriday.Text {
f.Title = string(it.FirstChild.Literal)
}

it = it.Next
}
}

outDir := filepath.Join(p.Config.RootDir, "docs", f.Path)
outFile := filepath.Join(outDir, "index.html")

ctx := RenderContext{
Title: f.Title,
Content: template.HTML(o),
OutDir: outDir,
OutFile: outFile,
}

rndCtxs = append(rndCtxs, &ctx)

menuItem := MenuItem{
Name: f.Name,
Title: f.Title,
Link: f.Path,
}

if f.AddToMenu {
if f.MenuGroup != "" {
for _, mit := range menuItems {
if mit.Group && mit.Name == f.MenuGroup {
if mit.Items == nil {
mit.Items = make([]*MenuItem, 0)
}

mit.Items = append(mit.Items, &menuItem)
}
}
} else {
menuItems = append(menuItems, &menuItem)
}
}
}
}

var baseTemplateStr string

for _, t := range p.Config.Templates {
if t.Name == "base" {
baseTemplatePath := filepath.Join(p.Config.RootDir, t.SourceFile)
baseTemplateData, err := ioutil.ReadFile(baseTemplatePath)

if err != nil {
return err
}

baseTemplateStr = string(baseTemplateData)
}
}

if baseTemplateStr == "" {
baseTemplateStr = templates.BaseTemplate
}

baseTemplate, err := template.New("").Parse(baseTemplateStr)

if err != nil {
return err
}

for _, ctx := range rndCtxs {
ctx.SiteTitle = p.Config.SiteTitle
ctx.Description = p.Config.Description
ctx.MenuItems = menuItems

for _, mit := range menuItems {
if mit.Group {
for _, cmit := range mit.Items {
cmit.Active = cmit.Title == ctx.Title
}
} else if mit.Title == ctx.Title {
mit.Active = true
} else {
mit.Active = false
}
}

buff := bytes.NewBuffer(make([]byte, 0))

if err := baseTemplate.Execute(buff, ctx); err != nil {
return err
}

ctx.Content = template.HTML(buff.String())

if err := os.MkdirAll(ctx.OutDir, 0755); err != nil {
return err
}

if err := ioutil.WriteFile(ctx.OutFile, []byte(ctx.Content), 0644); err != nil {
return err
}
}

return nil
}
Loading

0 comments on commit 010fd96

Please sign in to comment.