fast and minimal static site generator
ssg

Experimental: use map[string]any for post Meta

Changed files
+60 -57
atom
commands
formats
types
+7 -7
atom/feed.go
··· 6 6 "path/filepath" 7 7 "time" 8 8 9 - "git.icyphox.sh/vite/config" 10 - "git.icyphox.sh/vite/types" 9 + "tangled.sh/icyphox.sh/vite/config" 10 + "tangled.sh/icyphox.sh/vite/types" 11 11 ) 12 12 13 13 type AtomLink struct { ··· 54 54 entries := []AtomEntry{} 55 55 56 56 for _, p := range posts { 57 - dateStr := p.Meta["date"] 57 + dateStr := p.Meta["date"].(string) 58 58 date, err := time.Parse("2006-01-02", dateStr) 59 59 if err != nil { 60 60 return nil, err ··· 62 62 rfc3339 := date.Format(time.RFC3339) 63 63 64 64 entry := AtomEntry{ 65 - Title: p.Meta["title"], 65 + Title: p.Meta["title"].(string), 66 66 Updated: rfc3339, 67 67 // tag:icyphox.sh,2019-10-23:blog/some-post/ 68 68 ID: fmt.Sprintf( 69 69 "tag:%s,%s:%s", 70 70 config.Config.URL[8:], // strip https:// 71 71 dateStr, 72 - filepath.Join(srcDir, p.Meta["slug"]), 72 + filepath.Join(srcDir, p.Meta["slug"].(string)), 73 73 ), 74 74 // filepath.Join strips the second / in http:// 75 - Link: &AtomLink{Href: config.Config.URL + filepath.Join(srcDir, p.Meta["slug"])}, 75 + Link: &AtomLink{Href: config.Config.URL + filepath.Join(srcDir, p.Meta["slug"].(string))}, 76 76 Summary: &AtomSummary{ 77 77 Content: fmt.Sprintf("<h2>%s</h2>\n%s", 78 - p.Meta["subtitle"], 78 + p.Meta["subtitle"].(string), 79 79 string(p.Body)), 80 80 Type: "html", 81 81 },
+10 -10
commands/build.go
··· 8 8 "strings" 9 9 "time" 10 10 11 - "git.icyphox.sh/vite/atom" 12 - "git.icyphox.sh/vite/config" 13 - "git.icyphox.sh/vite/formats" 14 - "git.icyphox.sh/vite/formats/markdown" 15 - "git.icyphox.sh/vite/formats/yaml" 16 - "git.icyphox.sh/vite/types" 17 - "git.icyphox.sh/vite/util" 11 + "tangled.sh/icyphox.sh/vite/atom" 12 + "tangled.sh/icyphox.sh/vite/config" 13 + "tangled.sh/icyphox.sh/vite/formats" 14 + "tangled.sh/icyphox.sh/vite/formats/markdown" 15 + "tangled.sh/icyphox.sh/vite/formats/yaml" 16 + "tangled.sh/icyphox.sh/vite/types" 17 + "tangled.sh/icyphox.sh/vite/util" 18 18 ) 19 19 20 20 type Dir struct { ··· 181 181 182 182 // Copy the post to the root if it's marked as such. 183 183 // ex: build/blog/foo-bar -> build/foo-bar 184 - if post.Meta["atroot"] == "true" { 184 + if post.Meta["atroot"].(bool) { 185 185 os.Mkdir(filepath.Join(types.BuildDir, slug), 0755) 186 186 df := filepath.Join(types.BuildDir, slug+".html") 187 187 util.CopyFile(filepath.Join(dstDir, slug, "index.html"), df) ··· 189 189 } 190 190 191 191 sort.Slice(posts, func(i, j int) bool { 192 - dateStr1 := posts[j].Meta["date"] 193 - dateStr2 := posts[i].Meta["date"] 192 + dateStr1 := posts[j].Meta["date"].(string) 193 + dateStr2 := posts[i].Meta["date"].(string) 194 194 date1, _ := time.Parse("2006-01-02", dateStr1) 195 195 date2, _ := time.Parse("2006-01-02", dateStr2) 196 196 return date1.Before(date2)
+5 -5
formats/anything.go
··· 3 3 import ( 4 4 "path/filepath" 5 5 6 - "git.icyphox.sh/vite/util" 6 + "tangled.sh/icyphox.sh/vite/util" 7 7 ) 8 8 9 9 // Anything is a stub format for unrecognized files 10 10 type Anything struct{ Path string } 11 11 12 - func (Anything) Ext() string { return "" } 13 - func (Anything) Frontmatter() map[string]string { return nil } 14 - func (Anything) Body() string { return "" } 15 - func (a Anything) Basename() string { return filepath.Base(a.Path) } 12 + func (Anything) Ext() string { return "" } 13 + func (Anything) Frontmatter() map[string]any { return nil } 14 + func (Anything) Body() string { return "" } 15 + func (a Anything) Basename() string { return filepath.Base(a.Path) } 16 16 17 17 func (a Anything) Render(dest string, data interface{}, drafts bool) error { 18 18 return util.CopyFile(a.Path, dest)
+13 -12
formats/markdown/markdown.go
··· 8 8 gotmpl "text/template" 9 9 "time" 10 10 11 - "git.icyphox.sh/vite/config" 12 - "git.icyphox.sh/vite/template" 13 - "git.icyphox.sh/vite/types" 14 11 "github.com/adrg/frontmatter" 12 + "tangled.sh/icyphox.sh/vite/config" 13 + "tangled.sh/icyphox.sh/vite/template" 14 + "tangled.sh/icyphox.sh/vite/types" 15 15 16 16 bf "git.icyphox.sh/grayfriday" 17 17 ) ··· 26 26 27 27 type Markdown struct { 28 28 body []byte 29 - frontmatter map[string]string 29 + frontmatter map[string]any 30 30 Path string 31 31 } 32 32 ··· 49 49 // template checks the frontmatter for a specified template or falls back 50 50 // to the default template -- to which it, well, templates whatever is in 51 51 // data and writes it to dest. 52 - func (md *Markdown) template(dest, tmplDir string, data interface{}) error { 53 - metaTemplate := md.frontmatter["template"] 54 - if metaTemplate == "" { 52 + func (md *Markdown) template(dest, tmplDir string, data any) error { 53 + metaTemplate, ok := md.frontmatter["template"].(string) 54 + if !ok || metaTemplate == "" { 55 55 metaTemplate = config.Config.DefaultTemplate 56 56 } 57 57 ··· 81 81 return nil 82 82 } 83 83 84 - func (md *Markdown) Frontmatter() map[string]string { 84 + func (md *Markdown) Frontmatter() map[string]any { 85 85 return md.frontmatter 86 86 } 87 87 ··· 91 91 92 92 type templateData struct { 93 93 Cfg config.ConfigYaml 94 - Meta map[string]string 94 + Meta map[string]any 95 95 Body string 96 - Extra interface{} 96 + Extra any 97 97 } 98 98 99 - func (md *Markdown) Render(dest string, data interface{}, drafts bool) error { 99 + func (md *Markdown) Render(dest string, data any, drafts bool) error { 100 100 source, err := os.ReadFile(md.Path) 101 101 if err != nil { 102 102 return fmt.Errorf("markdown: error reading file: %w", err) ··· 107 107 return fmt.Errorf("markdown: error extracting frontmatter: %w", err) 108 108 } 109 109 110 - if md.frontmatter["draft"] == "true" { 110 + isDraft, ok := md.frontmatter["draft"].(bool) 111 + if ok && isDraft { 111 112 if !drafts { 112 113 fmt.Printf("vite: skipping draft %s\n", md.Path) 113 114 return nil
+21 -19
formats/yaml/yaml.go
··· 7 7 gotmpl "text/template" 8 8 "time" 9 9 10 - "git.icyphox.sh/vite/config" 11 - "git.icyphox.sh/vite/template" 12 - "git.icyphox.sh/vite/types" 13 10 "gopkg.in/yaml.v3" 11 + "tangled.sh/icyphox.sh/vite/config" 12 + "tangled.sh/icyphox.sh/vite/template" 13 + "tangled.sh/icyphox.sh/vite/types" 14 14 ) 15 15 16 16 type YAML struct { 17 17 Path string 18 18 19 - meta map[string]string 19 + meta map[string]any 20 20 } 21 21 22 22 func (*YAML) Ext() string { return ".yaml" } 23 23 func (*YAML) Body() string { return "" } 24 24 func (y *YAML) Basename() string { return filepath.Base(y.Path) } 25 25 26 - func (y *YAML) Frontmatter() map[string]string { 26 + func (y *YAML) Frontmatter() map[string]any { 27 27 return y.meta 28 28 } 29 29 30 30 type templateData struct { 31 31 Cfg config.ConfigYaml 32 - Meta map[string]string 33 - Yaml map[string]interface{} 32 + Meta map[string]any 33 + Yaml map[string]any 34 34 Body string 35 35 } 36 36 37 - func (y *YAML) template(dest, tmplDir string, data interface{}) error { 38 - metaTemplate := y.meta["template"] 37 + func (y *YAML) template(dest, tmplDir string, data any) error { 38 + var metaTemplate string 39 + if templateVal, ok := y.meta["template"]; ok { 40 + if strVal, isStr := templateVal.(string); isStr { 41 + metaTemplate = strVal 42 + } 43 + } 39 44 if metaTemplate == "" { 40 45 metaTemplate = config.Config.DefaultTemplate 41 46 } ··· 54 59 return tmpl.Write(dest, metaTemplate, data) 55 60 } 56 61 57 - func (y *YAML) Render(dest string, data interface{}, drafts bool) error { 62 + func (y *YAML) Render(dest string, data any, drafts bool) error { 58 63 yamlBytes, err := os.ReadFile(y.Path) 59 64 if err != nil { 60 65 return fmt.Errorf("yaml: failed to read file: %s: %w", y.Path, err) 61 66 } 62 67 63 - yamlData := map[string]interface{}{} 68 + yamlData := map[string]any{} 64 69 err = yaml.Unmarshal(yamlBytes, yamlData) 65 70 if err != nil { 66 71 return fmt.Errorf("yaml: failed to unmarshal yaml file: %s: %w", y.Path, err) 67 72 } 68 73 69 - metaInterface := yamlData["meta"].(map[string]interface{}) 70 - 71 - meta := make(map[string]string) 72 - for k, v := range metaInterface { 73 - vStr := convertToString(v) 74 - meta[k] = vStr 74 + metaInterface, ok := yamlData["meta"].(map[string]any) 75 + if !ok { 76 + return fmt.Errorf("yaml: meta section is not a map: %s", y.Path) 75 77 } 76 78 77 - y.meta = meta 79 + y.meta = metaInterface 78 80 79 81 err = y.template(dest, types.TemplatesDir, templateData{ 80 82 config.Config, ··· 89 91 return nil 90 92 } 91 93 92 - func convertToString(value interface{}) string { 94 + func convertToString(value any) string { 93 95 // Infer type and convert to string 94 96 switch v := value.(type) { 95 97 case string:
+1 -1
go.mod
··· 1 - module git.icyphox.sh/vite 1 + module tangled.sh/icyphox.sh/vite 2 2 3 3 go 1.21 4 4
+1 -1
main.go
··· 4 4 "fmt" 5 5 "os" 6 6 7 - "git.icyphox.sh/vite/commands" 7 + "tangled.sh/icyphox.sh/vite/commands" 8 8 ) 9 9 10 10 func main() {
+2 -2
types/types.go
··· 16 16 Render(dest string, data interface{}, drafts bool) error 17 17 18 18 // Frontmatter will not be populated if Render hasn't been called. 19 - Frontmatter() map[string]string 19 + Frontmatter() map[string]any 20 20 // Body will not be populated if Render hasn't been called. 21 21 Body() string 22 22 Basename() string ··· 24 24 25 25 // Only used for building indexes and Atom feeds 26 26 type Post struct { 27 - Meta map[string]string 27 + Meta map[string]any 28 28 // HTML-formatted body of post 29 29 Body string 30 30 }