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}