package tl import ( "html/template" "io/fs" "os" "path" "path/filepath" "strings" ) // Template collection. type Collection struct { // Path to the directory with templates. RootDir string // By default, template name is it's path relative to [RootDir]. // This prefix will be prepended to a template name: Prefix + "/" + RelativePath. // You can leave it empty, then it will be ignored. Prefix string } // Advanced options. type Options struct { // Files from shared collections will be available in every main template. SharedCollections []Collection } // Load templates from specified directories with default options. // Files from the main collection will be added to the returned map. func Load(mainCollection Collection) (map[string]*template.Template, error) { return LoadWithOptions(mainCollection, Options{}) } // Load templates from specified directories. // Files from [mainTemplates] collection will be added to the returned map. func LoadWithOptions( mainCollection Collection, options Options, ) (map[string]*template.Template, error) { sharedTemplate := template.New("") for _, stp := range options.SharedCollections { files, err := getFiles(stp.RootDir, stp.Prefix) if err != nil { return nil, err } for name, contents := range files { sharedTemplate, err = sharedTemplate.New(name).Parse(contents) if err != nil { return nil, err } } } templateFiles, err := getFiles(mainCollection.RootDir, mainCollection.Prefix) if err != nil { return nil, err } res := make(map[string]*template.Template) for name, contents := range templateFiles { clone, err := sharedTemplate.Clone() if err != nil { return nil, err } res[name], err = clone.New(name).Parse(contents) if err != nil { return nil, err } } return res, nil } func getFiles(root string, keyPrefix string) (map[string]string, error) { res := make(map[string]string) if !strings.HasSuffix(root, "/") { root += "/" } err := filepath.WalkDir(root, func(entrypath string, d fs.DirEntry, err error) error { if err != nil { return err } if d.IsDir() { return nil } shortPath := path.Join(keyPrefix, strings.TrimPrefix(entrypath, root)) contents, err := os.ReadFile(entrypath) if err != nil { return err } res[shortPath] = string(contents) return nil }) return res, err }