diff --git a/hugolib/page__content.go b/hugolib/page__content.go
index b5527a281..f7579f182 100644
--- a/hugolib/page__content.go
+++ b/hugolib/page__content.go
@@ -730,16 +730,15 @@ func (c *cachedContentScope) contentToC(ctx context.Context) (contentTableOfCont
isHTML := cp.po.p.m.pageConfig.ContentMediaType.IsHTML()
if !isHTML {
- createAndSetToC := func(tocProvider converter.TableOfContentsProvider) {
+ createAndSetToC := func(tocProvider converter.TableOfContentsProvider) error {
cfg := p.s.ContentSpec.Converters.GetMarkupConfig()
ct.tableOfContents = tocProvider.TableOfContents()
- ct.tableOfContentsHTML = template.HTML(
- ct.tableOfContents.ToHTML(
- cfg.TableOfContents.StartLevel,
- cfg.TableOfContents.EndLevel,
- cfg.TableOfContents.Ordered,
- ),
+ ct.tableOfContentsHTML, err = ct.tableOfContents.ToHTML(
+ cfg.TableOfContents.StartLevel,
+ cfg.TableOfContents.EndLevel,
+ cfg.TableOfContents.Ordered,
)
+ return err
}
// If the converter supports doing the parsing separately, we do that.
diff --git a/markup/goldmark/convert_test.go b/markup/goldmark/convert_test.go
index 6048bce39..a35441aff 100644
--- a/markup/goldmark/convert_test.go
+++ b/markup/goldmark/convert_test.go
@@ -208,8 +208,8 @@ unsafe = true
toc, ok := b.(converter.TableOfContentsProvider)
c.Assert(ok, qt.Equals, true)
- tocString := string(toc.TableOfContents().ToHTML(1, 2, false))
- c.Assert(tocString, qt.Contains, "TableOfContents")
+ tocHTML, _ := toc.TableOfContents().ToHTML(1, 2, false)
+ c.Assert(string(tocHTML), qt.Contains, "TableOfContents")
}
func TestConvertAutoIDAsciiOnly(t *testing.T) {
diff --git a/markup/tableofcontents/tableofcontents.go b/markup/tableofcontents/tableofcontents.go
index 49a9cdeb7..560e421b7 100644
--- a/markup/tableofcontents/tableofcontents.go
+++ b/markup/tableofcontents/tableofcontents.go
@@ -14,11 +14,13 @@
package tableofcontents
import (
+ "fmt"
"html/template"
"sort"
"strings"
"github.com/gohugoio/hugo/common/collections"
+ "github.com/spf13/cast"
)
// Empty is an empty ToC.
@@ -133,19 +135,30 @@ func (toc *Fragments) addAt(h *Heading, row, level int) {
}
// ToHTML renders the ToC as HTML.
-func (toc *Fragments) ToHTML(startLevel, stopLevel int, ordered bool) template.HTML {
+func (toc *Fragments) ToHTML(startLevel, stopLevel any, ordered bool) (template.HTML, error) {
if toc == nil {
- return ""
+ return "", nil
}
+
+ iStartLevel, err := cast.ToIntE(startLevel)
+ if err != nil {
+ return "", fmt.Errorf("startLevel: %w", err)
+ }
+
+ iStopLevel, err := cast.ToIntE(stopLevel)
+ if err != nil {
+ return "", fmt.Errorf("stopLevel: %w", err)
+ }
+
b := &tocBuilder{
s: strings.Builder{},
h: toc.Headings,
- startLevel: startLevel,
- stopLevel: stopLevel,
+ startLevel: iStartLevel,
+ stopLevel: iStopLevel,
ordered: ordered,
}
b.Build()
- return template.HTML(b.s.String())
+ return template.HTML(b.s.String()), nil
}
func (toc Fragments) walk(fn func(*Heading)) {
diff --git a/markup/tableofcontents/tableofcontents_integration_test.go b/markup/tableofcontents/tableofcontents_integration_test.go
index 87a7c0108..e6ae03ce2 100644
--- a/markup/tableofcontents/tableofcontents_integration_test.go
+++ b/markup/tableofcontents/tableofcontents_integration_test.go
@@ -14,6 +14,7 @@
package tableofcontents_test
import (
+ "strings"
"testing"
"github.com/gohugoio/hugo/hugolib"
@@ -43,3 +44,80 @@ disableKinds = ['page','rss','section','sitemap','taxonomy','term']
"heading-l5|5|Heading L5",
)
}
+
+// Issue #13107
+func TestToHTMLArgTypes(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- hugo.toml --
+disableKinds = ['home','section','rss','sitemap','taxonomy','term']
+-- layouts/_default/single.html --
+{{ .Fragments.ToHTML .Params.toc.startLevel .Params.toc.endLevel false }}
+-- content/json.md --
+{
+ "title": "json",
+ "params": {
+ "toc": {
+ "startLevel": 2,
+ "endLevel": 4
+ }
+ }
+}
+CONTENT
+-- content/toml.md --
++++
+title = 'toml'
+[params.toc]
+startLevel = 2
+endLevel = 4
++++
+CONTENT
+-- content/yaml.md --
+---
+title: yaml
+params:
+ toc:
+ startLevel: 2
+ endLevel: 4
+---
+CONTENT
+`
+
+ content := `
+# Level One
+## Level Two
+### Level Three
+#### Level Four
+##### Level Five
+###### Level Six
+ `
+
+ want := `
+
+`
+
+ files = strings.ReplaceAll(files, "CONTENT", content)
+
+ b := hugolib.Test(t, files)
+ b.AssertFileContentEquals("public/json/index.html", strings.TrimSpace(want))
+ b.AssertFileContentEquals("public/toml/index.html", strings.TrimSpace(want))
+ b.AssertFileContentEquals("public/yaml/index.html", strings.TrimSpace(want))
+
+ files = strings.ReplaceAll(files, `2`, `"x"`)
+
+ b, _ = hugolib.TestE(t, files)
+ b.AssertLogMatches(`error calling ToHTML: startLevel: unable to cast "x" of type string`)
+}
diff --git a/markup/tableofcontents/tableofcontents_test.go b/markup/tableofcontents/tableofcontents_test.go
index 3af9c4eb6..9ec7ec293 100644
--- a/markup/tableofcontents/tableofcontents_test.go
+++ b/markup/tableofcontents/tableofcontents_test.go
@@ -45,7 +45,8 @@ func TestToc(t *testing.T) {
toc.addAt(&Heading{Title: "1-H3-1", ID: "1-h2-2"}, 0, 2)
toc.addAt(&Heading{Title: "Heading 2", ID: "h1-2"}, 1, 0)
- got := string(toc.ToHTML(1, -1, false))
+ tocHTML, _ := toc.ToHTML(1, -1, false)
+ got := string(tocHTML)
c.Assert(got, qt.Equals, ``, qt.Commentf(got))
- got = string(toc.ToHTML(1, 1, false))
+ tocHTML, _ = toc.ToHTML(1, 1, false)
+ got = string(tocHTML)
c.Assert(got, qt.Equals, ``, qt.Commentf(got))
- got = string(toc.ToHTML(1, 2, false))
+ tocHTML, _ = toc.ToHTML(1, 2, false)
+ got = string(tocHTML)
c.Assert(got, qt.Equals, ``, qt.Commentf(got))
- got = string(toc.ToHTML(2, 2, false))
+ tocHTML, _ = toc.ToHTML(2, 2, false)
+ got = string(tocHTML)
c.Assert(got, qt.Equals, ``, qt.Commentf(got))
- got = string(toc.ToHTML(1, -1, true))
+ tocHTML, _ = toc.ToHTML(1, -1, true)
+ got = string(tocHTML)
c.Assert(got, qt.Equals, `