···
-
t map[string]*template.Template
avatar config.AvatarConfig
resolver *idresolver.Resolver
···
-
t: make(map[string]*template.Template),
···
logger: slog.Default().With("component", "pages"),
-
// Initial load of all templates
func (p *Pages) fragmentPaths() ([]string, error) {
var fragmentPaths []string
err := fs.WalkDir(p.embedFS, "templates", func(path string, d fs.DirEntry, err error) error {
···
return fragmentPaths, nil
-
func (p *Pages) loadAllTemplates() {
-
p.embedFS = os.DirFS(p.templateDir)
-
l := p.logger.With("handler", "loadAllTemplates")
-
templates := make(map[string]*template.Template)
fragmentPaths, err := p.fragmentPaths()
-
l.Error("failed to collect fragments", "err", err)
// parse all fragments together
-
allFragments := template.New("").Funcs(p.funcMap())
for _, f := range fragmentPaths {
-
name := strings.TrimPrefix(f, "templates/")
-
name = strings.TrimSuffix(name, ".html")
-
pf, err := template.New(name).Funcs(p.funcMap()).ParseFS(p.embedFS, f)
-
l.Error("failed to parse fragment", "name", name, "path", f)
allFragments, err = allFragments.AddParseTree(name, pf.Tree)
-
l.Error("failed to add parse tree", "name", name, "path", f)
-
templates[name] = allFragments.Lookup(name)
-
// Then walk through and setup the rest of the templates
-
err = fs.WalkDir(p.embedFS, "templates", func(path string, d fs.DirEntry, err error) error {
-
if !strings.HasSuffix(path, "html") {
-
// Skip fragments as they've already been loaded
-
if strings.Contains(path, "fragments/") {
-
if strings.Contains(path, "layouts/") {
-
name := strings.TrimPrefix(path, "templates/")
-
name = strings.TrimSuffix(name, ".html")
-
// Add the page template on top of the base
-
allPaths = append(allPaths, "templates/layouts/*.html")
-
allPaths = append(allPaths, fragmentPaths...)
-
allPaths = append(allPaths, path)
-
tmpl, err := template.New(name).
-
ParseFS(p.embedFS, allPaths...)
-
return fmt.Errorf("setting up template: %w", err)
-
l.Debug("loaded all templates")
-
l.Error("walking template dir", "err", err)
-
l.Info("loaded all templates", "total", len(templates))
-
func (p *Pages) executeOrReload(templateName string, w io.Writer, base string, params any) error {
-
// In dev mode, reparse templates from disk before executing
-
tmpl, exists := p.t[templateName]
-
return fmt.Errorf("template not found: %s", templateName)
-
return tmpl.Execute(w, params)
-
return tmpl.ExecuteTemplate(w, base, params)
-
func (p *Pages) execute(name string, w io.Writer, params any) error {
-
return p.executeOrReload(name, w, "layouts/base", params)
func (p *Pages) executePlain(name string, w io.Writer, params any) error {
-
return p.executeOrReload(name, w, "", params)
func (p *Pages) executeRepo(name string, w io.Writer, params any) error {
-
return p.executeOrReload(name, w, "layouts/repobase", params)
func (p *Pages) Favicon(w io.Writer) error {
···
+
cache *TmplCache[string, *template.Template]
avatar config.AvatarConfig
resolver *idresolver.Resolver
···
+
cache: NewTmplCache[string, *template.Template](),
···
logger: slog.Default().With("component", "pages"),
+
p.embedFS = os.DirFS(p.templateDir)
+
func (p *Pages) pathToName(s string) string {
+
return strings.TrimSuffix(strings.TrimPrefix(s, "templates/"), ".html")
+
// reverse of pathToName
+
func (p *Pages) nameToPath(s string) string {
+
return "templates/" + s + ".html"
func (p *Pages) fragmentPaths() ([]string, error) {
var fragmentPaths []string
err := fs.WalkDir(p.embedFS, "templates", func(path string, d fs.DirEntry, err error) error {
···
return fragmentPaths, nil
+
func (p *Pages) fragments() (*template.Template, error) {
fragmentPaths, err := p.fragmentPaths()
// parse all fragments together
+
allFragments := template.New("").Funcs(funcs)
for _, f := range fragmentPaths {
+
name := p.pathToName(f)
+
pf, err := template.New(name).
allFragments, err = allFragments.AddParseTree(name, pf.Tree)
+
return allFragments, nil
+
// parse without memoization
+
func (p *Pages) rawParse(stack ...string) (*template.Template, error) {
+
paths, err := p.fragmentPaths()
+
for _, s := range stack {
+
paths = append(paths, p.nameToPath(s))
+
top := stack[len(stack)-1]
+
parsed, err := template.New(top).
+
ParseFS(p.embedFS, paths...)
+
func (p *Pages) parse(stack ...string) (*template.Template, error) {
+
key := strings.Join(stack, "|")
+
// never cache in dev mode
+
if cached, exists := p.cache.Get(key); !p.dev && exists {
+
result, err := p.rawParse(stack...)
+
p.cache.Set(key, result)
+
func (p *Pages) parseBase(top string) (*template.Template, error) {
+
return p.parse(stack...)
+
func (p *Pages) parseRepoBase(top string) (*template.Template, error) {
+
return p.parse(stack...)
func (p *Pages) executePlain(name string, w io.Writer, params any) error {
+
tpl, err := p.parse(name)
+
return tpl.Execute(w, params)
+
func (p *Pages) execute(name string, w io.Writer, params any) error {
+
tpl, err := p.parseBase(name)
+
return tpl.ExecuteTemplate(w, "layouts/base", params)
func (p *Pages) executeRepo(name string, w io.Writer, params any) error {
+
tpl, err := p.parseRepoBase(name)
+
return tpl.ExecuteTemplate(w, "layouts/base", params)
func (p *Pages) Favicon(w io.Writer) error {