Repository: gohugoio/hugo (language: go)

Issue:
Support creating menu entries with properties within content adapters
Requesting support to create [menu entries with properties](https://gohugo.io/content-management/menus/#properties-front-matter) using a content adapter, per feedback in [my forum post](https://discourse.gohugo.io/t/content-adapters-create-menu-entry-with-properties/53499). 

---

In https://github.com/gohugoio/hugo/issues/12507, @jmooring requested support for menu entry creation in content adapters. The resulting implementation in 245928a supports defining flat menu entries:

Front Matter | Content Adapter
-------------|-----------------
`menus: main` | `{{ .AddPage (dict "menus" "main" [...]) }}`
`menus: [main, footer]` | `{{ .AddPage (dict "menus" (slice "main" "footer") [...]) }}`

However, it's not possible to create menu entries with properties within a content adapter, equivalent to defining the following in front matter:

```yaml
menus:
  main:
    parent: main-page
    identifier: example-id
```

I expected that passing a map would work:

```
{{- $mainMenu := dict 
    "parent" "main-page"
    "identifier" "example-id"
-}}
{{- $menus := dict "main" $mainMenu -}}
{{- .AddPage (dict "menus" $menus [...]) -}}
```

But it seems [menus are defined as an array of strings](https://github.com/gohugoio/hugo/blob/master/resources/page/pagemeta/page_frontmatter.go#L108). Maybe it should be a map, like `Params`?

Thank you!


Discussion / hints:
>But it seems [menus are defined as an array of strings](https://github.com/gohugoio/hugo/blob/master/resources/page/pagemeta/page_frontmatter.go#L108). Maybe it should be a map, like Params?

To fix this, `Menu []string` needs to be changed to `Menu any`; that's what the menu decoder gets from front matter (and then it checks if it's a slice of strings or a map).



Fix patch:
diff --git a/resources/page/pagemeta/page_frontmatter.go b/resources/page/pagemeta/page_frontmatter.go
index 60ba0e00a04..c26662fc2c1 100644
--- a/resources/page/pagemeta/page_frontmatter.go
+++ b/resources/page/pagemeta/page_frontmatter.go
@@ -105,7 +105,7 @@ type PageConfig struct {
 	Cascade []map[string]any
 	Sitemap config.SitemapConfig
 	Build   BuildConfig
-	Menus   []string
+	Menus   any // Can be a string, []string or map[string]any.
 
 	// User defined params.
 	Params maps.Params


Test patch:
diff --git a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go
index 64ee7039731..e1d562d4d21 100644
--- a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go
+++ b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go
@@ -653,6 +653,34 @@ Footer: {{ range index site.Menus.footer }}{{ .Name }}|{{ end }}|
 	)
 }
 
+// Issue 13384.
+func TestPagesFromGoTmplMenusMap(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['rss','section','sitemap','taxonomy','term']
+-- content/_content.gotmpl --
+{{ $menu1 := dict 
+    "parent" "main-page"
+    "identifier" "id1"
+}}
+{{ $menu2 := dict 
+    "parent" "main-page"
+    "identifier" "id2"
+}}
+{{ $menus := dict "m1" $menu1 "m2" $menu2 }}
+{{ .AddPage (dict "path" "p1" "title" "p1" "menus" $menus ) }}
+
+-- layouts/index.html --
+Menus: {{ range $k, $v := site.Menus }}{{ $k }}|{{ end }}
+
+`
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/index.html", "Menus: m1|m2|")
+}
+
 func TestPagesFromGoTmplMore(t *testing.T) {
 	t.Parallel()