github.com/neohugo/neohugo@v0.123.8/config/allconfig/alldecoders.go (about) 1 // Copyright 2024 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package allconfig 15 16 import ( 17 "fmt" 18 "strings" 19 20 "github.com/neohugo/neohugo/cache/filecache" 21 "github.com/neohugo/neohugo/common/maps" 22 "github.com/neohugo/neohugo/common/types" 23 "github.com/neohugo/neohugo/config" 24 "github.com/neohugo/neohugo/config/privacy" 25 "github.com/neohugo/neohugo/config/security" 26 "github.com/neohugo/neohugo/config/services" 27 "github.com/neohugo/neohugo/deploy/deployconfig" 28 "github.com/neohugo/neohugo/langs" 29 "github.com/neohugo/neohugo/markup/markup_config" 30 "github.com/neohugo/neohugo/media" 31 "github.com/neohugo/neohugo/minifiers" 32 "github.com/neohugo/neohugo/modules" 33 "github.com/neohugo/neohugo/navigation" 34 "github.com/neohugo/neohugo/output" 35 "github.com/neohugo/neohugo/related" 36 "github.com/neohugo/neohugo/resources/images" 37 "github.com/neohugo/neohugo/resources/page" 38 "github.com/neohugo/neohugo/resources/page/pagemeta" 39 40 "github.com/mitchellh/mapstructure" 41 "github.com/spf13/afero" 42 "github.com/spf13/cast" 43 ) 44 45 type decodeConfig struct { 46 p config.Provider 47 c *Config 48 fs afero.Fs 49 bcfg config.BaseConfig 50 } 51 52 type decodeWeight struct { 53 key string 54 decode func(decodeWeight, decodeConfig) error 55 getCompiler func(c *Config) configCompiler 56 weight int 57 internalOrDeprecated bool // Hide it from the docs. 58 } 59 60 var allDecoderSetups = map[string]decodeWeight{ 61 "": { 62 key: "", 63 weight: -100, // Always first. 64 decode: func(d decodeWeight, p decodeConfig) error { 65 if err := mapstructure.WeakDecode(p.p.Get(""), &p.c.RootConfig); err != nil { 66 return err 67 } 68 69 // This need to match with Lang which is always lower case. 70 p.c.RootConfig.DefaultContentLanguage = strings.ToLower(p.c.RootConfig.DefaultContentLanguage) 71 72 return nil 73 }, 74 }, 75 "imaging": { 76 key: "imaging", 77 decode: func(d decodeWeight, p decodeConfig) error { 78 var err error 79 p.c.Imaging, err = images.DecodeConfig(p.p.GetStringMap(d.key)) 80 return err 81 }, 82 }, 83 "caches": { 84 key: "caches", 85 decode: func(d decodeWeight, p decodeConfig) error { 86 var err error 87 p.c.Caches, err = filecache.DecodeConfig(p.fs, p.bcfg, p.p.GetStringMap(d.key)) 88 if p.c.IgnoreCache { 89 // Set MaxAge in all caches to 0. 90 for k, cache := range p.c.Caches { 91 cache.MaxAge = 0 92 p.c.Caches[k] = cache 93 } 94 } 95 return err 96 }, 97 }, 98 "build": { 99 key: "build", 100 decode: func(d decodeWeight, p decodeConfig) error { 101 p.c.Build = config.DecodeBuildConfig(p.p) 102 return nil 103 }, 104 getCompiler: func(c *Config) configCompiler { 105 return &c.Build 106 }, 107 }, 108 "frontmatter": { 109 key: "frontmatter", 110 decode: func(d decodeWeight, p decodeConfig) error { 111 var err error 112 p.c.Frontmatter, err = pagemeta.DecodeFrontMatterConfig(p.p) 113 return err 114 }, 115 }, 116 "markup": { 117 key: "markup", 118 decode: func(d decodeWeight, p decodeConfig) error { 119 var err error 120 p.c.Markup, err = markup_config.Decode(p.p) 121 return err 122 }, 123 }, 124 "server": { 125 key: "server", 126 decode: func(d decodeWeight, p decodeConfig) error { 127 var err error 128 p.c.Server, err = config.DecodeServer(p.p) 129 return err 130 }, 131 getCompiler: func(c *Config) configCompiler { 132 return &c.Server 133 }, 134 }, 135 "minify": { 136 key: "minify", 137 decode: func(d decodeWeight, p decodeConfig) error { 138 var err error 139 p.c.Minify, err = minifiers.DecodeConfig(p.p.Get(d.key)) 140 return err 141 }, 142 }, 143 "mediatypes": { 144 key: "mediatypes", 145 decode: func(d decodeWeight, p decodeConfig) error { 146 var err error 147 p.c.MediaTypes, err = media.DecodeTypes(p.p.GetStringMap(d.key)) 148 return err 149 }, 150 }, 151 "outputs": { 152 key: "outputs", 153 decode: func(d decodeWeight, p decodeConfig) error { 154 defaults := createDefaultOutputFormats(p.c.OutputFormats.Config) 155 m := maps.CleanConfigStringMap(p.p.GetStringMap("outputs")) 156 p.c.Outputs = make(map[string][]string) 157 for k, v := range m { 158 s := types.ToStringSlicePreserveString(v) 159 for i, v := range s { 160 s[i] = strings.ToLower(v) 161 } 162 p.c.Outputs[k] = s 163 } 164 // Apply defaults. 165 for k, v := range defaults { 166 if _, found := p.c.Outputs[k]; !found { 167 p.c.Outputs[k] = v 168 } 169 } 170 return nil 171 }, 172 }, 173 "outputformats": { 174 key: "outputformats", 175 decode: func(d decodeWeight, p decodeConfig) error { 176 var err error 177 p.c.OutputFormats, err = output.DecodeConfig(p.c.MediaTypes.Config, p.p.Get(d.key)) 178 return err 179 }, 180 }, 181 "params": { 182 key: "params", 183 decode: func(d decodeWeight, p decodeConfig) error { 184 p.c.Params = maps.CleanConfigStringMap(p.p.GetStringMap("params")) 185 if p.c.Params == nil { 186 p.c.Params = make(map[string]any) 187 } 188 189 // Before Hugo 0.112.0 this was configured via site Params. 190 if mainSections, found := p.c.Params["mainsections"]; found { 191 p.c.MainSections = types.ToStringSlicePreserveString(mainSections) 192 if p.c.MainSections == nil { 193 p.c.MainSections = []string{} 194 } 195 } 196 197 return nil 198 }, 199 }, 200 "module": { 201 key: "module", 202 decode: func(d decodeWeight, p decodeConfig) error { 203 var err error 204 p.c.Module, err = modules.DecodeConfig(p.p) 205 return err 206 }, 207 }, 208 "permalinks": { 209 key: "permalinks", 210 decode: func(d decodeWeight, p decodeConfig) error { 211 var err error 212 p.c.Permalinks, err = page.DecodePermalinksConfig(p.p.GetStringMap(d.key)) 213 return err 214 }, 215 }, 216 "sitemap": { 217 key: "sitemap", 218 decode: func(d decodeWeight, p decodeConfig) error { 219 var err error 220 p.c.Sitemap, err = config.DecodeSitemap(config.SitemapConfig{Priority: -1, Filename: "sitemap.xml"}, p.p.GetStringMap(d.key)) 221 return err 222 }, 223 }, 224 "taxonomies": { 225 key: "taxonomies", 226 decode: func(d decodeWeight, p decodeConfig) error { 227 p.c.Taxonomies = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key)) 228 return nil 229 }, 230 }, 231 "related": { 232 key: "related", 233 weight: 100, // This needs to be decoded after taxonomies. 234 decode: func(d decodeWeight, p decodeConfig) error { 235 if p.p.IsSet(d.key) { 236 var err error 237 p.c.Related, err = related.DecodeConfig(p.p.GetParams(d.key)) 238 if err != nil { 239 return fmt.Errorf("failed to decode related config: %w", err) 240 } 241 } else { 242 p.c.Related = related.DefaultConfig 243 if _, found := p.c.Taxonomies["tag"]; found { 244 p.c.Related.Add(related.IndexConfig{Name: "tags", Weight: 80, Type: related.TypeBasic}) 245 } 246 } 247 return nil 248 }, 249 }, 250 "languages": { 251 key: "languages", 252 decode: func(d decodeWeight, p decodeConfig) error { 253 var err error 254 m := p.p.GetStringMap(d.key) 255 if len(m) == 1 { 256 // In v0.112.4 we moved this to the language config, but it's very commmon for mono language sites to have this at the top level. 257 var first maps.Params 258 var ok bool 259 for _, v := range m { 260 first, ok = v.(maps.Params) 261 if ok { 262 break 263 } 264 } 265 if first != nil { 266 if _, found := first["languagecode"]; !found { 267 first["languagecode"] = p.p.GetString("languagecode") 268 } 269 } 270 } 271 p.c.Languages, err = langs.DecodeConfig(m) 272 if err != nil { 273 return err 274 } 275 276 // Validate defaultContentLanguage. 277 var found bool 278 for lang := range p.c.Languages { 279 if lang == p.c.DefaultContentLanguage { 280 found = true 281 break 282 } 283 } 284 if !found { 285 return fmt.Errorf("config value %q for defaultContentLanguage does not match any language definition", p.c.DefaultContentLanguage) 286 } 287 288 return nil 289 }, 290 }, 291 "cascade": { 292 key: "cascade", 293 decode: func(d decodeWeight, p decodeConfig) error { 294 var err error 295 p.c.Cascade, err = page.DecodeCascadeConfig(nil, p.p.Get(d.key)) 296 return err 297 }, 298 }, 299 "menus": { 300 key: "menus", 301 decode: func(d decodeWeight, p decodeConfig) error { 302 var err error 303 p.c.Menus, err = navigation.DecodeConfig(p.p.Get(d.key)) 304 return err 305 }, 306 }, 307 "privacy": { 308 key: "privacy", 309 decode: func(d decodeWeight, p decodeConfig) error { 310 var err error 311 p.c.Privacy, err = privacy.DecodeConfig(p.p) 312 return err 313 }, 314 }, 315 "security": { 316 key: "security", 317 decode: func(d decodeWeight, p decodeConfig) error { 318 var err error 319 p.c.Security, err = security.DecodeConfig(p.p) 320 return err 321 }, 322 }, 323 "services": { 324 key: "services", 325 decode: func(d decodeWeight, p decodeConfig) error { 326 var err error 327 p.c.Services, err = services.DecodeConfig(p.p) 328 return err 329 }, 330 }, 331 "deployment": { 332 key: "deployment", 333 decode: func(d decodeWeight, p decodeConfig) error { 334 var err error 335 p.c.Deployment, err = deployconfig.DecodeConfig(p.p) 336 return err 337 }, 338 }, 339 "author": { 340 key: "author", 341 decode: func(d decodeWeight, p decodeConfig) error { 342 p.c.Author = maps.CleanConfigStringMap(p.p.GetStringMap(d.key)) 343 return nil 344 }, 345 internalOrDeprecated: true, 346 }, 347 "social": { 348 key: "social", 349 decode: func(d decodeWeight, p decodeConfig) error { 350 p.c.Social = maps.CleanConfigStringMapString(p.p.GetStringMapString(d.key)) 351 return nil 352 }, 353 internalOrDeprecated: true, 354 }, 355 "uglyurls": { 356 key: "uglyurls", 357 decode: func(d decodeWeight, p decodeConfig) error { 358 v := p.p.Get(d.key) 359 switch vv := v.(type) { 360 case bool: 361 p.c.UglyURLs = vv 362 case string: 363 p.c.UglyURLs = vv == "true" 364 default: 365 p.c.UglyURLs = cast.ToStringMapBool(v) 366 } 367 return nil 368 }, 369 internalOrDeprecated: true, 370 }, 371 "internal": { 372 key: "internal", 373 decode: func(d decodeWeight, p decodeConfig) error { 374 return mapstructure.WeakDecode(p.p.GetStringMap(d.key), &p.c.Internal) 375 }, 376 internalOrDeprecated: true, 377 }, 378 } 379 380 func init() { 381 for k, v := range allDecoderSetups { 382 // Verify that k and v.key is all lower case. 383 if k != strings.ToLower(k) { 384 panic(fmt.Sprintf("key %q is not lower case", k)) 385 } 386 if v.key != strings.ToLower(v.key) { 387 panic(fmt.Sprintf("key %q is not lower case", v.key)) 388 } 389 390 if k != v.key { 391 panic(fmt.Sprintf("key %q is not the same as the map key %q", k, v.key)) 392 } 393 } 394 }