-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtemplate_html.go
160 lines (132 loc) · 3.49 KB
/
template_html.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package xun
import (
"html/template"
"io"
"io/fs"
"strings"
"errors"
)
// FuncMap is a map of functions that are available to templates.
var FuncMap template.FuncMap = make(template.FuncMap)
// HtmlTemplate is a template that is loaded from a file system.
type HtmlTemplate struct {
template *template.Template
name string
path string
layout string
dependencies map[string]struct{}
dependents map[string]*HtmlTemplate
}
// NewHtmlTemplate creates a new HtmlTemplate with the given name and path.
func NewHtmlTemplate(name, path string) *HtmlTemplate {
return &HtmlTemplate{
name: name,
path: path,
dependencies: make(map[string]struct{}),
dependents: make(map[string]*HtmlTemplate),
}
}
// Load loads the template from the given file system.
//
// It parses the file, and determines the dependencies of the template.
// The dependencies are stored in the `dependencies` field.
func (t *HtmlTemplate) Load(fsys fs.FS, templates map[string]*HtmlTemplate) error { // skipcq: GO-R1005
buf, err := fs.ReadFile(fsys, t.path)
if err != nil {
return err
}
nt := template.New(t.name).Funcs(FuncMap)
dependencies := make(map[string]struct{})
defer func() {
t.template = nt
t.dependencies = dependencies
}()
if len(buf) == 0 {
// fixed err: `template: "?" is an incomplete or empty template
nt, _ = nt.Parse("")
return nil
}
nt, err = nt.Parse(string(buf))
if err != nil {
return err
}
for _, it := range nt.Templates() {
tn := it.Name()
if strings.EqualFold(tn, t.name) {
continue
}
dependencies[tn] = struct{}{}
}
layoutName := ""
// <!--layout:home--> xxxxx \n
if len(buf) > 11 && string(buf[0:11]) == "<!--layout:" {
n := len(buf) - 2
for i := 11; i < n; i++ {
if buf[i] == '-' && buf[i+1] == '-' && buf[i+2] == '>' {
layoutName = strings.TrimSpace(string(buf[11:i]))
break
}
if buf[i] == '\n' {
break
}
}
if layoutName != "" {
layoutName = "layouts/" + layoutName
layout, ok := templates[layoutName]
if ok {
_, err = nt.AddParseTree(layoutName, layout.template.Tree)
if err != nil {
return err
}
layout.dependents[t.name] = t
for tn := range layout.dependencies {
dependencies[tn] = struct{}{}
}
}
t.layout = layoutName
} else {
t.layout = ""
}
}
for tn := range dependencies {
it, ok := templates[tn]
if ok {
_, err = nt.AddParseTree(tn, it.template.Tree)
if err != nil {
return err
}
it.dependents[t.name] = t
}
}
return nil
}
// Reload reloads the template and all its dependents from the given file system.
//
// It first reloads the current template and then recursively reloads all its dependents.
// If a dependency does not exist, it is removed from the list of dependents.
func (t *HtmlTemplate) Reload(fsys fs.FS, templates map[string]*HtmlTemplate) error {
err := t.Load(fsys, templates)
if err != nil {
return err
}
for n, it := range t.dependents {
err := it.Reload(fsys, templates)
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return err
}
delete(t.dependents, n)
}
}
return nil
}
// Execute renders the template with the given data and writes the result to the provided writer.
//
// If the template has a layout, it uses the layout to render the data.
// Otherwise, it renders the data using the template itself.
func (t *HtmlTemplate) Execute(wr io.Writer, data any) error {
if t.layout != "" {
return t.template.ExecuteTemplate(wr, t.layout, data)
}
return t.template.Execute(wr, data)
}