From 102d98dab2725edbd73ec2270e3c1662c2f598c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Thu, 27 Apr 2023 17:07:06 +0200 Subject: [PATCH] Work --- config/allconfig/allconfig.go | 278 ++-------------------------- config/allconfig/alldecoders.go | 319 ++++++++++++++++++++++++++++++++ config/commonConfig.go | 9 +- config/commonConfig_test.go | 1 + 4 files changed, 341 insertions(+), 266 deletions(-) create mode 100644 config/allconfig/alldecoders.go diff --git a/config/allconfig/allconfig.go b/config/allconfig/allconfig.go index 83ff99cb71a..ba0b8bbc745 100644 --- a/config/allconfig/allconfig.go +++ b/config/allconfig/allconfig.go @@ -27,7 +27,6 @@ import ( "github.com/gohugoio/hugo/cache/filecache" "github.com/gohugoio/hugo/common/maps" - "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/common/urls" "github.com/gohugoio/hugo/config" "github.com/gohugoio/hugo/config/privacy" @@ -45,9 +44,7 @@ import ( "github.com/gohugoio/hugo/resources/images" "github.com/gohugoio/hugo/resources/page" "github.com/gohugoio/hugo/resources/page/pagemeta" - "github.com/mitchellh/mapstructure" "github.com/spf13/afero" - "github.com/spf13/cast" xmaps "golang.org/x/exp/maps" ) @@ -152,7 +149,11 @@ type Config struct { UglyURLs any `mapstructure:"-"` } -func (c *Config) Compile() error { +type configCompiler interface { + CompileConfig() error +} + +func (c *Config) CompileConfig() error { s := c.Timeout if _, err := strconv.Atoi(s); err == nil { // A number, assume seconds. @@ -247,6 +248,14 @@ func (c *Config) Compile() error { MainSections: c.MainSections, } + for _, s := range allDecoderSetups { + if getCompiler := s.getCompiler; getCompiler != nil { + if err := getCompiler(c).CompileConfig(); err != nil { + return err + } + } + } + return nil } @@ -545,7 +554,7 @@ func FromLoadConfigResult(fs afero.Fs, res config.LoadConfigResult) (Configs, er languagesConfig := maps.CleanConfigStringMap(root.GetStringMap("languages")) var isMultiHost bool - if err := all.Compile(); err != nil { + if err := all.CompileConfig(); err != nil { return Configs{}, err } @@ -596,7 +605,7 @@ func FromLoadConfigResult(fs afero.Fs, res config.LoadConfigResult) (Configs, er if err := decodeConfigFromParams(fs, bcfg, mergedConfig, &clone, differentRootKeys); err != nil { return Configs{}, fmt.Errorf("failed to decode config for language %q: %w", k, err) } - if err := clone.Compile(); err != nil { + if err := clone.CompileConfig(); err != nil { return Configs{}, err } perLanguage[k] = clone @@ -650,259 +659,7 @@ func FromLoadConfigResult(fs afero.Fs, res config.LoadConfigResult) (Configs, er return cm, nil } -func decodeConfigFromParams(fs afero.Fs, bcfg config.BaseConfig, cfg maps.Params, all *Config, keys []string) error { - var err error - - type decodeWeight struct { - key string - decode func(d decodeWeight) error - weight int - } - - allDecoderSetups := map[string]decodeWeight{ - "": { - key: "", - weight: -100, // Always first. - decode: func(d decodeWeight) error { return mapstructure.WeakDecode(cfg, &all.RootConfig) }, - }, - "imaging": { - key: "imaging", - decode: func(d decodeWeight) error { - var err error - all.Imaging, err = images.DecodeConfig(cfg.GetStringMap(d.key)) - return err - }, - }, - "caches": { - key: "caches", - decode: func(d decodeWeight) error { - var err error - all.Caches, err = filecache.DecodeConfig(fs, bcfg, cfg.GetStringMap(d.key)) - if all.IgnoreCache { - // Set MaxAge in all caches to 0. - for k, c := range all.Caches { - c.MaxAge = 0 - all.Caches[k] = c - } - } - return err - }, - }, - "build": { - key: "build", - decode: func(d decodeWeight) error { - all.Build = config.DecodeBuildConfig(cfg) - return nil - }, - }, - "frontmatter": { - key: "frontmatter", - decode: func(d decodeWeight) error { - all.Frontmatter, err = pagemeta.DecodeFrontMatterConfig(cfg) - return err - }, - }, - "markup": { - key: "markup", - decode: func(d decodeWeight) error { - var err error - all.Markup, err = markup_config.Decode(cfg) - return err - }, - }, - "server": { - key: "server", - decode: func(d decodeWeight) error { - all.Server, err = config.DecodeServer(cfg) - return err - }, - }, - "minify": { - key: "minify", - decode: func(d decodeWeight) error { - all.Minify, err = minifiers.DecodeConfig(cfg.Get(d.key)) - return err - }, - }, - "mediaTypes": { - key: "mediaTypes", - decode: func(d decodeWeight) error { - all.MediaTypes, err = media.DecodeTypes2(cfg.GetStringMap(d.key)) - return err - }, - }, - "outputs": { - key: "outputs", - decode: func(d decodeWeight) error { - defaults := createDefaultOutputFormats(all.OutputFormats.Config) - m := cfg.GetStringMap("outputs") - all.Outputs = make(map[string][]string) - for k, v := range m { - s := types.ToStringSlicePreserveString(v) - for i, v := range s { - // TODO1 also do this with the output slice in frontmatter. - s[i] = strings.ToLower(v) - } - all.Outputs[k] = s - } - // Apply defaults. - for k, v := range defaults { - if _, found := all.Outputs[k]; !found { - all.Outputs[k] = v - } - } - return nil - }, - }, - "outputFormats": { - key: "outputFormats", - decode: func(d decodeWeight) error { - all.OutputFormats, err = output.DecodeConfig(all.MediaTypes.Config, cfg.Get(d.key)) - return err - }, - }, - "params": { - key: "params", - decode: func(d decodeWeight) error { - all.Params = maps.CleanConfigStringMap(cfg.GetStringMap("params")) - if all.Params == nil { - all.Params = make(map[string]any) - } - - // Before Hugo 0.112.0 this was configured via site Params. - if mainSections, found := all.Params["mainsections"]; found { - all.MainSections = types.ToStringSlicePreserveString(mainSections) - } - - return nil - }, - }, - "module": { - key: "module", - decode: func(d decodeWeight) error { - all.Module, err = modules.DecodeConfig(cfg) - return err - }, - }, - "permalinks": { - key: "permalinks", - decode: func(d decodeWeight) error { - all.Permalinks = maps.CleanConfigStringMapString(cfg.GetStringMapString(d.key)) - return nil - }, - }, - "sitemap": { - key: "sitemap", - decode: func(d decodeWeight) error { - var err error - all.Sitemap, err = config.DecodeSitemap(config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, cfg.GetStringMap(d.key)) - return err - }, - }, - "taxonomies": { - key: "taxonomies", - decode: func(d decodeWeight) error { - all.Taxonomies = maps.CleanConfigStringMapString(cfg.GetStringMapString(d.key)) - return nil - }, - }, - "related": { - key: "related", - weight: 100, // This needs to be decoded after taxonomies. - decode: func(d decodeWeight) error { - if cfg.IsSet(d.key) { - all.Related, err = related.DecodeConfig(cfg.GetParams(d.key)) - if err != nil { - return fmt.Errorf("failed to decode related config: %w", err) - } - } else { - all.Related = related.DefaultConfig - if _, found := all.Taxonomies["tag"]; found { - all.Related.Add(related.IndexConfig{Name: "tags", Weight: 80}) - } - } - return nil - }, - }, - "languages": { - key: "languages", - decode: func(d decodeWeight) error { - all.Languages, err = langs.DecodeConfig(cfg.GetStringMap(d.key)) - return err - }, - }, - "cascade": { - key: "cascade", - decode: func(d decodeWeight) error { - all.Cascade, err = page.DecodeCascadeConfig(cfg.Get(d.key)) - return err - }, - }, - "menus": { - key: "menus", - decode: func(d decodeWeight) error { - all.Menus, err = navigation.DecodeConfig(cfg.Get(d.key)) - return err - }, - }, - "privacy": { - key: "privacy", - decode: func(d decodeWeight) error { - all.Privacy, err = privacy.DecodeConfig(cfg) - return err - }, - }, - "security": { - key: "security", - decode: func(d decodeWeight) error { - all.Security, err = security.DecodeConfig(cfg) - return err - }, - }, - "services": { - key: "services", - decode: func(d decodeWeight) error { - all.Services, err = services.DecodeConfig(cfg) - return err - }, - }, - "author": { - key: "author", - decode: func(d decodeWeight) error { - all.Author = cfg.GetStringMap(d.key) - return nil - }, - }, - "social": { - key: "social", - decode: func(d decodeWeight) error { - all.Social = cfg.GetStringMapString(d.key) - return nil - }, - }, - "uglyurls": { - key: "uglyurls", - decode: func(d decodeWeight) error { - v := cfg.Get(d.key) - switch vv := v.(type) { - case bool: - all.UglyURLs = vv - case string: - all.UglyURLs = vv == "true" - default: - all.UglyURLs = cast.ToStringMapBool(v) - } - return nil - }, - }, - "internal": { - // TODO1 make sure this isn't set from the outside. - key: "internal", - decode: func(d decodeWeight) error { - return mapstructure.WeakDecode(cfg.GetStringMap(d.key), &all.Internal) - }, - }, - } +func decodeConfigFromParams(fs afero.Fs, bcfg config.BaseConfig, p maps.Params, foo *Config, keys []string) error { var decoderSetups []decodeWeight @@ -930,7 +687,8 @@ func decodeConfigFromParams(fs afero.Fs, bcfg config.BaseConfig, cfg maps.Params }) for _, v := range decoderSetups { - if err := v.decode(v); err != nil { + p := decodeParams{p: p, c: foo, fs: fs, bcfg: bcfg} + if err := v.decode(v, p); err != nil { return fmt.Errorf("failed to decode %q: %w", v.key, err) } } diff --git a/config/allconfig/alldecoders.go b/config/allconfig/alldecoders.go new file mode 100644 index 00000000000..3897bf5cbdb --- /dev/null +++ b/config/allconfig/alldecoders.go @@ -0,0 +1,319 @@ +// Copyright 2023 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 allconfig + +import ( + "fmt" + "strings" + + "github.com/gohugoio/hugo/cache/filecache" + "github.com/gohugoio/hugo/common/maps" + "github.com/gohugoio/hugo/common/types" + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/config/privacy" + "github.com/gohugoio/hugo/config/security" + "github.com/gohugoio/hugo/config/services" + "github.com/gohugoio/hugo/langs" + "github.com/gohugoio/hugo/markup/markup_config" + "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/minifiers" + "github.com/gohugoio/hugo/modules" + "github.com/gohugoio/hugo/navigation" + "github.com/gohugoio/hugo/output" + "github.com/gohugoio/hugo/related" + "github.com/gohugoio/hugo/resources/images" + "github.com/gohugoio/hugo/resources/page" + "github.com/gohugoio/hugo/resources/page/pagemeta" + "github.com/mitchellh/mapstructure" + "github.com/spf13/afero" + "github.com/spf13/cast" +) + +type decodeParams struct { + p maps.Params + c *Config + fs afero.Fs + bcfg config.BaseConfig +} + +type decodeWeight struct { + key string + decode func(decodeWeight, decodeParams) error + getCompiler func(c *Config) configCompiler + weight int +} + +// TODO1 getValue +var allDecoderSetups = map[string]decodeWeight{ + "": { + key: "", + weight: -100, // Always first. + decode: func(d decodeWeight, p decodeParams) error { + return mapstructure.WeakDecode(p.p, &p.c.RootConfig) + }, + }, + "imaging": { + key: "imaging", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Imaging, err = images.DecodeConfig(p.p.GetStringMap(d.key)) + return err + }, + }, + "caches": { + key: "caches", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Caches, err = filecache.DecodeConfig(p.fs, p.bcfg, p.p.GetStringMap(d.key)) + if p.c.IgnoreCache { + // Set MaxAge in all caches to 0. + for k, cache := range p.c.Caches { + cache.MaxAge = 0 + p.c.Caches[k] = cache + } + } + return err + }, + }, + "build": { + key: "build", + decode: func(d decodeWeight, p decodeParams) error { + p.c.Build = config.DecodeBuildConfig(p.p) + return nil + }, + }, + "frontmatter": { + key: "frontmatter", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Frontmatter, err = pagemeta.DecodeFrontMatterConfig(p.p) + return err + }, + }, + "markup": { + key: "markup", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Markup, err = markup_config.Decode(p.p) + return err + }, + }, + "server": { + key: "server", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Server, err = config.DecodeServer(p.p) + return err + }, + getCompiler: func(c *Config) configCompiler { + return &c.Server + }, + }, + "minify": { + key: "minify", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Minify, err = minifiers.DecodeConfig(p.p.Get(d.key)) + return err + }, + }, + "mediaTypes": { + key: "mediaTypes", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.MediaTypes, err = media.DecodeTypes2(p.p.GetStringMap(d.key)) + return err + }, + }, + "outputs": { + key: "outputs", + decode: func(d decodeWeight, p decodeParams) error { + defaults := createDefaultOutputFormats(p.c.OutputFormats.Config) + m := p.p.GetStringMap("outputs") + p.c.Outputs = make(map[string][]string) + for k, v := range m { + s := types.ToStringSlicePreserveString(v) + for i, v := range s { + // TODO1 also do this with the output slice in frontmatter. + s[i] = strings.ToLower(v) + } + p.c.Outputs[k] = s + } + // Apply defaults. + for k, v := range defaults { + if _, found := p.c.Outputs[k]; !found { + p.c.Outputs[k] = v + } + } + return nil + }, + }, + "outputFormats": { + key: "outputFormats", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.OutputFormats, err = output.DecodeConfig(p.c.MediaTypes.Config, p.p.Get(d.key)) + return err + }, + }, + "params": { + key: "params", + decode: func(d decodeWeight, p decodeParams) error { + p.c.Params = maps.CleanConfigStringMap(p.p.GetStringMap("params")) + if p.c.Params == nil { + p.c.Params = make(map[string]any) + } + + // Before Hugo 0.112.0 this was configured via site Params. + if mainSections, found := p.c.Params["mainsections"]; found { + p.c.MainSections = types.ToStringSlicePreserveString(mainSections) + } + + return nil + }, + }, + "module": { + key: "module", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Module, err = modules.DecodeConfig(p.p) + return err + }, + }, + "permalinks": { + key: "permalinks", + decode: func(d decodeWeight, p decodeParams) error { + p.c.Permalinks = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key)) + return nil + }, + }, + "sitemap": { + key: "sitemap", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Sitemap, err = config.DecodeSitemap(config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, p.p.GetStringMap(d.key)) + return err + }, + }, + "taxonomies": { + key: "taxonomies", + decode: func(d decodeWeight, p decodeParams) error { + p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key)) + return nil + }, + }, + "related": { + key: "related", + weight: 100, // This needs to be decoded after taxonomies. + decode: func(d decodeWeight, p decodeParams) error { + if p.p.IsSet(d.key) { + var err error + p.c.Related, err = related.DecodeConfig(p.p.GetParams(d.key)) + if err != nil { + return fmt.Errorf("failed to decode related config: %w", err) + } + } else { + p.c.Related = related.DefaultConfig + if _, found := p.c.Taxonomies["tag"]; found { + p.c.Related.Add(related.IndexConfig{Name: "tags", Weight: 80}) + } + } + return nil + }, + }, + "languages": { + key: "languages", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Languages, err = langs.DecodeConfig(p.p.GetStringMap(d.key)) + return err + }, + }, + "cascade": { + key: "cascade", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Cascade, err = page.DecodeCascadeConfig(p.p.Get(d.key)) + return err + }, + }, + "menus": { + key: "menus", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Menus, err = navigation.DecodeConfig(p.p.Get(d.key)) + return err + }, + }, + "privacy": { + key: "privacy", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Privacy, err = privacy.DecodeConfig(p.p) + return err + }, + }, + "security": { + key: "security", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Security, err = security.DecodeConfig(p.p) + return err + }, + }, + "services": { + key: "services", + decode: func(d decodeWeight, p decodeParams) error { + var err error + p.c.Services, err = services.DecodeConfig(p.p) + return err + }, + }, + "author": { + key: "author", + decode: func(d decodeWeight, p decodeParams) error { + p.c.Author = p.p.GetStringMap(d.key) + return nil + }, + }, + "social": { + key: "social", + decode: func(d decodeWeight, p decodeParams) error { + p.c.Social = p.p.GetStringMapString(d.key) + return nil + }, + }, + "uglyurls": { + key: "uglyurls", + decode: func(d decodeWeight, p decodeParams) error { + v := p.p.Get(d.key) + switch vv := v.(type) { + case bool: + p.c.UglyURLs = vv + case string: + p.c.UglyURLs = vv == "true" + default: + p.c.UglyURLs = cast.ToStringMapBool(v) + } + return nil + }, + }, + "internal": { + // TODO1 make sure this isn't set from the outside. + key: "internal", + decode: func(d decodeWeight, p decodeParams) error { + return mapstructure.WeakDecode(p.p.GetStringMap(d.key), &p.c.Internal) + }, + }, +} diff --git a/config/commonConfig.go b/config/commonConfig.go index e7d58f401a2..bec22445f3d 100644 --- a/config/commonConfig.go +++ b/config/commonConfig.go @@ -152,9 +152,9 @@ type Server struct { compiledRedirects []glob.Glob } -func (s *Server) init() { +func (s *Server) CompileConfig() error { if s.compiledHeaders != nil { - return + return nil } for _, h := range s.Headers { s.compiledHeaders = append(s.compiledHeaders, glob.MustCompile(h.For)) @@ -162,11 +162,10 @@ func (s *Server) init() { for _, r := range s.Redirects { s.compiledRedirects = append(s.compiledRedirects, glob.MustCompile(r.From)) } + return nil } func (s *Server) MatchHeaders(pattern string) []types.KeyValueStr { - s.init() - if s.compiledHeaders == nil { return nil } @@ -190,8 +189,6 @@ func (s *Server) MatchHeaders(pattern string) []types.KeyValueStr { } func (s *Server) MatchRedirect(pattern string) Redirect { - s.init() - if s.compiledRedirects == nil { return Redirect{} } diff --git a/config/commonConfig_test.go b/config/commonConfig_test.go index 23e86c27e08..f0566444820 100644 --- a/config/commonConfig_test.go +++ b/config/commonConfig_test.go @@ -91,6 +91,7 @@ status = 301 s, err := DecodeServer(cfg) c.Assert(err, qt.IsNil) + c.Assert(s.CompileConfig(), qt.IsNil) c.Assert(s.MatchHeaders("/foo.jpg"), qt.DeepEquals, []types.KeyValueStr{ {Key: "X-Content-Type-Options", Value: "nosniff"},