package webutil import ( "embed" "html/template" "io/fs" "log" "path" "strings" ) // ParseTemplateSet parses sets of templates from an embed.FS. // // Each directory constitutes a set of templates that are parsed together. // // Structure (within a directory): // - share/* are always parsed. // - base.html will be parsed with each other file in same dir // // Call a template with m[path].Execute(w, data) (root dir name is excluded). // // For example, if you have // - /user/share/* // - /user/base.html // - /user/home.html // // Then you call m["/user/home.html"].Execute(w, data). func ParseTemplateSet(funcs template.FuncMap, fs embed.FS) map[string]*template.Template { m := map[string]*template.Template{} rootDir := readDir(fs, ".")[0].Name() loadTemplateDir(fs, funcs, m, rootDir, rootDir) return m } func loadTemplateDir( fs embed.FS, funcs template.FuncMap, m map[string]*template.Template, dirPath string, rootDir string, ) map[string]*template.Template { t := template.New("") if funcs != nil { t = t.Funcs(funcs) } shareDir := path.Join(dirPath, "share") if _, err := fs.ReadDir(shareDir); err == nil { log.Printf("Parsing %s...", path.Join(shareDir, "*")) t = template.Must(t.ParseFS(fs, path.Join(shareDir, "*"))) } if data, _ := fs.ReadFile(path.Join(dirPath, "base.html")); data != nil { log.Printf("Parsing %s...", path.Join(dirPath, "base.html")) t = template.Must(t.Parse(string(data))) } for _, ent := range readDir(fs, dirPath) { if ent.Type().IsDir() { if ent.Name() != "share" { m = loadTemplateDir(fs, funcs, m, path.Join(dirPath, ent.Name()), rootDir) } continue } if !ent.Type().IsRegular() { continue } if ent.Name() == "base.html" { continue } filePath := path.Join(dirPath, ent.Name()) log.Printf("Parsing %s...", filePath) key := strings.TrimPrefix(path.Join(dirPath, ent.Name()), rootDir) tt := template.Must(t.Clone()) tt = template.Must(tt.Parse(readFile(fs, filePath))) m[key] = tt } return m } func readDir(fs embed.FS, dirPath string) []fs.DirEntry { ents, err := fs.ReadDir(dirPath) if err != nil { panic(err) } return ents } func readFile(fs embed.FS, path string) string { data, err := fs.ReadFile(path) if err != nil { panic(err) } return string(data) }