// https://gobyexample.com/ package main import ( "encoding/json" "fmt" "log" "net/http" "os" "path/filepath" "strconv" "text/template" "time" ) type PageProperties struct { Title string StylePath string Breadcrumbs []*HeaderLink Children []*ChildPage } type HeaderLink struct { Name string Active bool } type PageConfig struct { Parent string Title string Image string } type ChildPage struct { Title string Path string Image string } var rootPath = "." var now = time.Now() func main() { serve := false args := os.Args[1:] if len(args) != 0 { rootPath = args[0] if len(args) == 2 { flag := args[1] switch flag { case "--serve": case "-s": serve = true default: fmt.Println(flag + " is not a recognized flag.") os.Exit(1) } } } build() if serve { http.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("out")))) http.HandleFunc("GET /{page}", pageHandler("out")) fmt.Println("Serving on http://localhost:8000") go waitForInput( "Press Enter to rebuild", build, ) log.Fatal(http.ListenAndServe(":8000", nil)) } } func pageHandler(rootPath string) func(http.ResponseWriter, *http.Request) { return func(responseWriter http.ResponseWriter, request *http.Request) { pageName := request.PathValue("page") pageFile, err := os.ReadFile(filepath.Join(rootPath, pageName+".html")) if err == nil { responseWriter.Write(pageFile) return } http.NotFound(responseWriter, request) } } func copyDir(srcPath string, dstPath string) { srcFS := os.DirFS(srcPath) os.CopyFS(dstPath, srcFS) } func __(foo any) {} func waitForInput(message string, onInput func()) { for { fmt.Println(message) var char rune _, err := fmt.Scanf("%c", &char) if err != nil { log.Fatal(err) } onInput() } } func build() { now = time.Now() fmt.Println("Starting build...") os.RemoveAll(filepath.Join("out")) os.Mkdir(filepath.Join("out"), 0777) fmt.Print("Building pages...") buildPages() fmt.Println("Done") fmt.Print("Copying assets...") copyDir( filepath.Join(rootPath, "assets"), filepath.Join("out", "assets"), ) cssOutPath := filepath.Join("out", "assets", "css") os.Rename( filepath.Join(cssOutPath, "style.css"), filepath.Join(cssOutPath, "style."+strconv.FormatInt(now.Unix(), 10)+".css"), ) fmt.Println("Done") fmt.Println("Build complete.") } func buildPages() { siteJson, err := os.ReadFile(filepath.Join(rootPath, "site.json")) if err != nil { fmt.Println(err) return } var pageConfigs map[string]*PageConfig err = json.Unmarshal(siteJson, &pageConfigs) if err != nil { fmt.Println(err) return } for fileName, page := range pageConfigs { parent := page.Parent breadcrumbs := make([]*HeaderLink, 0) for parent != "" { parentPage := pageConfigs[parent] breadcrumbs = append( []*HeaderLink{ { Name: parent, Active: false, }, }, breadcrumbs..., ) parent = parentPage.Parent } pageName := page.Title if pageName == "" { pageName = fileName } breadcrumbs = append( breadcrumbs, &HeaderLink{ Name: pageName, Active: true, }, ) outDir := filepath.Join("out") pageSrcDir := filepath.Join(rootPath, "pages") templateSrcDir := filepath.Join(rootPath, "templates") outfile, _ := os.Create(filepath.Join(outDir, fileName+".html")) template, _ := template.ParseFiles( filepath.Join(templateSrcDir, "document.html"), filepath.Join(templateSrcDir, "header.html"), filepath.Join(templateSrcDir, "child-links.html"), filepath.Join(pageSrcDir, fileName+".html"), ) pageProps := &PageProperties{ Title: pageName, StylePath: "/assets/css/style." + strconv.FormatInt(now.Unix(), 10) + ".css", Breadcrumbs: breadcrumbs, Children: pageChildren(fileName, pageConfigs), } template.Execute( outfile, pageProps, ) } } func pageChildren(parentPageName string, pages map[string]*PageConfig) []*ChildPage { children := make([]*ChildPage, 0) for path, page := range pages { if page.Parent == parentPageName { title := page.Title if title == "" { title = path } children = append( children, &ChildPage{ Title: title, Path: path, Image: page.Image, }, ) } } return children }