all repos — vite @ cc1488114f0c8b9aacd9e328680daae7028d1ed9

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

build.go (view raw)

  1package main
  2
  3import (
  4	"fmt"
  5	"io/ioutil"
  6	"os"
  7	"os/exec"
  8	"path/filepath"
  9	"sort"
 10	"strings"
 11	"text/template"
 12
 13	"github.com/cross-cpm/go-shutil"
 14)
 15
 16var cfg = parseConfig()
 17
 18type Post struct {
 19	Fm Matter
 20}
 21
 22var posts []Post
 23
 24type NewFm struct {
 25	Template string
 26	URL      string
 27	Title    string
 28	Subtitle string
 29	Date     string
 30	Body     string
 31}
 32
 33func execute(cmds []string) {
 34	for _, cmd := range cmds {
 35		out, err := exec.Command(cmd).Output()
 36		printMsg("running:", cmd)
 37		if err != nil {
 38			printErr(err)
 39			fmt.Println(string(out))
 40		}
 41	}
 42}
 43
 44func processTemplate(tmplPath string) *template.Template {
 45	tmplFile := filepath.Join("templates", tmplPath)
 46	tmpl, err := template.ParseFiles(tmplFile)
 47	if err != nil {
 48		printErr(err)
 49	}
 50
 51	return tmpl
 52}
 53
 54func handleMd(mdPath string) {
 55	content, err := ioutil.ReadFile(mdPath)
 56	if err != nil {
 57		printErr(err)
 58	}
 59
 60	restContent, fm := parseFrontmatter(content)
 61	bodyHtml := mdRender(restContent, cfg)
 62	relPath, _ := filepath.Rel("pages/", mdPath)
 63
 64	var buildPath string
 65	if strings.HasSuffix(relPath, "_index.md") {
 66		dir, _ := filepath.Split(relPath)
 67		buildPath = filepath.Join("build", dir)
 68	} else {
 69		buildPath = filepath.Join(
 70			"build",
 71			strings.TrimSuffix(relPath, filepath.Ext(relPath)),
 72		)
 73	}
 74
 75	os.MkdirAll(buildPath, 0755)
 76
 77	fm.Body = string(bodyHtml)
 78
 79	if strings.Contains(relPath, "blog/") {
 80		posts = append(posts, Post{fm})
 81	}
 82
 83	var newFm = NewFm{
 84		fm.Template,
 85		fm.URL,
 86		fm.Title,
 87		fm.Subtitle,
 88		fm.Date.Time.Format(cfg.DateFmt),
 89		fm.Body,
 90	}
 91	// combine config and matter structs
 92	combined := struct {
 93		Cfg Config
 94		Fm  NewFm
 95	}{cfg, newFm}
 96
 97	htmlFile, err := os.Create(filepath.Join(buildPath, "index.html"))
 98	if err != nil {
 99		printErr(err)
100		return
101	}
102	if fm.Template == "" {
103		fm.Template = "text.html"
104	}
105	tmpl := processTemplate(fm.Template)
106	err = tmpl.Execute(htmlFile, combined)
107	if err != nil {
108		printErr(err)
109		return
110	}
111	htmlFile.Close()
112}
113
114func renderIndex(posts []Post) {
115	indexTmpl := processTemplate("index.html")
116	path := filepath.Join("pages", "_index.md")
117
118	// Sort posts by date
119	sort.Slice(posts, func(i, j int) bool {
120		return posts[j].Fm.Date.Time.Before(posts[i].Fm.Date.Time)
121	})
122
123	content, err := ioutil.ReadFile(path)
124	if err != nil {
125		printErr(err)
126	}
127
128	restContent, fm := parseFrontmatter(content)
129	bodyHtml := mdRender(restContent, cfg)
130	fm.Body = string(bodyHtml)
131
132	var newFm = NewFm{
133		fm.Template,
134		fm.URL,
135		fm.Title,
136		fm.Subtitle,
137		fm.Date.Time.Format(cfg.DateFmt),
138		fm.Body,
139	}
140
141	combined := struct {
142		Fm    NewFm
143		Posts []Post
144		Cfg   Config
145	}{newFm, posts, cfg}
146
147	htmlFile, err := os.Create(filepath.Join("build", "index.html"))
148	err = indexTmpl.Execute(htmlFile, combined)
149	if err != nil {
150		printErr(err)
151		return
152	}
153	htmlFile.Close()
154}
155
156func viteBuild() {
157	if len(cfg.Prebuild) != 0 {
158		printMsg("executing pre-build actions...")
159		execute(cfg.Prebuild)
160	}
161	err := filepath.Walk("./pages", func(path string, info os.FileInfo, err error) error {
162		if err != nil {
163			printErr(err)
164			return err
165		}
166		if filepath.Ext(path) == ".md" && path != filepath.Join("pages", "_index.md") {
167			handleMd(path)
168		} else {
169			f, err := os.Stat(path)
170			if err != nil {
171				printErr(err)
172			}
173			mode := f.Mode()
174			if mode.IsRegular() {
175				options := shutil.CopyOptions{}
176				relPath, _ := filepath.Rel("pages/", path)
177				options.FollowSymlinks = true
178				shutil.CopyFile(
179					path,
180					filepath.Join("build", relPath),
181					&options,
182				)
183			}
184		}
185		return nil
186	})
187
188	if err != nil {
189		printErr(err)
190	}
191
192	// Deal with the special snowflake '_index.md'
193	renderIndex(posts)
194
195	_, err = shutil.CopyTree("static", filepath.Join("build", "static"), nil)
196	if err != nil {
197		printErr(err)
198	}
199	printMsg("site build complete")
200	printMsg("generating feeds...")
201	generateRSS(posts, cfg)
202	if len(cfg.Postbuild) != 0 {
203		printMsg("executing post-build actions...")
204		execute(cfg.Postbuild)
205	}
206}