generate sized derivatives ahead of the request
A pass over the content at startup writes three widths per picture into a cache outside the site root, named by the source's content hash and the width (ADR-0042). Idempotent by construction: a rerun stats and skips, an edited picture takes a new name, and nothing stale can be served under an old one. Restarting the evidence site made 0 derivatives the second time, as it should. Ahead of the request rather than during it, because resampling is felt and there is no page cache yet to hide it. Outside the site root, because the engine reads that directory and must not leave generated files in somebody's content git — a lost cache costs one startup pass and no correctness. Markup now carries the original as src, the derivatives as srcset closed by the original at its own width, and width/height from the original — which retires most of the latent row about the output floor; only a gallery's alt is still empty, and a filename cannot supply that. Two things the work itself decided: `Fragment.Items` became `Fragment.Pictures`, ADR-0037's own revisit trigger. Items had one consumer, so widening it beat adding a second list beside it. "A browser can show it" and "we can resample it" are different questions, and conflating them nearly deleted content: an SVG has no decoder here, so a single predicate would have dropped SVGs from galleries silently. Undecodable and unsupported pictures are now rendered as they are, without a size or a srcset.
This commit is contained in:
@@ -38,7 +38,7 @@ func assetHandler(t *testing.T) http.Handler {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return Handler(content.NewSite(bundles), r, fsys, content.Settings{})
|
||||
return Handler(content.NewSite(bundles), r, fsys, nil, content.Settings{})
|
||||
}
|
||||
|
||||
func TestABundlesOwnFilesAreServed(t *testing.T) {
|
||||
|
||||
@@ -29,7 +29,7 @@ func crawlerHandler(t *testing.T, settings content.Settings, extra fstest.MapFS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return Handler(content.NewSite(bundles), r, fsys, settings)
|
||||
return Handler(content.NewSite(bundles), r, fsys, nil, settings)
|
||||
}
|
||||
|
||||
func TestSitemapListsEveryVariantAbsolutely(t *testing.T) {
|
||||
|
||||
@@ -21,7 +21,7 @@ func slugHandler(t *testing.T, fsys fstest.MapFS) http.Handler {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return Handler(content.NewSite(bundles), r, fsys, content.Settings{})
|
||||
return Handler(content.NewSite(bundles), r, fsys, nil, content.Settings{})
|
||||
}
|
||||
|
||||
func TestASlugRenamesTheAddressInEveryLanguage(t *testing.T) {
|
||||
@@ -82,7 +82,7 @@ func TestListingsAndSitemapsUseTheSluggedAddress(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := Handler(content.NewSite(bundles), r, fsys, settings)
|
||||
h := Handler(content.NewSite(bundles), r, fsys, nil, settings)
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/posts/", nil))
|
||||
|
||||
+7
-1
@@ -15,7 +15,7 @@ import (
|
||||
// Handler serves a site.
|
||||
//
|
||||
// One mux entry, because URL shape is the resolver's business rather than the mux's: see resolve.
|
||||
func Handler(site *content.Site, r *render.Renderer, siteFS fs.FS, settings content.Settings) http.Handler {
|
||||
func Handler(site *content.Site, r *render.Renderer, siteFS, derivedFS fs.FS, settings content.Settings) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("GET /", func(w http.ResponseWriter, req *http.Request) {
|
||||
serve(w, req, site, r, siteFS)
|
||||
@@ -33,6 +33,12 @@ func Handler(site *content.Site, r *render.Renderer, siteFS fs.FS, settings cont
|
||||
mux.Handle("GET /static/", http.StripPrefix("/static/", serveStatic(sub)))
|
||||
}
|
||||
}
|
||||
// Generated files, served as opaque names. Core knows only that a directory of them exists: which files
|
||||
// are there, and what they are derived from, is the feature's business (ADR-0042).
|
||||
if derivedFS != nil {
|
||||
mux.Handle("GET "+content.DerivedPrefix,
|
||||
http.StripPrefix(content.DerivedPrefix, serveStatic(derivedFS)))
|
||||
}
|
||||
return mux
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ func testHandler(t *testing.T) http.Handler {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return Handler(content.NewSite(bundles), r, fsys, content.Settings{})
|
||||
return Handler(content.NewSite(bundles), r, fsys, nil, content.Settings{})
|
||||
}
|
||||
|
||||
func TestServeBundleAtItsPermalink(t *testing.T) {
|
||||
@@ -76,7 +76,7 @@ func multilingualHandler(t *testing.T) http.Handler {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return Handler(content.NewSite(bundles), r, fsys, content.Settings{})
|
||||
return Handler(content.NewSite(bundles), r, fsys, nil, content.Settings{})
|
||||
}
|
||||
|
||||
func TestPrefixedLanguageServesThatVariant(t *testing.T) {
|
||||
@@ -128,7 +128,7 @@ func aliasHandler(t *testing.T) http.Handler {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return Handler(content.NewSite(bundles), r, fsys, content.Settings{})
|
||||
return Handler(content.NewSite(bundles), r, fsys, nil, content.Settings{})
|
||||
}
|
||||
|
||||
func TestAliasRedirectsToCanonical(t *testing.T) {
|
||||
@@ -175,7 +175,7 @@ func listingHandler(t *testing.T, n int) http.Handler {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return Handler(content.NewSite(bundles), r, fsys, content.Settings{})
|
||||
return Handler(content.NewSite(bundles), r, fsys, nil, content.Settings{})
|
||||
}
|
||||
|
||||
func TestSectionIndexListsNewestFirst(t *testing.T) {
|
||||
@@ -243,7 +243,7 @@ func TestStaticFilesAreServedAndDirectoriesAreNot(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := Handler(content.NewSite(bundles), r, fsys, content.Settings{})
|
||||
h := Handler(content.NewSite(bundles), r, fsys, nil, content.Settings{})
|
||||
for path, want := range map[string]int{
|
||||
"/static/style.css": http.StatusOK,
|
||||
"/static/img/logo.svg": http.StatusOK,
|
||||
@@ -284,7 +284,7 @@ func TestAStaticPathThatEscapesTheRootIs404(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := Handler(content.NewSite(nil), r, fsys, content.Settings{})
|
||||
h := Handler(content.NewSite(nil), r, fsys, nil, content.Settings{})
|
||||
for path, want := range map[string]int{
|
||||
"/static/ok.css": http.StatusOK,
|
||||
"/static/escape.txt": http.StatusNotFound,
|
||||
@@ -317,7 +317,7 @@ func seriesHandler(t *testing.T) http.Handler {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return Handler(content.NewSite(bundles), r, nil, content.Settings{})
|
||||
return Handler(content.NewSite(bundles), r, nil, nil, content.Settings{})
|
||||
}
|
||||
|
||||
func TestSequenceNavigationLinksNeighbours(t *testing.T) {
|
||||
@@ -407,7 +407,7 @@ func tagHandler(t *testing.T) http.Handler {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return Handler(content.NewSite(bundles), r, nil, content.Settings{})
|
||||
return Handler(content.NewSite(bundles), r, nil, nil, content.Settings{})
|
||||
}
|
||||
|
||||
func TestGlobalTagListingSpansSectionsGroupedByOne(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user