Commit 0a4bb99d63

0a4bb99d63653021f509101d804ddcf516f75722

parent: 7effe3feb4

Verified · cmc ci/build: success ci/test: success ci/vuln: success

cmc <hello@cleberg.net> · 2026-09-04 01:37 UTC

web: parse every page template once, at start-up

Render parsed the layout and the page on every request, and a template
that did not parse failed the first visit to its page rather than the
process. All page templates parse with the layout at package init;
Render looks the page up. TestEveryPageTemplateParses asserts the set.

Closes #116

Layout: unified · split

internal/web/web.go +37 −5
@@ -4,10 +4,13 @@ package web
44
55import (
66 "embed"
7 "fmt"
78 "html/template"
89 "io"
10 "io/fs"
911 "reflect"
1012 "runtime/debug"
13 "sort"
1114 "strconv"
1215 "strings"
1316 "sync"
@@ -225,13 +228,42 @@ var funcs = template.FuncMap{
225228 },
226229}
227230
231// pages holds every page template parsed once with the layout, at
232// start-up: a template that does not parse fails the process before it
233// serves anything, rather than the first visit to a rarely-hit page, and
234// a request no longer re-parses the layout (#116).
235var pages = func() map[string]*template.Template {
236 entries, err := fs.ReadDir(templateFS, "templates")
237 if err != nil {
238 panic(err)
239 }
240 m := map[string]*template.Template{}
241 for _, e := range entries {
242 name := e.Name()
243 if name == "layout.html" || !strings.HasSuffix(name, ".html") {
244 continue
245 }
246 layout := template.Must(template.New("layout.html").Funcs(funcs).ParseFS(templateFS, "templates/layout.html"))
247 m[name] = template.Must(layout.ParseFS(templateFS, "templates/"+name))
248 }
249 return m
250}()
251
252// Pages lists the page template names, for tests that render each one.
253func Pages() []string {
254 names := make([]string, 0, len(pages))
255 for name := range pages {
256 names = append(names, name)
257 }
258 sort.Strings(names)
259 return names
260}
261
228262// Render executes the named page template with the shared layout.
229263func Render(w io.Writer, page string, data any) error {
230 t, err := template.Must(
231 template.New("layout.html").Funcs(funcs).ParseFS(templateFS, "templates/layout.html"),
232 ).ParseFS(templateFS, "templates/"+page)
233 if err != nil {
234 return err
264 t, ok := pages[page]
265 if !ok {
266 return fmt.Errorf("no page template %q", page)
235267 }
236268 return t.ExecuteTemplate(w, "layout", data)
237269}
internal/web/web_test.go added +19
@@ -0,0 +1,19 @@
1package web
2
3import "testing"
4
5// Every page template parses with the layout at start-up, so a broken
6// template fails the process rather than the first visit to its page.
7// Package init has already done the work; this asserts it covered every
8// file (#116).
9func TestEveryPageTemplateParses(t *testing.T) {
10 names := Pages()
11 if len(names) < 30 {
12 t.Fatalf("only %d page templates parsed: %v", len(names), names)
13 }
14 for _, want := range []string{"tree.html", "blob.html", "mr.html", "issue.html", "dashboard.html", "registered.html"} {
15 if _, ok := pages[want]; !ok {
16 t.Errorf("%s not parsed", want)
17 }
18 }
19}