-
Notifications
You must be signed in to change notification settings - Fork 2
/
renders.go
165 lines (138 loc) · 3.73 KB
/
renders.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
161
162
163
164
165
package renders
import (
"fmt"
"html/template"
"os"
"path/filepath"
"regexp"
"strings"
"sync"
)
var (
cache []*namedTemplate
regularTemplateDefs []string
basePath string
exts []string
lock sync.Mutex
re_defineTag *regexp.Regexp = regexp.MustCompile("{{ ?define \"([^\"]*)\" ?\"?([a-zA-Z0-9]*)?\"? ?}}")
re_templateTag *regexp.Regexp = regexp.MustCompile("{{ ?template \"([^\"]*)\" ?([^ ]*)? ?}}")
)
type namedTemplate struct {
Name string
Src string
}
// Load prepares and parses all templates from the passed basePath
func Load(opt Options) (map[string]*template.Template, error) {
basePath = opt.Directory
exts = opt.Extensions
return loadTemplates(nil)
}
// LoadWithFuncMap prepares and parses all templates from the passed basePath and injects
// a custom template.FuncMap into each template
func LoadWithFuncMap(opt Options) (map[string]*template.Template, error) {
basePath = opt.Directory
exts = opt.Extensions
return loadTemplates(opt.Funcs)
}
func loadTemplates(funcMap template.FuncMap) (map[string]*template.Template, error) {
lock.Lock()
defer lock.Unlock()
templates := make(map[string]*template.Template)
err := filepath.Walk(basePath, func(path string, fi os.FileInfo, err error) error {
r, err := filepath.Rel(basePath, path)
if err != nil {
return err
}
ext := getExt(r)
for _, extension := range exts {
if ext == extension {
if err := add(path); err != nil {
panic(err)
}
// Now we find all regular template definitions and check for the most recent definiton
for _, t := range regularTemplateDefs {
found := false
defineIdx := 0
// From the beginning (which should) most specifc we look for definitions
for _, nt := range cache {
nt.Src = re_defineTag.ReplaceAllStringFunc(nt.Src, func(raw string) string {
parsed := re_defineTag.FindStringSubmatch(raw)
name := parsed[1]
if name != t {
return raw
}
// Don't touch the first definition
if !found {
found = true
return raw
}
defineIdx += 1
return fmt.Sprintf("{{ define \"%s_invalidated_#%d\" }}", name, defineIdx)
})
}
}
var (
baseTmpl *template.Template
i int
)
for _, nt := range cache {
var currentTmpl *template.Template
if i == 0 {
baseTmpl = template.New(nt.Name)
currentTmpl = baseTmpl
} else {
currentTmpl = baseTmpl.New(nt.Name)
}
template.Must(currentTmpl.Funcs(funcMap).Parse(nt.Src))
i++
}
tname := generateTemplateName(basePath, path)
templates[tname] = baseTmpl
// Make sure we empty the cache between runs
cache = cache[0:0]
break
//return nil
}
}
return nil
})
return templates, err
}
func add(path string) error {
// Get file content
tplSrc, err := file_content(path)
if err != nil {
return err
}
tplName := generateTemplateName(basePath, path)
// Make sure template is not already included
alreadyIncluded := false
for _, nt := range cache {
if nt.Name == tplName {
alreadyIncluded = true
break
}
}
if alreadyIncluded {
return nil
}
// Add to the cache
nt := &namedTemplate{
Name: tplName,
Src: tplSrc,
}
cache = append(cache, nt)
// Check for any template block
for _, raw := range re_templateTag.FindAllString(nt.Src, -1) {
parsed := re_templateTag.FindStringSubmatch(raw)
templatePath := parsed[1]
ext := getExt(templatePath)
if !strings.Contains(templatePath, ext) {
regularTemplateDefs = append(regularTemplateDefs, templatePath)
continue
}
// Add this template and continue looking for more template blocks
add(filepath.Join(basePath, templatePath))
}
return nil
}