forked from mirrors/pkg-proxy
Templates are parsed on first Render call instead of at server startup. API-only traffic never pays the ~780µs parsing cost. Closes #53
80 lines
1.7 KiB
Go
80 lines
1.7 KiB
Go
package server
|
|
|
|
import (
|
|
"embed"
|
|
"html/template"
|
|
"net/http"
|
|
"path/filepath"
|
|
"sync"
|
|
)
|
|
|
|
//go:embed templates/**/*.html
|
|
var templatesFS embed.FS
|
|
|
|
// Templates holds lazily-parsed templates for each page.
|
|
type Templates struct {
|
|
once sync.Once
|
|
pages map[string]*template.Template
|
|
err error
|
|
}
|
|
|
|
// load parses all templates from the embedded filesystem on first call.
|
|
func (t *Templates) load() error {
|
|
t.once.Do(func() {
|
|
pages := make(map[string]*template.Template)
|
|
|
|
funcMap := template.FuncMap{
|
|
"add": func(a, b int) int { return a + b },
|
|
"sub": func(a, b int) int { return a - b },
|
|
"supportedEcosystems": supportedEcosystems,
|
|
"ecosystemBadgeClass": ecosystemBadgeClasses,
|
|
"ecosystemBadgeLabel": ecosystemBadgeLabel,
|
|
}
|
|
|
|
pageFiles, err := templatesFS.ReadDir("templates/pages")
|
|
if err != nil {
|
|
t.err = err
|
|
return
|
|
}
|
|
|
|
for _, pageFile := range pageFiles {
|
|
if pageFile.IsDir() {
|
|
continue
|
|
}
|
|
|
|
pageName := pageFile.Name()
|
|
pageName = pageName[:len(pageName)-len(filepath.Ext(pageName))]
|
|
|
|
tmpl, err := template.New("").Funcs(funcMap).ParseFS(templatesFS,
|
|
"templates/layout/*.html",
|
|
"templates/components/*.html",
|
|
"templates/pages/"+pageFile.Name(),
|
|
)
|
|
if err != nil {
|
|
t.err = err
|
|
return
|
|
}
|
|
|
|
pages[pageName] = tmpl
|
|
}
|
|
|
|
t.pages = pages
|
|
})
|
|
return t.err
|
|
}
|
|
|
|
// Render renders a page template with the given data.
|
|
func (t *Templates) Render(w http.ResponseWriter, pageName string, data any) error {
|
|
if err := t.load(); err != nil {
|
|
return err
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
|
|
|
tmpl, ok := t.pages[pageName]
|
|
if !ok {
|
|
return http.ErrAbortHandler
|
|
}
|
|
|
|
return tmpl.ExecuteTemplate(w, "base", data)
|
|
}
|