-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.go
156 lines (136 loc) · 4.36 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"sort"
"time"
)
func main() {
args := os.Args[1:]
if len(args) != 2 {
log.Fatal("Usage: gensite <src dir> <dst dir>")
}
srcDir, dstDir := args[0], args[1]
srcFile := func(elem ...string) string {
elem = append([]string{srcDir}, elem...)
return filepath.Join(elem...)
}
dstFile := func(elem ...string) string {
elem = append([]string{dstDir}, elem...)
return filepath.Join(elem...)
}
// Read site configuration.
conf := &siteConf{}
decodeTOML(srcFile("index.toml"), conf)
if conf.RootURL == "" {
log.Fatal("RootURL must be specified; needed by feed and sitemap")
}
if conf.Template == "" {
log.Fatal("Template must be specified")
}
if conf.BaseCSS == nil {
log.Fatal("BaseCSS must be specified")
}
template := readFile(srcFile(conf.Template))
baseCSS := catInDir(srcDir, conf.BaseCSS)
// Initialize templates. They are all initialized from the same source code,
// plus a snippet to fix the "content" reference.
categoryTmpl := newTemplate("category", "..", template, contentIs("category"))
articleTmpl := newTemplate("article", "..", template, contentIs("article"))
homepageTmpl := newTemplate("homepage", ".", template, contentIs("article"))
feedTmpl := newTemplate("feed", ".", feedTemplText)
// Base for the {{ . }} object used in all templates.
base := newBaseDot(conf, baseCSS)
// Up to conf.FeedPosts recent posts, used in the feed.
recents := recentArticles{nil, conf.FeedPosts}
// Last modified time of the newest post, used in the feed.
var lastModified time.Time
// Paths of all generated URLs, relative to the destination directory,
// always without "index.html". Used to generate the sitemap.
allPaths := []string{""}
// Render a category index.
renderCategoryIndex := func(name, prelude, css, js string, groups []group) {
// Add category index to the sitemap, without "/index.html"
allPaths = append(allPaths, name)
// Create directory
catDir := dstFile(name)
err := os.MkdirAll(catDir, 0755)
if err != nil {
log.Fatal(err)
}
// Generate index
cd := &categoryDot{base, name, prelude, groups, css, js}
executeToFile(categoryTmpl, cd, filepath.Join(catDir, "index.html"))
}
for _, cat := range conf.Categories {
catConf := &categoryConf{}
decodeTOML(srcFile(cat.Name, "index.toml"), catConf)
prelude := ""
if catConf.Prelude != "" {
prelude = readFile(srcFile(cat.Name, catConf.Prelude+".html"))
}
css := catInDir(srcFile(cat.Name), catConf.ExtraCSS)
js := catInDir(srcFile(cat.Name), catConf.ExtraJS)
var groups []group
if catConf.AutoIndex {
groups = makeGroups(catConf.Articles, catConf.Groups)
}
renderCategoryIndex(cat.Name, prelude, css, js, groups)
// Generate articles
for _, am := range catConf.Articles {
// Add article URL to sitemap.
p := filepath.Join(cat.Name, am.Name+".html")
allPaths = append(allPaths, p)
a := getArticle(article{Category: cat.Name}, am, srcFile(cat.Name))
modTime := time.Time(a.LastModified)
if modTime.After(lastModified) {
lastModified = modTime
}
// Generate article page.
ad := &articleDot{base, a}
executeToFile(articleTmpl, ad, dstFile(p))
recents.insert(a)
}
}
// Generate index page. XXX(xiaq): duplicated code with generating ordinary
// article pages.
a := getArticle(article{IsHomepage: true, Category: "homepage"}, conf.Index, srcDir)
ad := &articleDot{base, a}
executeToFile(homepageTmpl, ad, dstFile("index.html"))
// Generate feed.
feedArticles := recents.articles
fd := feedDot{base, feedArticles, rfc3339Time(lastModified)}
executeToFile(feedTmpl, fd, dstFile("feed.atom"))
// Generate site map.
file := openForWrite(dstFile("sitemap.txt"))
defer file.Close()
for _, p := range allPaths {
fmt.Fprintf(file, "%s/%s\n", conf.RootURL, p)
}
}
func makeGroups(articles []articleMeta, groupMetas []groupMeta) []group {
groups := make(map[int]*group)
for _, am := range articles {
g := groups[am.Group]
if g == nil {
g = &group{}
if 0 <= am.Group && am.Group < len(groupMetas) {
g.groupMeta = groupMetas[am.Group]
}
groups[am.Group] = g
}
g.Articles = append(g.Articles, am)
}
indices := make([]int, 0, len(groups))
for i := range groups {
indices = append(indices, i)
}
sort.Ints(indices)
sortedGroups := make([]group, len(groups))
for i, idx := range indices {
sortedGroups[i] = *groups[idx]
}
return sortedGroups
}