µsg, a tiny static site generator https://nilptr.dev/msg.html
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
msg/main.go

144 lines
2.9 KiB

package main
import (
"errors"
"html/template"
"io"
"io/fs"
"log"
"os"
"path/filepath"
"strings"
"github.com/microcosm-cc/bluemonday"
"github.com/russross/blackfriday/v2"
"gopkg.in/yaml.v3"
_ "embed"
)
//go:embed assets/tmpl.html
var tmplData string
var tmpl = template.Must(template.New("").Parse(tmplData))
type Data struct {
Title string `yaml:"title"`
Source string `yaml:"source"`
Navigation []NavEntry `yaml:"navigation"`
}
type NavEntry struct {
Name string `yaml:"name"`
URL string `yaml:"url"`
}
func main() {
cfg, err := os.ReadFile("_config.yaml")
if err != nil {
log.Fatalln("reading config:", err)
}
var dat Data
err = yaml.Unmarshal(cfg, &dat)
if err != nil {
log.Fatalln("reading config:", err)
}
// create a build directory
if fi, err := os.Stat("build"); errors.Is(err, os.ErrNotExist) {
err := os.Mkdir("build", 0o775)
if err != nil {
log.Fatalln("error creating build directory:", err)
}
} else if !fi.IsDir() {
log.Fatalln("build/ must be a directory, aborting")
}
// first, render all .md files into HTML
err = filepath.WalkDir("data", func(path string, d fs.DirEntry, err error) error {
// ignore everything that is not a markdown file
if !(strings.HasSuffix(path, ".md") || d.IsDir()) ||
strings.HasPrefix(d.Name(), "_") ||
strings.HasPrefix(d.Name(), ".") {
if d.IsDir() {
return filepath.SkipDir
}
return nil
}
if d.IsDir() {
return nil
}
md, err := os.ReadFile(path)
if err != nil {
log.Fatalf("reading markdown file %q: %v\n", path, err)
}
err = renderHTML(dat, strings.TrimPrefix(path, "data/"), md)
if err != nil {
log.Fatalf("rendering HTML for %q: %v\n", path, err)
}
return nil
})
if err != nil {
log.Fatalln("error writing markdown files:", err)
}
// copy all other (non-ignored) files to build directory
err = filepath.WalkDir("data", func(path string, d fs.DirEntry, err error) error {
if strings.HasSuffix(path, ".md") ||
strings.HasPrefix(d.Name(), "_") ||
strings.HasPrefix(d.Name(), ".") {
if d.IsDir() {
return filepath.SkipDir
}
return nil
}
if d.IsDir() {
return nil
}
log.Printf("copying %q to build directory", path)
f, err := os.Open(path)
if err != nil {
return err
}
out, err := os.Create(filepath.Join("build", strings.TrimPrefix(path, "data/")))
if err != nil {
return err
}
_, err = io.Copy(out, f)
return err
})
if err != nil {
log.Fatalln("error copying non-markdown files:", err)
}
}
type RenderData struct {
Data
Text template.HTML
}
func renderHTML(cfg Data, name string, data []byte) error {
log.Printf("converting %q", name)
name = strings.TrimSuffix(name, ".md")
f, err := os.Create(filepath.Join("build", name+".html"))
if err != nil {
return err
}
defer f.Close()
text := template.HTML(bluemonday.UGCPolicy().SanitizeBytes(blackfriday.Run(data)))
return tmpl.Execute(f, RenderData{
Data: cfg,
Text: text,
})
}