all repos — vite @ c9a8e448d78283a7faf80fd20cb0eed900914153

a fast (this time, actually) and minimal static site generator

Init vite v2

Doesn't fully work yet. Don't bother trying.
Anirudh Oppiliappan x@icyphox.sh
Tue, 03 Aug 2021 21:10:24 +0530
commit

c9a8e448d78283a7faf80fd20cb0eed900914153

parent

cc1488114f0c8b9aacd9e328680daae7028d1ed9

D build.go

@@ -1,206 +0,0 @@

-package main - -import ( - "fmt" - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "sort" - "strings" - "text/template" - - "github.com/cross-cpm/go-shutil" -) - -var cfg = parseConfig() - -type Post struct { - Fm Matter -} - -var posts []Post - -type NewFm struct { - Template string - URL string - Title string - Subtitle string - Date string - Body string -} - -func execute(cmds []string) { - for _, cmd := range cmds { - out, err := exec.Command(cmd).Output() - printMsg("running:", cmd) - if err != nil { - printErr(err) - fmt.Println(string(out)) - } - } -} - -func processTemplate(tmplPath string) *template.Template { - tmplFile := filepath.Join("templates", tmplPath) - tmpl, err := template.ParseFiles(tmplFile) - if err != nil { - printErr(err) - } - - return tmpl -} - -func handleMd(mdPath string) { - content, err := ioutil.ReadFile(mdPath) - if err != nil { - printErr(err) - } - - restContent, fm := parseFrontmatter(content) - bodyHtml := mdRender(restContent, cfg) - relPath, _ := filepath.Rel("pages/", mdPath) - - var buildPath string - if strings.HasSuffix(relPath, "_index.md") { - dir, _ := filepath.Split(relPath) - buildPath = filepath.Join("build", dir) - } else { - buildPath = filepath.Join( - "build", - strings.TrimSuffix(relPath, filepath.Ext(relPath)), - ) - } - - os.MkdirAll(buildPath, 0755) - - fm.Body = string(bodyHtml) - - if strings.Contains(relPath, "blog/") { - posts = append(posts, Post{fm}) - } - - var newFm = NewFm{ - fm.Template, - fm.URL, - fm.Title, - fm.Subtitle, - fm.Date.Time.Format(cfg.DateFmt), - fm.Body, - } - // combine config and matter structs - combined := struct { - Cfg Config - Fm NewFm - }{cfg, newFm} - - htmlFile, err := os.Create(filepath.Join(buildPath, "index.html")) - if err != nil { - printErr(err) - return - } - if fm.Template == "" { - fm.Template = "text.html" - } - tmpl := processTemplate(fm.Template) - err = tmpl.Execute(htmlFile, combined) - if err != nil { - printErr(err) - return - } - htmlFile.Close() -} - -func renderIndex(posts []Post) { - indexTmpl := processTemplate("index.html") - path := filepath.Join("pages", "_index.md") - - // Sort posts by date - sort.Slice(posts, func(i, j int) bool { - return posts[j].Fm.Date.Time.Before(posts[i].Fm.Date.Time) - }) - - content, err := ioutil.ReadFile(path) - if err != nil { - printErr(err) - } - - restContent, fm := parseFrontmatter(content) - bodyHtml := mdRender(restContent, cfg) - fm.Body = string(bodyHtml) - - var newFm = NewFm{ - fm.Template, - fm.URL, - fm.Title, - fm.Subtitle, - fm.Date.Time.Format(cfg.DateFmt), - fm.Body, - } - - combined := struct { - Fm NewFm - Posts []Post - Cfg Config - }{newFm, posts, cfg} - - htmlFile, err := os.Create(filepath.Join("build", "index.html")) - err = indexTmpl.Execute(htmlFile, combined) - if err != nil { - printErr(err) - return - } - htmlFile.Close() -} - -func viteBuild() { - if len(cfg.Prebuild) != 0 { - printMsg("executing pre-build actions...") - execute(cfg.Prebuild) - } - err := filepath.Walk("./pages", func(path string, info os.FileInfo, err error) error { - if err != nil { - printErr(err) - return err - } - if filepath.Ext(path) == ".md" && path != filepath.Join("pages", "_index.md") { - handleMd(path) - } else { - f, err := os.Stat(path) - if err != nil { - printErr(err) - } - mode := f.Mode() - if mode.IsRegular() { - options := shutil.CopyOptions{} - relPath, _ := filepath.Rel("pages/", path) - options.FollowSymlinks = true - shutil.CopyFile( - path, - filepath.Join("build", relPath), - &options, - ) - } - } - return nil - }) - - if err != nil { - printErr(err) - } - - // Deal with the special snowflake '_index.md' - renderIndex(posts) - - _, err = shutil.CopyTree("static", filepath.Join("build", "static"), nil) - if err != nil { - printErr(err) - } - printMsg("site build complete") - printMsg("generating feeds...") - generateRSS(posts, cfg) - if len(cfg.Postbuild) != 0 { - printMsg("executing post-build actions...") - execute(cfg.Postbuild) - } -}
A commands/build.go

@@ -0,0 +1,163 @@

+package commands + +import ( + "fmt" + "os" + "path/filepath" + "sort" + "strings" + "time" + + "git.icyphox.sh/vite/config" + "git.icyphox.sh/vite/markdown" + "git.icyphox.sh/vite/util" +) + +const ( + BUILD = "build" + PAGES = "pages" + TEMPLATES = "templates" +) + +type Pages struct { + Dirs []string + Files []string +} + +// Populates a Pages object with dirs and files +// found in 'pages/'. +func (pgs *Pages) initPages() error { + files, err := os.ReadDir("./pages") + if err != nil { + return err + } + + for _, f := range files { + if f.IsDir() { + pgs.Dirs = append(pgs.Dirs, f.Name()) + } else { + pgs.Files = append(pgs.Files, f.Name()) + } + } + + return nil +} + +// Core builder function. Converts markdown to html, +// copies over non .md files, etc. +func Build() error { + pages := Pages{} + err := pages.initPages() + if err != nil { + return err + } + + // Deal with files. + // ex: pages/{_index,about,etc}.md + err = func() error { + for _, f := range pages.Files { + if filepath.Ext(f) == ".md" { + // ex: pages/about.md + mdFile := filepath.Join(PAGES, f) + var htmlDir string + if f == "_index.md" { + htmlDir = BUILD + } else { + htmlDir = filepath.Join( + BUILD, + strings.TrimSuffix(f, ".md"), + ) + } + os.Mkdir(htmlDir, 0755) + // ex: build/about/index.html + htmlFile := filepath.Join(htmlDir, "index.html") + + fb, err := os.ReadFile(mdFile) + if err != nil { + return err + } + + out := markdown.Output{} + out.RenderMarkdown(fb) + if err = out.RenderHTML( + htmlFile, + TEMPLATES, + struct { + Cfg config.ConfigYaml + Fm markdown.Matter + Body string + }{config.Config, out.Meta, string(out.HTML)}, + ); err != nil { + return err + } + } else { + src := filepath.Join(PAGES, f) + util.CopyFile(src, BUILD) + } + } + return nil + }() + if err != nil { + return err + } + + // Deal with dirs -- i.e. of markdown files. + // ex: pages/{blog,travel}/*.md + err = func() error { + for _, d := range pages.Dirs { + // ex: build/blog + dstDir := filepath.Join(BUILD, d) + // ex: pages/blog + srcDir := filepath.Join(PAGES, d) + os.Mkdir(dstDir, 0755) + + entries, err := os.ReadDir(srcDir) + if err != nil { + return err + } + + posts := []markdown.Output{} + // Collect all posts + for _, e := range entries { + ePath := filepath.Join(srcDir, e.Name()) + fb, err := os.ReadFile(ePath) + if err != nil { + return err + } + + out := markdown.Output{} + out.RenderMarkdown(fb) + + slug := strings.TrimSuffix(filepath.Ext(e.Name()), ".md") + + htmlFile := filepath.Join(dstDir, slug) + out.RenderHTML( + htmlFile, + TEMPLATES, + struct { + Cfg config.ConfigYaml + Fm markdown.Matter + Body string + }{config.Config, out.Meta, string(out.HTML)}) + posts = append(posts, out) + } + + // Sort posts slice by date + sort.Slice(posts, func(i, j int) bool { + date1 := posts[j].Meta["date"].(time.Time) + date2 := posts[i].Meta["date"].(time.Time) + return date1.Before(date2) + }) + + for _, p := range posts { + fmt.Println(p.Meta["date"]) + } + } + return nil + }() + if err != nil { + return err + } + + return nil +}
D config.go

@@ -1,34 +0,0 @@

-package main - -import ( - "gopkg.in/yaml.v2" - "io/ioutil" -) - -type Config struct { - Title string `yaml:"title"` - Header string `yaml:"header"` - DateFmt string `yaml:"datefmt"` - SiteURL string `yaml:"siteurl"` - Description string `yaml:"description"` - Author map[string]string `yaml:"author"` - Footer string `yaml:"footer"` - Prebuild []string `yaml:"prebuild"` - Postbuild []string `yaml:"postbuild"` - RSSPrefixURL string `yaml:"rssprefixurl"` -} - -func parseConfig() Config { - var config Config - cf, err := ioutil.ReadFile("config.yaml") - if err != nil { - printErr(err) - } - - err = yaml.Unmarshal(cf, &config) - if err != nil { - printErr(err) - } - - return config -}
A config/config.go

@@ -0,0 +1,38 @@

+package config + +import ( + "fmt" + "os" + + "gopkg.in/yaml.v3" +) + +type ConfigYaml struct { + Title string `yaml:"title"` + Desc string `yaml:"description"` + Author struct { + Name string `yaml:"name"` + Email string `yaml:"email"` + } `yaml:"author"` + URL string `yaml:"url"` + Prebuild []string `yaml:"prebuild"` + Postbuild []string `yaml:"postbuild"` +} + +var Config ConfigYaml + +func init() { + cf, err := os.ReadFile("config.yaml") + if err != nil { + fmt.Errorf("error: %+v\n", err) + os.Exit(1) + } + if err = Config.ParseConfig(cf); err != nil { + fmt.Errorf("error: %+v\n", err) + } +} + +func (c *ConfigYaml) ParseConfig(cf []byte) error { + err := yaml.Unmarshal(cf, c) + return err +}
D frontmatter.go

@@ -1,45 +0,0 @@

-package main - -import ( - "bytes" - "github.com/adrg/frontmatter" - "time" -) - -type Date8601 struct { - time.Time -} - -func (d *Date8601) UnmarshalYAML(unmarshal func(interface{}) error) error { - var date string - - err := unmarshal(&date) - if err != nil { - return err - } - - d.Time, err = time.Parse("2006-01-02", date) - return err -} - -type Matter struct { - Template string `yaml:"template"` - URL string `yaml:"url"` - Title string `yaml:"title"` - Subtitle string `yaml:"subtitle"` - Date Date8601 `yaml:"date"` - Body string -} - -// Parses frontmatter, populates the `matter` struct and -// returns the rest -func parseFrontmatter(inputBytes []byte) ([]byte, Matter) { - m := Matter{} - input := bytes.NewReader(inputBytes) - rest, err := frontmatter.Parse(input, &m) - - if err != nil { - printErr(err) - } - return rest, m -}
M go.modgo.mod

@@ -1,14 +1,10 @@

-module github.com/icyphox/go-vite +module git.icyphox.sh/vite go 1.13 require ( github.com/Depado/bfchroma v1.3.0 - github.com/adrg/frontmatter v0.2.0 - github.com/alecthomas/chroma v0.9.2 // indirect - github.com/cross-cpm/go-shutil v0.0.0-20190908093542-3fcbb1a2151e - github.com/gorilla/feeds v1.1.1 - github.com/kr/pretty v0.2.1 // indirect + github.com/alecthomas/chroma v0.9.2 github.com/russross/blackfriday/v2 v2.1.0 - gopkg.in/yaml.v2 v2.4.0 + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b )
M go.sumgo.sum

@@ -1,14 +1,7 @@

-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Depado/bfchroma v1.3.0 h1:zz14vpvySU6S0CL6yGPr1vkFevQecIt8dJdCsMS2JpM= github.com/Depado/bfchroma v1.3.0/go.mod h1:c0bFk0tFmT+clD3TIGurjWCfD/QV8/EebfM3JGr+98M= -github.com/adrg/frontmatter v0.1.0 h1:8gYVjU1CRsoc6in25uKQVpcQpR9cYz2BD0NQk6JepyU= -github.com/adrg/frontmatter v0.1.0/go.mod h1:93rQCj3z3ZlwyxxpQioRKC1wDLto4aXHrbqIsnH9wmE= -github.com/adrg/frontmatter v0.2.0 h1:/DgnNe82o03riBd1S+ZDjd43wAmC6W35q67NHeLkPd4= -github.com/adrg/frontmatter v0.2.0/go.mod h1:93rQCj3z3ZlwyxxpQioRKC1wDLto4aXHrbqIsnH9wmE= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= -github.com/alecthomas/chroma v0.7.3 h1:NfdAERMy+esYQs8OXk0I868/qDxxCEo7FMz1WIqMAeI= github.com/alecthomas/chroma v0.7.3/go.mod h1:sko8vR34/90zvl5QdcUdvzL3J8NKjAUx9va9jPuFNoM= github.com/alecthomas/chroma v0.9.2 h1:yU1sE2+TZbLIQPMk30SolL2Hn53SR/Pv750f7qZ/XMs= github.com/alecthomas/chroma v0.9.2/go.mod h1:eMuEnpA18XbG/WhOWtCzJHS7WqEtDAI+HxdwoW0nVSk=

@@ -18,24 +11,14 @@ github.com/alecthomas/kong v0.2.4/go.mod h1:kQOmtJgV+Lb4aj+I2LEn40cbtawdWJ9Y8QLq+lElKxE=

github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c h1:MVVbswUlqicyj8P/JljoocA7AyCo62gzD0O7jfvrhtE= github.com/alecthomas/repr v0.0.0-20200325044227-4184120f674c/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= -github.com/cross-cpm/go-shutil v0.0.0-20190908093542-3fcbb1a2151e h1:x8RmdI3kIwKOGLb/SaraDgArPZ0Kb+0WmkM0K2P1CZk= -github.com/cross-cpm/go-shutil v0.0.0-20190908093542-3fcbb1a2151e/go.mod h1:VPohcIQ6FLWAgzOT7enwtRTAvwfQBDD1v4c7mvY2s2c= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY= -github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=

@@ -43,13 +26,11 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=

@@ -62,9 +43,6 @@ golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=

golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/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.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
M init.gocommands/init.go

@@ -1,21 +1,23 @@

-package main +package commands import ( + "fmt" "os" "path/filepath" ) -func viteInit(path string) { +func Init(path string) error { paths := []string{"build", "pages", "static", "templates"} var dirPath string + for _, p := range paths { dirPath = filepath.Join(path, p) err := os.MkdirAll(dirPath, 0755) if err != nil { - printErr(err) - return + return err } } fp, _ := filepath.Abs(path) - printMsg("created project:", fp) + fmt.Printf("vite: created project at %q\n", fp) + return nil }
M main.gomain.go

@@ -3,6 +3,8 @@

import ( "fmt" "os" + + "git.icyphox.sh/vite/commands" ) func main() {

@@ -18,8 +20,6 @@ build builds the current project

new PATH create a new markdown post ` - // TODO: make arg parsing less shit - if len(args) <= 1 { fmt.Println(helpStr) return

@@ -32,20 +32,27 @@ fmt.Println(helpStr)

return } initPath := args[2] - viteInit(initPath) + err := commands.Init(initPath) + if err != nil { + fmt.Errorf("error: init: %+v\n", err) + } + case "build": - _, err := os.Stat("config.yaml") + err := commands.Build() if err != nil { - return + fmt.Errorf("error: build: %+v\n", err) } - viteBuild() + case "new": if len(args) <= 2 { fmt.Println(helpStr) return } newPath := args[2] - viteNew(newPath) + err := commands.New(newPath) + if err != nil { + fmt.Errorf("error: new: %+v\n", err) + } } }
D markdown.go

@@ -1,29 +0,0 @@

-package main - -import ( - bfc "github.com/Depado/bfchroma" - bf "github.com/russross/blackfriday/v2" -) - -var bfFlags = bf.UseXHTML | bf.Smartypants | bf.SmartypantsFractions | - bf.SmartypantsDashes | bf.NofollowLinks -var bfExts = bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Autolink | - bf.Strikethrough | bf.SpaceHeadings | bf.BackslashLineBreak | - bf.HeadingIDs | bf.Footnotes | bf.NoEmptyLineBeforeBlock - -func mdRender(input []byte, cfg Config) []byte { - return bf.Run( - input, - bf.WithRenderer( - bfc.NewRenderer( - bfc.ChromaStyle(Icy), - bfc.Extend( - bf.NewHTMLRenderer(bf.HTMLRendererParameters{ - Flags: bfFlags, - }), - ), - ), - ), - bf.WithExtensions(bfExts), - ) -}
A markdown/frontmatter.go

@@ -0,0 +1,82 @@

+package markdown + +import ( + "bufio" + "bytes" + "errors" + "fmt" + + "gopkg.in/yaml.v3" +) + +type Matter map[string]interface{} + +type MarkdownDoc struct { + Frontmatter Matter + Body []byte +} + +const ( + yamlDelim = "---" +) + +func (md *MarkdownDoc) Extract(source []byte) error { + bufsize := 1024 * 1024 + buf := make([]byte, bufsize) + + input := bytes.NewReader(source) + s := bufio.NewScanner(input) + s.Buffer(buf, bufsize) + + matter := []byte{} + body := []byte{} + + s.Split(splitFunc) + n := 0 + for s.Scan() { + if n == 0 { + matter = s.Bytes() + } else if n == 1 { + body = s.Bytes() + } + n++ + } + if err := s.Err(); err != nil { + return fmt.Errorf("error: failed to scan text") + } + if err := yaml.Unmarshal(matter, &md.Frontmatter); err != nil { + return fmt.Errorf("error: failed to parse yaml") + } + md.Body = body + return nil +} + +func splitFunc(data []byte, atEOF bool) (advance int, token []byte, err error) { + if atEOF && len(data) == 0 { + return 0, nil, nil + } + delim, err := sniffDelim(data) + if err != nil { + return 0, nil, err + } + if delim != yamlDelim { + return 0, nil, fmt.Errorf("error: %s is not a supported delimiter", delim) + } + if x := bytes.Index(data, []byte(delim)); x >= 0 { + if next := bytes.Index(data[x+len(delim):], []byte(delim)); next > 0 { + return next + len(delim), bytes.TrimSpace(data[:next+len(delim)]), nil + } + return len(data), bytes.TrimSpace(data[x+len(delim):]), nil + } + if atEOF { + return len(data), data, nil + } + return 0, nil, nil +} + +func sniffDelim(input []byte) (string, error) { + if len(input) < 4 { + return "", errors.New("error: input is empty") + } + return string(input[:3]), nil +}
A markdown/markdown.go

@@ -0,0 +1,67 @@

+package markdown + +import ( + "os" + "path/filepath" + "text/template" + + bfc "github.com/Depado/bfchroma" + bf "github.com/russross/blackfriday/v2" +) + +var bfFlags = bf.UseXHTML | bf.Smartypants | bf.SmartypantsFractions | + bf.SmartypantsDashes | bf.NofollowLinks +var bfExts = bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Autolink | + bf.Strikethrough | bf.SpaceHeadings | bf.BackslashLineBreak | + bf.HeadingIDs | bf.Footnotes | bf.NoEmptyLineBeforeBlock + +type Output struct { + HTML []byte + Meta Matter +} + +// Renders markdown to html, and fetches metadata. +func (out *Output) RenderMarkdown(source []byte) { + md := MarkdownDoc{} + md.Extract(source) + + out.HTML = bf.Run( + md.Body, + bf.WithRenderer( + bfc.NewRenderer( + bfc.ChromaStyle(Icy), + bfc.Extend( + bf.NewHTMLRenderer(bf.HTMLRendererParameters{ + Flags: bfFlags, + }), + ), + ), + ), + bf.WithExtensions(bfExts), + ) + out.Meta = md.Frontmatter +} + +// Renders out.HTML into dst html file, using the template specified +// in the frontmatter. data is the template struct. +func (out *Output) RenderHTML(dst, tmplDir string, data interface{}) error { + metaTemplate := out.Meta["template"].(string) + if metaTemplate == "" { + metaTemplate = "text.html" + } + + t, err := template.ParseGlob(filepath.Join(tmplDir, "*.html")) + if err != nil { + return err + } + + w, err := os.Create(dst) + if err != nil { + return err + } + + if err = t.ExecuteTemplate(w, metaTemplate, data); err != nil { + return err + } + return nil +}
M new.gocommands/new.go

@@ -1,15 +1,14 @@

-package main +package commands import ( "fmt" - "io/ioutil" "os" "path/filepath" "strings" "time" ) -func viteNew(path string) { +func New(path string) error { _, file := filepath.Split(path) url := strings.TrimSuffix(file, filepath.Ext(file))

@@ -23,9 +22,8 @@ ---`, url, time.Now().Format("2006-01-02"))

_, err := os.Create(path) if err != nil { - printErr(err) - return + return err } - ioutil.WriteFile(path, []byte(content), 0644) - printMsg("created:", path) + os.WriteFile(path, []byte(content), 0644) + return nil }
D rss.go

@@ -1,47 +0,0 @@

-package main - -import ( - "os" - "path/filepath" - "sort" - "time" - - . "github.com/gorilla/feeds" -) - -func generateRSS(posts []Post, cfg Config) { - now := time.Now() - feed := &Feed{ - Title: cfg.Title, - Link: &Link{Href: cfg.SiteURL}, - Description: cfg.Description, - Author: &Author{ - Name: cfg.Author["name"], - Email: cfg.Author["email"], - }, - Created: now, - } - - // Sort posts by date - sort.Slice(posts, func(i, j int) bool { - return posts[j].Fm.Date.Time.Before(posts[i].Fm.Date.Time) - }) - - atomfile, err := os.Create(filepath.Join("build", "blog", "feed.xml")) - if err != nil { - printErr(err) - } - for _, p := range posts { - feed.Items = append(feed.Items, &Item{ - Title: p.Fm.Title, - Link: &Link{Href: cfg.RSSPrefixURL + p.Fm.URL}, - Description: string(p.Fm.Body), - Created: p.Fm.Date.Time, - }) - } - - err = feed.WriteAtom(atomfile) - if err != nil { - printErr(err) - } -}
M style.gomarkdown/style.go

@@ -1,4 +1,4 @@

-package main +package markdown import ( "github.com/alecthomas/chroma"
A util/copy.go

@@ -0,0 +1,73 @@

+package util + +import ( + "fmt" + "io" + "os" + "path/filepath" +) + +// CopyFile copies a file from src to dst. +func CopyFile(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer out.Close() + + _, err = io.Copy(out, in) + if err != nil { + return err + } + + // Copy modes. + f, err := os.Stat(src) + if err == nil { + err = os.Chmod(dst, f.Mode()) + if err != nil { + return err + } + } + + return out.Close() +} + +// CopyDir copies an entire directory tree from +// src to dst. +func CopyDir(src, dst string) error { + fi, err := os.Stat(src) + if err != nil { + return err + } + + if !fi.IsDir() { + return fmt.Errorf("error: %q is not a directory", fi) + } + + if err = os.MkdirAll(dst, 0755); err != nil { + return err + } + + items, _ := os.ReadDir(src) + for _, item := range items { + srcFilename := filepath.Join(src, item.Name()) + dstFilename := filepath.Join(dst, item.Name()) + if item.IsDir() { + if err := CopyDir(srcFilename, dstFilename); err != nil { + return err + } + } else { + if err := CopyFile(srcFilename, dstFilename); err != nil { + return err + } + } + } + + return nil +}
D utils.go

@@ -1,15 +0,0 @@

-package main - -import ( - "fmt" - "os" - "strings" -) - -func printMsg(s ...string) { - fmt.Println("vite:", strings.Join(s, " ")) -} - -func printErr(e error) { - fmt.Fprintln(os.Stderr, "error:", e) -}