/
template.go
135 lines (109 loc) · 3.36 KB
/
template.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package goutils
import (
"fmt"
"html/template"
"os"
"path/filepath"
"strings"
"github.com/Masterminds/sprig"
)
// Loader loads layout, partial and template files from a specified
// directory.
//
// Dealing with partials and layouts in template files is notoriously tricky
// using the standard Golang template parser. This Loader makes is
// easier to simply load a template by name and parse one of the blocks within
// that template file.
type Loader struct {
Dir string
LayoutsDir string
PartialsDir string
Suffix string
}
// NewTemplateLoader creates a Loader with the recommended layouts
// directory "/layouts" and partials directory "/partials". These directories
// are relative from the specified template directory.
func NewTemplateLoader(dir string) *Loader {
return &Loader{
Dir: dir,
LayoutsDir: "/layouts",
PartialsDir: "/partials",
Suffix: ".tmpl",
}
}
// LoadAllTemplates creates separate templates for each template
// in the template directory.
func (t *Loader) LoadAllTemplates() (map[string]*template.Template, error) {
files, err := os.ReadDir(t.Dir)
if err != nil {
return nil, fmt.Errorf("reading dir %q: %w", t.Dir, err)
}
tmpls := make(map[string]*template.Template)
for _, f := range files {
tmplName := strings.TrimSuffix(f.Name(), filepath.Ext(f.Name()))
info, err := f.Info()
if err != nil {
return nil, fmt.Errorf("reading file info of %q: %w", f.Name(), err)
}
if t.isTemplate(info) {
tmpl, err := t.LoadTemplate(tmplName)
if err != nil {
return nil, fmt.Errorf("loading template file %q: %w", tmplName, err)
}
tmpls[tmplName] = tmpl
}
}
return tmpls, nil
}
// LoadTemplate loads a single template file and any partials and layout templates
// from the template directory.
func (t *Loader) LoadTemplate(templateName string) (*template.Template, error) {
fs, err := t.getTemplateFileNames(templateName)
if err != nil {
return nil, err
}
return template.Must(template.New(templateName).Funcs(sprig.FuncMap()).ParseFiles(fs...)), nil
}
// GetTemplateFileNames returns all template filenames that should be loaded,
// including the layout and partial templates (located in ./layouts and ./partials).
func (t *Loader) getTemplateFileNames(templateName string) ([]string, error) {
templatePath := t.Dir + "/" + templateName + t.Suffix
if _, err := os.Stat(templatePath); err != nil {
return nil, fmt.Errorf("reading template file info of %q: %w", templatePath, err)
}
fs := make([]string, 0)
fs = append(fs, templatePath)
// Load partials
filenames, err := t.dirFilenames(t.Dir + t.PartialsDir)
if err != nil {
return nil, err
}
fs = append(fs, filenames...)
// Load layouts
layouts, err := t.dirFilenames(t.Dir + t.LayoutsDir)
if err != nil {
return nil, err
}
fs = append(fs, layouts...)
return fs, nil
}
func (t *Loader) dirFilenames(dir string) ([]string, error) {
fs := make([]string, 0)
partials, err := os.ReadDir(dir)
if err != nil {
return nil, fmt.Errorf("reading dir %q: %w", dir, err)
}
for _, f := range partials {
info, err := f.Info()
if err != nil {
return nil, fmt.Errorf("reading file info of %q: %w", f.Name(), err)
}
if t.isTemplate(info) {
fs = append(fs, dir+"/"+f.Name())
}
}
return fs, nil
}
func (t *Loader) isTemplate(f os.FileInfo) bool {
return !f.IsDir() && len(f.Name()) > 0 && strings.HasSuffix(f.Name(), t.Suffix)
}