From: Bjørn Erik Pedersen Date: Wed, 15 Apr 2026 17:11:10 +0000 (+0200) Subject: Fix filename dimension identifiers (_role_X_, _version_X_) to replace mount config X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=8d6145f3c31528ecafca4e5f301abd1c672468fd;p=brevno-suite%2Fhugo Fix filename dimension identifiers (_role_X_, _version_X_) to replace mount config 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) --- diff --git a/hugofs/component_fs.go b/hugofs/component_fs.go index ec7f06a07..e59b595c7 100644 --- a/hugofs/component_fs.go +++ b/hugofs/component_fs.go @@ -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() { diff --git a/hugolib/sitesmatrix/dimensions.go b/hugolib/sitesmatrix/dimensions.go index d7a96a038..aefa6f5e0 100644 --- a/hugolib/sitesmatrix/dimensions.go +++ b/hugolib/sitesmatrix/dimensions.go @@ -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 diff --git a/hugolib/sitesmatrix/sitematrix_integration_test.go b/hugolib/sitesmatrix/sitematrix_integration_test.go index 84908a190..e76e2b7aa 100644 --- a/hugolib/sitesmatrix/sitematrix_integration_test.go +++ b/hugolib/sitesmatrix/sitematrix_integration_test.go @@ -1901,3 +1901,55 @@ title: p1 de `) } + +// 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|$") +} diff --git a/hugolib/sitesmatrix/vectorstores.go b/hugolib/sitesmatrix/vectorstores.go index 174dd6ea9..b865bea65 100644 --- a/hugolib/sitesmatrix/vectorstores.go +++ b/hugolib/sitesmatrix/vectorstores.go @@ -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() }