forked from brandur/sorg
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdependency_registry.go
132 lines (103 loc) · 3.4 KB
/
dependency_registry.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
package main
import (
"bufio"
"context"
"html/template"
"io"
"os"
"regexp"
"sync"
"golang.org/x/xerrors"
"github.com/brandur/modulir"
"github.com/brandur/modulir/modules/mtemplatemd"
"github.com/brandur/sorg/modules/scommon"
)
//
// TODO: Extract types/functions below this line to something better, probably
// in Modulir.
//
// DependencyRegistry maps Go template sources to other Go template sources that
// have been included in them as dependencies. It's used to know when to trigger
// a rebuild on a file change.
type DependencyRegistry struct {
// Maps sources to their dependencies.
sources map[string][]string
sourcesMu sync.RWMutex
}
func NewDependencyRegistry() *DependencyRegistry {
return &DependencyRegistry{
sources: make(map[string][]string),
}
}
func (r *DependencyRegistry) getDependencies(source string) []string {
r.sourcesMu.RLock()
defer r.sourcesMu.RUnlock()
return r.sources[source]
}
func (r *DependencyRegistry) parseGoTemplate(baseTmpl *template.Template,
path string,
) (*template.Template, []string, error) {
templateData, err := os.ReadFile(path)
if err != nil {
return nil, nil, xerrors.Errorf("error reading template file %q: %w", path, err)
}
dependencies := []string{path}
for _, subTemplatePath := range findGoSubTemplates(string(templateData)) {
newBaseTmpl, subDependencies, err := r.parseGoTemplate(baseTmpl, subTemplatePath)
if err != nil {
return nil, nil, err
}
dependencies = append(dependencies, subDependencies...)
baseTmpl = newBaseTmpl
}
newBaseTmpl, err := baseTmpl.New(path).Funcs(scommon.HTMLTemplateFuncMap).Parse(string(templateData))
if err != nil {
return nil, nil, xerrors.Errorf("error reading parsing template %q: %w", path, err)
}
return newBaseTmpl, dependencies, nil
}
func (r *DependencyRegistry) renderGoTemplate(ctx context.Context, c *modulir.Context,
source, target string, locals map[string]interface{},
) error {
file, err := os.Create(target)
if err != nil {
return xerrors.Errorf("error creating target file: %w", err)
}
defer file.Close()
writer := bufio.NewWriter(file)
defer writer.Flush()
return r.renderGoTemplateWriter(ctx, c, source, writer, locals)
}
func (r *DependencyRegistry) renderGoTemplateWriter(ctx context.Context, c *modulir.Context,
source string, writer io.Writer, locals map[string]interface{},
) error {
ctx, includeMarkdownContainer := mtemplatemd.Context(ctx)
locals["Ctx"] = ctx
tmpl, dependencies, err := r.parseGoTemplate(template.New("base_empty"), source)
if err != nil {
return err
}
if err := tmpl.Execute(writer, locals); err != nil {
return xerrors.Errorf("error executing template: %w", err)
}
r.setDependencies(ctx, c, source, append(dependencies, includeMarkdownContainer.Dependencies...))
return nil
}
func (r *DependencyRegistry) setDependencies(_ context.Context, c *modulir.Context,
source string, dependencies []string,
) {
r.sourcesMu.Lock()
r.sources[source] = dependencies
r.sourcesMu.Unlock()
// Make sure all dependencies are watched.
c.ChangedAny(dependencies...)
}
var goFileTemplateRE = regexp.MustCompile(`\{\{\-? ?template "([^"]+\.tmpl.html)"`)
func findGoSubTemplates(templateData string) []string {
subTemplateMatches := goFileTemplateRE.FindAllStringSubmatch(templateData, -1)
subTemplateNames := make([]string, len(subTemplateMatches))
for i, match := range subTemplateMatches {
subTemplateNames[i] = match[1]
}
return subTemplateNames
}