let a site root override the theme, and serve static/

A site's templates/ is parsed after the embedded set, so the last definition of a
name wins and a theme redefines one block while inheriting the document
(ADR-0019). Per kind exactly two files are overlaid — base.html and that kind's
block — because overlaying every site template into every set lets a listing's
"main" leak into bundle pages, which is the collision per-kind sets exist to
prevent. Both directions are tested.

templates/theme.css in the site root replaces the reference stylesheet outright;
there is no merging to reason about. static/ is served verbatim under /static/,
through the same os.Root-backed fs.FS, and directory paths answer 404 so it never
indexes its own contents.

Queue entries 6 and 7 are deferred again with triggers: the cascade's consumers are
stage toggles, view selection and cache flags — none of which exist — and declared
types are read by ordering, feeds and check, none of which have landed. Building
either now is the speculation that justified withdrawing them.

Evidence: a real site override renders <section class="mine"> inside the embedded
document with canonical intact, the listing still uses the embedded block, and
/static/site.css and /static/img/logo.svg are 200 while /static/ and /static/img/
are 404.
This commit is contained in:
2026-08-01 02:23:34 +06:00
parent 60a7e10aee
commit 61af326474
7 changed files with 204 additions and 29 deletions
+38 -8
View File
@@ -22,11 +22,11 @@ func testHandler(t *testing.T) http.Handler {
if err != nil {
t.Fatal(err)
}
r, err := render.New()
r, err := render.New(nil)
if err != nil {
t.Fatal(err)
}
return Handler(content.NewSite(bundles), r)
return Handler(content.NewSite(bundles), r, fsys)
}
func TestServeBundleAtItsPermalink(t *testing.T) {
@@ -82,11 +82,11 @@ func multilingualHandler(t *testing.T) http.Handler {
if err != nil {
t.Fatal(err)
}
r, err := render.New()
r, err := render.New(nil)
if err != nil {
t.Fatal(err)
}
return Handler(content.NewSite(bundles), r)
return Handler(content.NewSite(bundles), r, fsys)
}
func TestPrefixedLanguageServesThatVariant(t *testing.T) {
@@ -173,11 +173,11 @@ func aliasHandler(t *testing.T) http.Handler {
if err != nil {
t.Fatal(err)
}
r, err := render.New()
r, err := render.New(nil)
if err != nil {
t.Fatal(err)
}
return Handler(content.NewSite(bundles), r)
return Handler(content.NewSite(bundles), r, fsys)
}
func TestAliasRedirectsToCanonical(t *testing.T) {
@@ -220,11 +220,11 @@ func listingHandler(t *testing.T, n int) http.Handler {
if err != nil {
t.Fatal(err)
}
r, err := render.New()
r, err := render.New(nil)
if err != nil {
t.Fatal(err)
}
return Handler(content.NewSite(bundles), r)
return Handler(content.NewSite(bundles), r, fsys)
}
func TestSectionIndexListsNewestFirst(t *testing.T) {
@@ -289,3 +289,33 @@ func TestPagePastTheEndIs404(t *testing.T) {
t.Errorf("got %d, want 404: an empty page is a URL that means nothing", rec.Code)
}
}
func TestStaticFilesAreServedAndDirectoriesAreNot(t *testing.T) {
fsys := fstest.MapFS{
"content/pages/about.md": {Data: []byte("---\ntitle: About\n---\nx\n")},
"static/style.css": {Data: []byte("body{}")},
"static/img/logo.svg": {Data: []byte("<svg/>")},
}
bundles, err := content.Scan(fsys)
if err != nil {
t.Fatal(err)
}
r, err := render.New(fsys)
if err != nil {
t.Fatal(err)
}
h := Handler(content.NewSite(bundles), r, fsys)
for path, want := range map[string]int{
"/static/style.css": http.StatusOK,
"/static/img/logo.svg": http.StatusOK,
"/static/": http.StatusNotFound,
"/static/img/": http.StatusNotFound,
"/static/nope.css": http.StatusNotFound,
} {
rec := httptest.NewRecorder()
h.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, path, nil))
if rec.Code != want {
t.Errorf("GET %s = %d, want %d", path, rec.Code, want)
}
}
}