From f8dc47eeffa847fd0b51e376da355e3d957848a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sat, 10 Mar 2018 11:45:29 +0100 Subject: [PATCH] Allow partial redefinition of the ouputs config Fixes #4487 --- hugolib/site_output.go | 86 +++++++++++++----------- hugolib/site_output_test.go | 129 ++++++++++++++++++++++-------------- output/outputFormat.go | 10 +++ 3 files changed, 137 insertions(+), 88 deletions(-) diff --git a/hugolib/site_output.go b/hugolib/site_output.go index 5a99692a3..497092e8c 100644 --- a/hugolib/site_output.go +++ b/hugolib/site_output.go @@ -24,9 +24,46 @@ import ( "github.com/spf13/cast" ) +func createDefaultOutputFormats(allFormats output.Formats, cfg config.Provider) map[string]output.Formats { + rssOut, _ := allFormats.GetByName(output.RSSFormat.Name) + htmlOut, _ := allFormats.GetByName(output.HTMLFormat.Name) + robotsOut, _ := allFormats.GetByName(output.RobotsTxtFormat.Name) + sitemapOut, _ := allFormats.GetByName(output.SitemapFormat.Name) + + // TODO(bep) this mumbo jumbo is deprecated and should be removed, but there are tests that + // depends on this, so that will have to wait. + rssBase := cfg.GetString("rssURI") + if rssBase == "" || rssBase == "index.xml" { + rssBase = rssOut.BaseName + } else { + // Remove in Hugo 0.36. + helpers.Deprecated("Site config", "rssURI", "Set baseName in outputFormats.RSS", true) + // RSS has now a well defined media type, so strip any suffix provided + rssBase = strings.TrimSuffix(rssBase, path.Ext(rssBase)) + } + + rssOut.BaseName = rssBase + + return map[string]output.Formats{ + KindPage: output.Formats{htmlOut}, + KindHome: output.Formats{htmlOut, rssOut}, + KindSection: output.Formats{htmlOut, rssOut}, + KindTaxonomy: output.Formats{htmlOut, rssOut}, + KindTaxonomyTerm: output.Formats{htmlOut, rssOut}, + // Below are for conistency. They are currently not used during rendering. + kindRSS: output.Formats{rssOut}, + kindSitemap: output.Formats{sitemapOut}, + kindRobotsTXT: output.Formats{robotsOut}, + kind404: output.Formats{htmlOut}, + } + +} + func createSiteOutputFormats(allFormats output.Formats, cfg config.Provider) (map[string]output.Formats, error) { + defaultOutputFormats := createDefaultOutputFormats(allFormats, cfg) + if !cfg.IsSet("outputs") { - return createDefaultOutputFormats(allFormats, cfg) + return defaultOutputFormats, nil } outFormats := make(map[string]output.Formats) @@ -37,6 +74,8 @@ func createSiteOutputFormats(allFormats output.Formats, cfg config.Provider) (ma return outFormats, nil } + seen := make(map[string]bool) + for k, v := range outputs { var formats output.Formats vals := cast.ToStringSlice(v) @@ -48,52 +87,21 @@ func createSiteOutputFormats(allFormats output.Formats, cfg config.Provider) (ma formats = append(formats, f) } + // This effectively prevents empty outputs entries for a given Kind. + // We need at least one. if len(formats) > 0 { + seen[k] = true outFormats[k] = formats } } - // Make sure every kind has at least one output format - for _, kind := range allKinds { - if _, found := outFormats[kind]; !found { - outFormats[kind] = output.Formats{output.HTMLFormat} + // Add defaults for the entries not provided by the user. + for k, v := range defaultOutputFormats { + if !seen[k] { + outFormats[k] = v } } return outFormats, nil } - -func createDefaultOutputFormats(allFormats output.Formats, cfg config.Provider) (map[string]output.Formats, error) { - outFormats := make(map[string]output.Formats) - rssOut, _ := allFormats.GetByName(output.RSSFormat.Name) - htmlOut, _ := allFormats.GetByName(output.HTMLFormat.Name) - - for _, kind := range allKinds { - var formats output.Formats - // All have HTML - formats = append(formats, htmlOut) - - // All but page have RSS - if kind != KindPage { - - rssBase := cfg.GetString("rssURI") - if rssBase == "" || rssBase == "index.xml" { - rssBase = rssOut.BaseName - } else { - // Remove in Hugo 0.36. - helpers.Deprecated("Site config", "rssURI", "Set baseName in outputFormats.RSS", true) - // RSS has now a well defined media type, so strip any suffix provided - rssBase = strings.TrimSuffix(rssBase, path.Ext(rssBase)) - } - - rssOut.BaseName = rssBase - formats = append(formats, rssOut) - - } - - outFormats[kind] = formats - } - - return outFormats, nil -} diff --git a/hugolib/site_output_test.go b/hugolib/site_output_test.go index a8a43d625..7da6f105f 100644 --- a/hugolib/site_output_test.go +++ b/hugolib/site_output_test.go @@ -14,7 +14,6 @@ package hugolib import ( - "reflect" "strings" "testing" @@ -29,54 +28,6 @@ import ( "github.com/spf13/viper" ) -func TestDefaultOutputFormats(t *testing.T) { - t.Parallel() - defs, err := createDefaultOutputFormats(output.DefaultFormats, viper.New()) - - require.NoError(t, err) - - tests := []struct { - name string - kind string - want output.Formats - }{ - {"RSS not for regular pages", KindPage, output.Formats{output.HTMLFormat}}, - {"Home Sweet Home", KindHome, output.Formats{output.HTMLFormat, output.RSSFormat}}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := defs[tt.kind]; !reflect.DeepEqual(got, tt.want) { - t.Errorf("createDefaultOutputFormats(%v) = %v, want %v", tt.kind, got, tt.want) - } - }) - } -} - -func TestDefaultOutputFormatsWithOverrides(t *testing.T) { - t.Parallel() - - htmlOut := output.HTMLFormat - htmlOut.BaseName = "htmlindex" - rssOut := output.RSSFormat - rssOut.BaseName = "feed" - - defs, err := createDefaultOutputFormats(output.Formats{htmlOut, rssOut}, viper.New()) - - homeDefs := defs[KindHome] - - rss, found := homeDefs.GetByName("RSS") - require.True(t, found) - require.Equal(t, rss.BaseName, "feed") - - html, found := homeDefs.GetByName("HTML") - require.True(t, found) - require.Equal(t, html.BaseName, "htmlindex") - - require.NoError(t, err) - -} - func TestSiteWithPageOutputs(t *testing.T) { for _, outputs := range [][]string{{"html", "json", "calendar"}, {"json"}} { t.Run(fmt.Sprintf("%v", outputs), func(t *testing.T) { @@ -373,3 +324,83 @@ baseName = "customdelimbase" require.Equal(t, "/blog/customdelimbase_del", outputs.Get("CUS").RelPermalink()) } + +func TestCreateSiteOutputFormats(t *testing.T) { + assert := require.New(t) + + outputsConfig := map[string]interface{}{ + KindHome: []string{"HTML", "JSON"}, + KindSection: []string{"JSON"}, + } + + cfg := viper.New() + cfg.Set("outputs", outputsConfig) + + outputs, err := createSiteOutputFormats(output.DefaultFormats, cfg) + assert.NoError(err) + assert.Equal(output.Formats{output.JSONFormat}, outputs[KindSection]) + assert.Equal(output.Formats{output.HTMLFormat, output.JSONFormat}, outputs[KindHome]) + + // Defaults + assert.Equal(output.Formats{output.HTMLFormat, output.RSSFormat}, outputs[KindTaxonomy]) + assert.Equal(output.Formats{output.HTMLFormat, output.RSSFormat}, outputs[KindTaxonomyTerm]) + assert.Equal(output.Formats{output.HTMLFormat}, outputs[KindPage]) + + // These aren't (currently) in use when rendering in Hugo, + // but the pages needs to be assigned an output format, + // so these should also be correct/sensible. + assert.Equal(output.Formats{output.RSSFormat}, outputs[kindRSS]) + assert.Equal(output.Formats{output.SitemapFormat}, outputs[kindSitemap]) + assert.Equal(output.Formats{output.RobotsTxtFormat}, outputs[kindRobotsTXT]) + assert.Equal(output.Formats{output.HTMLFormat}, outputs[kind404]) + +} + +func TestCreateSiteOutputFormatsInvalidConfig(t *testing.T) { + assert := require.New(t) + + outputsConfig := map[string]interface{}{ + KindHome: []string{"FOO", "JSON"}, + } + + cfg := viper.New() + cfg.Set("outputs", outputsConfig) + + _, err := createSiteOutputFormats(output.DefaultFormats, cfg) + assert.Error(err) +} + +func TestCreateSiteOutputFormatsEmptyConfig(t *testing.T) { + assert := require.New(t) + + outputsConfig := map[string]interface{}{ + KindHome: []string{}, + } + + cfg := viper.New() + cfg.Set("outputs", outputsConfig) + + outputs, err := createSiteOutputFormats(output.DefaultFormats, cfg) + assert.NoError(err) + assert.Equal(output.Formats{output.HTMLFormat, output.RSSFormat}, outputs[KindHome]) +} + +func TestCreateSiteOutputFormatsCustomFormats(t *testing.T) { + assert := require.New(t) + + outputsConfig := map[string]interface{}{ + KindHome: []string{}, + } + + cfg := viper.New() + cfg.Set("outputs", outputsConfig) + + var ( + customRSS = output.Format{Name: "RSS", BaseName: "customRSS"} + customHTML = output.Format{Name: "HTML", BaseName: "customHTML"} + ) + + outputs, err := createSiteOutputFormats(output.Formats{customRSS, customHTML}, cfg) + assert.NoError(err) + assert.Equal(output.Formats{customHTML, customRSS}, outputs[KindHome]) +} diff --git a/output/outputFormat.go b/output/outputFormat.go index 0920e5736..877850160 100644 --- a/output/outputFormat.go +++ b/output/outputFormat.go @@ -140,6 +140,14 @@ var ( NoUgly: true, Rel: "alternate", } + + SitemapFormat = Format{ + Name: "Sitemap", + MediaType: media.XMLType, + BaseName: "sitemap", + NoUgly: true, + Rel: "sitemap", + } ) var DefaultFormats = Formats{ @@ -149,7 +157,9 @@ var DefaultFormats = Formats{ CSVFormat, HTMLFormat, JSONFormat, + RobotsTxtFormat, RSSFormat, + SitemapFormat, } func init() {