M README.md => README.md +1 -0
@@ 76,6 76,7 @@ copies of the same code.
```go
svc := chrome.NewService(conf, "compare.sr.ht")
svc.StyleHref = cssHref // after discovering the hashed stylesheet
+svc.Assets = map[string]string{"bundle.js": bundleHref} // any further artefacts
t := chrome.MustAttach(template.New("layout").Funcs(chrome.Funcs()))
// ... parse the service's own templates into t ...
M chrome/chrome.go => chrome/chrome.go +18 -0
@@ 135,6 135,20 @@ type Page struct {
StyleHref string // "" when the binary was built without a stylesheet
+ // Assets are the hashed hrefs of the extra build artefacts a layout links
+ // beyond the stylesheet — a vendored chart library, a front-end bundle —
+ // keyed by names the service picks. Read as {{index .Assets "uplot.js"}},
+ // guarded on emptiness exactly like StyleHref.
+ //
+ // They belong to the chrome for the reason StyleHref does: the hash in the
+ // name is a property of this binary, not of any page. A page that had to
+ // be handed its own asset URLs is a page that can be written without them
+ // and silently render nothing where the chart was.
+ //
+ // The map is the Service's, shared by every Page it builds: written once
+ // at startup, read-only afterwards. A handler must not write to it.
+ Assets map[string]string
+
Environment string // uppercased; banner text
ShowBanner bool // true outside production
@@ 175,6 189,9 @@ type Service struct {
// ExtraNav holds service-specific switcher entries (e.g. a /tokens link),
// rendered after the shared network entries, for authenticated viewers.
ExtraNav []NavItem
+ // Assets holds the extra hashed asset hrefs every Page carries; see
+ // Page.Assets. Populate it at startup, next to StyleHref.
+ Assets map[string]string
siteName string
environment string
@@ 250,6 267,7 @@ func (s *Service) Page(r *http.Request, title, username string) Page {
SelfOrigin: s.selfOrigin,
HubOrigin: s.hubOrigin,
StyleHref: s.StyleHref,
+ Assets: s.Assets,
Environment: strings.ToUpper(s.environment),
ShowBanner: s.environment != "" && s.environment != "production",
ContainerClass: "container",
M chrome/chrome_test.go => chrome/chrome_test.go +16 -0
@@ 201,6 201,22 @@ func TestLoginURLForMatchesTheNav(t *testing.T) {
"https%3A%2F%2Fcompare.example%2F~alice%2Fdemo%3Fa%3D1", svc.LoginURLFor(r))
}
+// TestPageCarriesTheServiceAssets pins the slot three services grew their own
+// copy of: the layout reads an extra hashed artefact off the chrome, not off
+// the page's payload.
+func TestPageCarriesTheServiceAssets(t *testing.T) {
+ svc := NewService(testConf(), "bench.sr.ht")
+ svc.Assets = map[string]string{"uplot.js": "/static/uplot.0badc0de.js"}
+ p := svc.Page(httptest.NewRequest("GET", "/", nil), "t", "alice")
+
+ assert.Equal(t, "/static/uplot.0badc0de.js", p.Assets["uplot.js"])
+ // A name the service never registered reads as empty, which is what the
+ // layout's {{if}} guard is written against.
+ assert.Empty(t, p.Assets["missing.js"])
+ assert.Empty(t, NewService(testConf(), "bench.sr.ht").
+ Page(httptest.NewRequest("GET", "/", nil), "t", "alice").Assets)
+}
+
func TestServiceAccessors(t *testing.T) {
svc := NewService(testConf(), "compare.sr.ht")
assert.Equal(t, "https://compare.example", svc.SelfOrigin())