]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Fix filename dimension identifiers (_role_X_, _version_X_) to replace mount config
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 15 Apr 2026 17:11:10 +0000 (19:11 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 15 Apr 2026 18:34:34 +0000 (20:34 +0200)
Filename identifiers for roles and versions were parsed but never applied
to the SitesMatrix. Now they replace the mount's configuration for that
dimension, matching how language identifiers already worked.

Fixes #14756

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
hugofs/component_fs.go
hugolib/sitesmatrix/dimensions.go
hugolib/sitesmatrix/sitematrix_integration_test.go
hugolib/sitesmatrix/vectorstores.go

index ec7f06a075a6f5a0c37726f34c6dfef3764c5495..e59b595c788d1772a1bee33496d20ed2c20cd30a 100644 (file)
@@ -320,8 +320,9 @@ func (fs *componentFs) applyMeta(fi FileNameIsDir, name string) (FileMetaInfo, b
 
        meta.PathInfo = pi
        if !fim.IsDir() {
+               pp := fs.opts.Cfg.PathParser()
                if fileLang := meta.PathInfo.Lang(); fileLang != "" {
-                       if idx, ok := fs.opts.Cfg.PathParser().LanguageIndex[fileLang]; ok {
+                       if idx, ok := pp.LanguageIndex[fileLang]; ok {
                                // A valid lang set in filename.
                                // Give priority to myfile.sv.txt inside the sv filesystem.
                                meta.Weight++
@@ -330,7 +331,29 @@ func (fs *componentFs) applyMeta(fi FileNameIsDir, name string) (FileMetaInfo, b
                                        // Not the default language, add some weight.
                                        meta.SitesMatrix = sitesmatrix.NewWeightedVectorStore(meta.SitesMatrix, 10)
                                }
-
+                       }
+               }
+               // Filename identifiers for roles/versions replace any mount configuration for that dimension.
+               if roles := pi.Roles(); len(roles) > 0 {
+                       var indices []int
+                       for _, role := range roles {
+                               if idx := pp.ConfiguredDimensions.ConfiguredRoles.ResolveIndex(role); idx >= 0 {
+                                       indices = append(indices, idx)
+                               }
+                       }
+                       if len(indices) > 0 {
+                               meta.SitesMatrix = meta.SitesMatrix.WithRoleIndices(indices...)
+                       }
+               }
+               if versions := pi.Versions(); len(versions) > 0 {
+                       var indices []int
+                       for _, version := range versions {
+                               if idx := pp.ConfiguredDimensions.ConfiguredVersions.ResolveIndex(version); idx >= 0 {
+                                       indices = append(indices, idx)
+                               }
+                       }
+                       if len(indices) > 0 {
+                               meta.SitesMatrix = meta.SitesMatrix.WithVersionIndices(indices...)
                        }
                }
                switch meta.Component {
@@ -341,7 +364,6 @@ func (fs *componentFs) applyMeta(fi FileNameIsDir, name string) (FileMetaInfo, b
                                meta.Weight--
                        }
                }
-
        }
 
        if fi.IsDir() {
index d7a96a038169800d4eba83842893826d396d7f10..aefa6f5e06f64c2df9c64f059aa4afe2046bc534 100644 (file)
@@ -214,7 +214,9 @@ type VectorProvider interface {
 type VectorStore interface {
        VectorProvider
        Complement(...VectorProvider) VectorStore
-       WithLanguageIndices(i int) VectorStore
+       WithLanguageIndices(i ...int) VectorStore
+       WithVersionIndices(i ...int) VectorStore
+       WithRoleIndices(i ...int) VectorStore
        HasLanguage(lang int) bool
        HasVersion(version int) bool
        HasRole(role int) bool
index 84908a1904865238cef7e0fbf30aebeb9529fa1d..e76e2b7aa006e773c80be842ff01cbd007b6ab5c 100644 (file)
@@ -1901,3 +1901,55 @@ title: p1 de
 </ul>
        `)
 }
+
+// Issue 14756.
+func TestFilenameIdentifierShouldReplaceMountDimension(t *testing.T) {
+       t.Parallel()
+       files := `
+-- hugo.toml --
+baseURL = "https://example.org/"
+disableKinds = ["taxonomy", "term", "rss", "sitemap"]
+defaultContentLanguage = "en"
+defaultContentRole = "guest"
+defaultContentRoleInSubdir = true
+[roles]
+[roles.guest]
+weight = 100
+[roles.member]
+weight = 200
+[module]
+[[module.mounts]]
+source = 'content/member'
+target = 'content'
+[module.mounts.sites.matrix]
+roles = "member"
+[[module.mounts]]
+source = 'content/guest'
+target = 'content'
+[module.mounts.sites.matrix]
+roles = "*"
+-- content/member/bundle/index.md --
+---
+title: "Bundle"
+---
+-- content/guest/bundle/index.md --
+---
+title: "Bundle"
+---
+-- content/guest/bundle/img._role_member_.txt --
+memberimg
+-- content/guest/bundle/guestimg.txt --
+guestimg
+-- layouts/page.html --
+Role: {{ .Site.Role.Name }}|{{ range .Resources }}{{ .Name }}|{{ end }}$
+-- layouts/list.html --
+List.
+`
+
+       b := hugolib.Test(t, files)
+
+       // The guest role should only see guestimg.txt (not img._role_member_.txt).
+       b.AssertFileContent("public/guest/bundle/index.html", "Role: guest|guestimg.txt|$")
+       // The member role should see both.
+       b.AssertFileContent("public/member/bundle/index.html", "Role: member|guestimg.txt|img._role_member_.txt|$")
+}
index 174dd6ea942289382629c2bf923d2a2003e7034a..b865bea65bfc19baad88530d8d2845ccbe13e95c 100644 (file)
@@ -209,17 +209,46 @@ func (s *vectorStoreMap) Vectors() []Vector {
        return vectors
 }
 
-func (s *vectorStoreMap) WithLanguageIndices(i int) VectorStore {
+func (s *vectorStoreMap) WithLanguageIndices(i ...int) VectorStore {
        c := s.clone()
 
        for v := range c.sets {
-               v[Language] = i
-               c.sets[v] = struct{}{}
+               for _, i := range i {
+                       nv := v
+                       nv[Language] = i
+                       c.sets[nv] = struct{}{}
+               }
        }
 
        return c
 }
 
+func (s *vectorStoreMap) WithVersionIndices(indices ...int) VectorStore {
+       old := s.clone()
+       c := newVectorStoreMap(len(old.sets) * len(indices))
+       for v := range old.sets {
+               for _, i := range indices {
+                       nv := v
+                       nv[Version] = i
+                       c.sets[nv] = struct{}{}
+               }
+       }
+       return c
+}
+
+func (s *vectorStoreMap) WithRoleIndices(indices ...int) VectorStore {
+       old := s.clone()
+       c := newVectorStoreMap(len(old.sets) * len(indices))
+       for v := range old.sets {
+               for _, i := range indices {
+                       nv := v
+                       nv[Role] = i
+                       c.sets[nv] = struct{}{}
+               }
+       }
+       return c
+}
+
 func (s *vectorStoreMap) HasLanguage(i int) bool {
        for v := range s.sets {
                if v.Language() == i {
@@ -589,10 +618,24 @@ func (s IntSets) shallowClone() *IntSets {
        return &s
 }
 
-// WithLanguageIndices replaces the current language set with a single language index.
-func (s *IntSets) WithLanguageIndices(i int) VectorStore {
+// WithLanguageIndices replaces the current language set with the given language indices.
+func (s *IntSets) WithLanguageIndices(indices ...int) VectorStore {
+       c := s.shallowClone()
+       c.languages = hmaps.NewOrderedIntSet(indices...)
+       return c.init()
+}
+
+// WithVersionIndices replaces the current version set with the given version indices.
+func (s *IntSets) WithVersionIndices(indices ...int) VectorStore {
+       c := s.shallowClone()
+       c.versions = hmaps.NewOrderedIntSet(indices...)
+       return c.init()
+}
+
+// WithRoleIndices replaces the current role set with the given role indices.
+func (s *IntSets) WithRoleIndices(indices ...int) VectorStore {
        c := s.shallowClone()
-       c.languages = hmaps.NewOrderedIntSet(i)
+       c.roles = hmaps.NewOrderedIntSet(indices...)
        return c.init()
 }