all repos — website @ 7625cb131a85d4f3c45683251702fc0f0ff3f8c2

My website

template: open files only once
Alan Pearce alan@alanpearce.eu
Wed, 24 Apr 2024 15:17:51 +0200
commit

7625cb131a85d4f3c45683251702fc0f0ff3f8c2

parent

2ef8e8286acfc06c4d7680d299e47dedea7b35eb

1 files changed, 31 insertions(+), 11 deletions(-)

jump to
M cmd/build/template.gocmd/build/template.go
@@ -8,6 +8,7 @@ "log/slog" 	"net/url"
 	"os"
 	"strings"
+	"sync"
 	"time"
 	"website/internal/atom"
 	"website/internal/config"
@@ -20,16 +21,39 @@ mapset "github.com/deckarep/golang-set/v2" 	"golang.org/x/net/html"
 )
 
+var (
+	assetsOnce sync.Once
+	css        string
+	templates  map[string]*os.File = make(map[string]*os.File)
+)
+
+func loadTemplate(path string) (file *os.File, err error) {
+	if templates[path] == nil {
+		file, err = os.Open(path)
+		if err != nil {
+			return nil, err
+		}
+		templates[path] = file
+	}
+	file = templates[path]
+	return
+}
+
 func layout(filename string, config config.Config, pageTitle string) (*goquery.Document, error) {
-	html, err := os.Open(filename)
+	html, err := loadTemplate(filename)
 	if err != nil {
 		return nil, err
 	}
-	defer html.Close()
-	css, err := os.ReadFile("templates/style.css")
+	defer html.Seek(0, io.SeekStart)
+	assetsOnce.Do(func() {
+		var bytes []byte
+		bytes, err = os.ReadFile("templates/style.css")
+		css = string(bytes)
+	})
 	if err != nil {
 		return nil, err
 	}
+
 	doc, err := goquery.NewDocumentFromReader(html)
 	if err != nil {
 		return nil, err
@@ -178,11 +202,11 @@ return renderHTML(doc), nil }
 
 func renderFeed(title string, config config.Config, posts []Post, specific string) (io.Reader, error) {
-	reader, err := os.Open("templates/feed.xml")
+	reader, err := loadTemplate("templates/feed.xml")
 	if err != nil {
 		return nil, err
 	}
-	defer reader.Close()
+	defer reader.Seek(0, io.SeekStart)
 	doc, err := xmlquery.Parse(reader)
 	feed := doc.SelectElement("feed")
 	feed.SelectElement("title").FirstChild.Data = title
@@ -230,11 +254,11 @@ return strings.NewReader(doc.OutputXML(true)), nil }
 
 func renderFeedStyles() (io.Reader, error) {
-	reader, err := os.Open("templates/feed-styles.xsl")
+	reader, err := loadTemplate("templates/feed-styles.xsl")
 	if err != nil {
 		return nil, err
 	}
-	defer reader.Close()
+	defer reader.Seek(0, io.SeekStart)
 	nsMap := map[string]string{
 		"xsl":   "http://www.w3.org/1999/XSL/Transform",
 		"atom":  "http://www.w3.org/2005/Atom",
@@ -246,10 +270,6 @@ if err != nil { 		return nil, err
 	}
 	style := xmlquery.QuerySelector(doc, expr)
-	css, err := os.ReadFile("templates/style.css")
-	if err != nil {
-		return nil, err
-	}
 	xmlquery.AddChild(style, &xmlquery.Node{
 		Type: xmlquery.TextNode,
 		Data: string(css),