resources: Fix the :counter placeholder

Closes #14921

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Joe Mooring
2026-05-20 17:29:13 -07:00
committed by Bjørn Erik Pedersen
parent d81e3c29c0
commit 5d51b82a7e
3 changed files with 64 additions and 6 deletions
+2 -1
View File
@@ -652,8 +652,9 @@ func (m *pageMap) getOrCreateResourcesForPage(ps *pageState) resource.Resources
sort.SliceStable(res, lessFunc)
if len(ps.m.pageConfig.ResourcesMeta) > 0 {
counters := make(map[string]int)
for i, r := range res {
res[i] = resources.CloneWithMetadataFromMapIfNeeded(ps.m.pageConfig.ResourcesMeta, r)
res[i] = resources.CloneWithMetadataFromMapIfNeeded(ps.m.pageConfig.ResourcesMeta, r, counters)
}
sort.SliceStable(res, lessFunc)
}
+58
View File
@@ -0,0 +1,58 @@
// Copyright 2026 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package hugolib
import (
"testing"
)
// Issue 14921
func TestResourceMetaCounterPlaceholder(t *testing.T) {
t.Parallel()
files := `
-- hugo.toml --
disableKinds = ['home','rss','section','sitemap','taxonomy','term']
-- content/p1/index.md --
---
title: p1
resources:
- src: "*specs.pdf"
title: "Specification #:counter"
- src: "**.pdf"
name: "pdf-file-:counter.pdf"
---
-- content/p1/checklist.pdf --
-- content/p1/guide.pdf --
-- content/p1/other_specs.pdf --
-- content/p1/photo_specs.pdf --
-- layouts/page.html --
<ul>
{{- range seq 1 4 }}
{{- with $.Resources.Get (printf "pdf-file-%d.pdf" .) }}
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
{{- end }}
{{- end }}
</ul>
`
b := Test(t, files)
b.AssertFileContent("public/p1/index.html",
`<a href="/p1/checklist.pdf">checklist.pdf</a>`,
`<a href="/p1/guide.pdf">guide.pdf</a>`,
`<a href="/p1/other_specs.pdf">Specification #1</a>`,
`<a href="/p1/photo_specs.pdf">Specification #2</a>`,
)
}
+4 -5
View File
@@ -119,7 +119,8 @@ func cloneWithMetadataFromResourceConfigIfNeeded(rc *pagemeta.ResourceConfig, r
}
// CloneWithMetadataFromMapIfNeeded clones the given resource with the given metadata if the resource supports it.
func CloneWithMetadataFromMapIfNeeded(m []map[string]any, r resource.Resource) resource.Resource {
// The counters map is shared across all resources in a bundle so that the :counter placeholder increments correctly.
func CloneWithMetadataFromMapIfNeeded(m []map[string]any, r resource.Resource, counters map[string]int) resource.Resource {
wmp, ok := r.(resource.WithResourceMetaProvider)
if !ok {
return r
@@ -131,7 +132,7 @@ func CloneWithMetadataFromMapIfNeeded(m []map[string]any, r resource.Resource) r
params: r.Params(),
}
assignMetadata(m, wrapped)
assignMetadata(m, wrapped, counters)
if !wrapped.changed {
return r
}
@@ -144,9 +145,7 @@ func CloneWithMetadataFromMapIfNeeded(m []map[string]any, r resource.Resource) r
// This assignment is additive, but the most specific match needs to be first.
// The `name` and `title` metadata field support shell-matched collection it got a match in.
// See https://golang.org/pkg/path/#Match
func assignMetadata(metadata []map[string]any, ma *metaResource) error {
counters := make(map[string]int)
func assignMetadata(metadata []map[string]any, ma *metaResource, counters map[string]int) error {
var (
nameSet, titleSet bool
nameCounter, titleCounter = 0, 0