From 363ab48a24fe4efcd602c963080c02b955119e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Tue, 6 May 2025 09:09:05 +0200 Subject: [PATCH] Fix handling of "outputs" from content adapter pages Fixes #13689 --- hugolib/content_map.go | 2 +- hugolib/page__meta.go | 24 ++--------- .../pagesfromgotmpl_integration_test.go | 42 +++++++++++++++++++ hugolib/site.go | 2 +- resources/page/pagemeta/page_frontmatter.go | 20 +++++++-- .../page/pagemeta/page_frontmatter_test.go | 3 +- 6 files changed, 66 insertions(+), 27 deletions(-) diff --git a/hugolib/content_map.go b/hugolib/content_map.go index 56a602f54..454d36df7 100644 --- a/hugolib/content_map.go +++ b/hugolib/content_map.go @@ -356,7 +356,7 @@ func (m *pageMap) addPagesFromGoTmplFi(fi hugofs.FileMetaInfo, buildConfig *Buil Watching: s.Conf.Watching(), HandlePage: func(pt *pagesfromdata.PagesFromTemplate, pc *pagemeta.PageConfig) error { s := pt.Site.(*Site) - if err := pc.Compile(pt.GoTmplFi.Meta().PathInfo.Base(), true, "", s.Log, s.conf.MediaTypes.Config); err != nil { + if err := pc.Compile(pt.GoTmplFi.Meta().PathInfo.Base(), true, "", s.Log, s.conf.OutputFormats.Config, s.conf.MediaTypes.Config); err != nil { return err } diff --git a/hugolib/page__meta.go b/hugolib/page__meta.go index 9516c482a..94dbbab3e 100644 --- a/hugolib/page__meta.go +++ b/hugolib/page__meta.go @@ -54,7 +54,6 @@ type pageMeta struct { resource.Staler *pageMetaParams - pageMetaFrontMatter // Set for standalone pages, e.g. robotsTXT. standaloneOutputFormat output.Format @@ -79,7 +78,6 @@ func (m *pageMeta) setMetaPostPrepareRebuild() { Path: m.pageConfig.Path, Params: params, } - m.pageMetaFrontMatter = pageMetaFrontMatter{} } type pageMetaParams struct { @@ -94,11 +92,6 @@ type pageMetaParams struct { cascadeOriginal *maps.Ordered[page.PageMatcher, maps.Params] // contains the original cascade as defined in the front matter. } -// From page front matter. -type pageMetaFrontMatter struct { - configuredOutputFormats output.Formats // outputs defined in front matter. -} - func (m *pageMetaParams) init(preserveOriginal bool) { if preserveOriginal { m.paramsOriginal = xmaps.Clone[maps.Params](m.pageConfig.Params) @@ -531,16 +524,7 @@ params: for i, s := range o { o[i] = strings.ToLower(s) } - if len(o) > 0 { - // Output formats are explicitly set in front matter, use those. - outFormats, err := p.s.conf.OutputFormats.Config.GetByNames(o...) - if err != nil { - p.s.Log.Errorf("Failed to resolve output formats: %s", err) - } else { - pm.configuredOutputFormats = outFormats - params[loki] = outFormats - } - } + pm.pageConfig.Outputs = o case "draft": draft = new(bool) *draft = cast.ToBool(v) @@ -672,7 +656,7 @@ params: return err } - if err := pcfg.Compile("", false, ext, p.s.Log, p.s.conf.MediaTypes.Config); err != nil { + if err := pcfg.Compile("", false, ext, p.s.Log, p.s.conf.OutputFormats.Config, p.s.conf.MediaTypes.Config); err != nil { return err } @@ -829,8 +813,8 @@ func (p *pageMeta) newContentConverter(ps *pageState, markup string) (converter. // The output formats this page will be rendered to. func (m *pageMeta) outputFormats() output.Formats { - if len(m.configuredOutputFormats) > 0 { - return m.configuredOutputFormats + if len(m.pageConfig.ConfiguredOutputFormats) > 0 { + return m.pageConfig.ConfiguredOutputFormats } return m.s.conf.C.KindOutputFormats[m.Kind()] } diff --git a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go index a31b93999..f6e4221a0 100644 --- a/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go +++ b/hugolib/pagesfromdata/pagesfromgotmpl_integration_test.go @@ -779,3 +779,45 @@ Single. b.AssertFileContent("public/tags/index.html", "Terms: mytag: 1|§s") } + +func TestContentAdapterOutputsIssue13689(t *testing.T) { + t.Parallel() + + files := ` +-- hugo.toml -- +disableKinds = ['home','rss','section','sitemap','taxonomy','term'] +[outputs] +page = ['html','json'] +-- layouts/page.html -- +html: {{ .Title }} +-- layouts/page.json -- +json: {{ .Title }} +-- content/p1.md -- +--- +title: p1 +--- +-- content/p2.md -- +--- +title: p2 +outputs: + - html +--- +-- content/_content.gotmpl -- +{{ $page := dict "path" "p3" "title" "p3" }} +{{ $.AddPage $page }} + +{{ $page := dict "path" "p4" "title" "p4" "outputs" (slice "html") }} +{{ $.AddPage $page }} +` + + b := hugolib.Test(t, files) + + b.AssertFileExists("public/p1/index.html", true) + b.AssertFileExists("public/p1/index.json", true) + b.AssertFileExists("public/p2/index.html", true) + b.AssertFileExists("public/p2/index.json", false) + b.AssertFileExists("public/p3/index.html", true) + b.AssertFileExists("public/p3/index.json", true) + b.AssertFileExists("public/p4/index.html", true) + b.AssertFileExists("public/p4/index.json", false) // currently returns true +} diff --git a/hugolib/site.go b/hugolib/site.go index 728b036d2..0e658086b 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -804,7 +804,7 @@ func (s *Site) initRenderFormats() { Tree: s.pageMap.treePages, Handle: func(key string, n contentNodeI, match doctree.DimensionFlag) (bool, error) { if p, ok := n.(*pageState); ok { - for _, f := range p.m.configuredOutputFormats { + for _, f := range p.m.pageConfig.ConfiguredOutputFormats { if !formatSet[f.Name] { formats = append(formats, f) formatSet[f.Name] = true diff --git a/resources/page/pagemeta/page_frontmatter.go b/resources/page/pagemeta/page_frontmatter.go index 7f98b1b88..eb52e5055 100644 --- a/resources/page/pagemeta/page_frontmatter.go +++ b/resources/page/pagemeta/page_frontmatter.go @@ -29,6 +29,7 @@ import ( "github.com/gohugoio/hugo/hugofs/files" "github.com/gohugoio/hugo/markup" "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/resources/kinds" "github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/resource" @@ -114,9 +115,10 @@ type PageConfig struct { Content Source // Compiled values. - CascadeCompiled *maps.Ordered[page.PageMatcher, maps.Params] `mapstructure:"-" json:"-"` - ContentMediaType media.Type `mapstructure:"-" json:"-"` - IsFromContentAdapter bool `mapstructure:"-" json:"-"` + CascadeCompiled *maps.Ordered[page.PageMatcher, maps.Params] `mapstructure:"-" json:"-"` + ContentMediaType media.Type `mapstructure:"-" json:"-"` + ConfiguredOutputFormats output.Formats `mapstructure:"-" json:"-"` + IsFromContentAdapter bool `mapstructure:"-" json:"-"` } var DefaultPageConfig = PageConfig{ @@ -150,7 +152,7 @@ func (p *PageConfig) Validate(pagesFromData bool) error { } // Compile sets up the page configuration after all fields have been set. -func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, logger loggers.Logger, mediaTypes media.Types) error { +func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, logger loggers.Logger, outputFormats output.Formats, mediaTypes media.Types) error { // In content adapters, we always get relative paths. if basePath != "" { p.Path = path.Join(basePath, p.Path) @@ -195,6 +197,15 @@ func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, lo p.Content.Markup = p.ContentMediaType.SubType } + if len(p.Outputs) > 0 { + outFormats, err := outputFormats.GetByNames(p.Outputs...) + if err != nil { + return fmt.Errorf("failed to resolve output formats %v: %w", p.Outputs, err) + } else { + p.ConfiguredOutputFormats = outFormats + } + } + if pagesFromData { if p.Kind == "" { p.Kind = kinds.KindPage @@ -205,6 +216,7 @@ func (p *PageConfig) Compile(basePath string, pagesFromData bool, ext string, lo // but also because people tend to use use the filename to name their resources (with spaces and all), // and this isn't relevant when creating resources from an API where it's easy to add textual meta data. p.Path = paths.NormalizePathStringBasic(p.Path) + } if p.Cascade != nil { diff --git a/resources/page/pagemeta/page_frontmatter_test.go b/resources/page/pagemeta/page_frontmatter_test.go index fe9d3d99c..de2932391 100644 --- a/resources/page/pagemeta/page_frontmatter_test.go +++ b/resources/page/pagemeta/page_frontmatter_test.go @@ -22,6 +22,7 @@ import ( "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config/testconfig" "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/resources/page/pagemeta" @@ -175,7 +176,7 @@ func TestContentMediaTypeFromMarkup(t *testing.T) { } { var pc pagemeta.PageConfig pc.Content.Markup = test.in - c.Assert(pc.Compile("", true, "", logger, media.DefaultTypes), qt.IsNil) + c.Assert(pc.Compile("", true, "", logger, output.DefaultFormats, media.DefaultTypes), qt.IsNil) c.Assert(pc.ContentMediaType.Type, qt.Equals, test.expected) } }