github.com/anakojm/hugo-katex@v0.0.0-20231023141351-42d6f5de9c0b/config/allconfig/allconfig.go (about) 1 // Copyright 2023 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 contains the full configuration for Hugo. 15 // <docsmeta>{ "name": "Configuration", "description": "This section holds all configuration options in Hugo." }</docsmeta> 16 package allconfig 17 18 import ( 19 "errors" 20 "fmt" 21 "reflect" 22 "regexp" 23 "sort" 24 "strconv" 25 "strings" 26 "sync" 27 "time" 28 29 "github.com/gohugoio/hugo/cache/filecache" 30 "github.com/gohugoio/hugo/common/loggers" 31 "github.com/gohugoio/hugo/common/maps" 32 "github.com/gohugoio/hugo/common/urls" 33 "github.com/gohugoio/hugo/config" 34 "github.com/gohugoio/hugo/config/privacy" 35 "github.com/gohugoio/hugo/config/security" 36 "github.com/gohugoio/hugo/config/services" 37 "github.com/gohugoio/hugo/deploy" 38 "github.com/gohugoio/hugo/helpers" 39 "github.com/gohugoio/hugo/langs" 40 "github.com/gohugoio/hugo/markup/markup_config" 41 "github.com/gohugoio/hugo/media" 42 "github.com/gohugoio/hugo/minifiers" 43 "github.com/gohugoio/hugo/modules" 44 "github.com/gohugoio/hugo/navigation" 45 "github.com/gohugoio/hugo/output" 46 "github.com/gohugoio/hugo/related" 47 "github.com/gohugoio/hugo/resources/images" 48 "github.com/gohugoio/hugo/resources/kinds" 49 "github.com/gohugoio/hugo/resources/page" 50 "github.com/gohugoio/hugo/resources/page/pagemeta" 51 "github.com/spf13/afero" 52 53 xmaps "golang.org/x/exp/maps" 54 ) 55 56 // InternalConfig is the internal configuration for Hugo, not read from any user provided config file. 57 type InternalConfig struct { 58 // Server mode? 59 Running bool 60 61 Quiet bool 62 Verbose bool 63 Clock string 64 Watch bool 65 LiveReloadPort int 66 } 67 68 // All non-params config keys for language. 69 var configLanguageKeys map[string]bool 70 71 func init() { 72 skip := map[string]bool{ 73 "internal": true, 74 "c": true, 75 "rootconfig": true, 76 } 77 configLanguageKeys = make(map[string]bool) 78 addKeys := func(v reflect.Value) { 79 for i := 0; i < v.NumField(); i++ { 80 name := strings.ToLower(v.Type().Field(i).Name) 81 if skip[name] { 82 continue 83 } 84 configLanguageKeys[name] = true 85 } 86 } 87 addKeys(reflect.ValueOf(Config{})) 88 addKeys(reflect.ValueOf(RootConfig{})) 89 addKeys(reflect.ValueOf(config.CommonDirs{})) 90 addKeys(reflect.ValueOf(langs.LanguageConfig{})) 91 } 92 93 type Config struct { 94 // For internal use only. 95 Internal InternalConfig `mapstructure:"-" json:"-"` 96 // For internal use only. 97 C *ConfigCompiled `mapstructure:"-" json:"-"` 98 99 RootConfig 100 101 // Author information. 102 Author map[string]any 103 104 // Social links. 105 Social map[string]string 106 107 // The build configuration section contains build-related configuration options. 108 // <docsmeta>{"identifiers": ["build"] }</docsmeta> 109 Build config.BuildConfig `mapstructure:"-"` 110 111 // The caches configuration section contains cache-related configuration options. 112 // <docsmeta>{"identifiers": ["caches"] }</docsmeta> 113 Caches filecache.Configs `mapstructure:"-"` 114 115 // The markup configuration section contains markup-related configuration options. 116 // <docsmeta>{"identifiers": ["markup"] }</docsmeta> 117 Markup markup_config.Config `mapstructure:"-"` 118 119 // The mediatypes configuration section maps the MIME type (a string) to a configuration object for that type. 120 // <docsmeta>{"identifiers": ["mediatypes"], "refs": ["types:media:type"] }</docsmeta> 121 MediaTypes *config.ConfigNamespace[map[string]media.MediaTypeConfig, media.Types] `mapstructure:"-"` 122 123 Imaging *config.ConfigNamespace[images.ImagingConfig, images.ImagingConfigInternal] `mapstructure:"-"` 124 125 // The outputformats configuration sections maps a format name (a string) to a configuration object for that format. 126 OutputFormats *config.ConfigNamespace[map[string]output.OutputFormatConfig, output.Formats] `mapstructure:"-"` 127 128 // The outputs configuration section maps a Page Kind (a string) to a slice of output formats. 129 // This can be overridden in the front matter. 130 Outputs map[string][]string `mapstructure:"-"` 131 132 // The cascade configuration section contains the top level front matter cascade configuration options, 133 // a slice of page matcher and params to apply to those pages. 134 Cascade *config.ConfigNamespace[[]page.PageMatcherParamsConfig, map[page.PageMatcher]maps.Params] `mapstructure:"-"` 135 136 // Menu configuration. 137 // <docsmeta>{"refs": ["config:languages:menus"] }</docsmeta> 138 Menus *config.ConfigNamespace[map[string]navigation.MenuConfig, navigation.Menus] `mapstructure:"-"` 139 140 // The deployment configuration section contains for hugo deploy. 141 Deployment deploy.DeployConfig `mapstructure:"-"` 142 143 // Module configuration. 144 Module modules.Config `mapstructure:"-"` 145 146 // Front matter configuration. 147 Frontmatter pagemeta.FrontmatterConfig `mapstructure:"-"` 148 149 // Minification configuration. 150 Minify minifiers.MinifyConfig `mapstructure:"-"` 151 152 // Permalink configuration. 153 Permalinks map[string]map[string]string `mapstructure:"-"` 154 155 // Taxonomy configuration. 156 Taxonomies map[string]string `mapstructure:"-"` 157 158 // Sitemap configuration. 159 Sitemap config.SitemapConfig `mapstructure:"-"` 160 161 // Related content configuration. 162 Related related.Config `mapstructure:"-"` 163 164 // Server configuration. 165 Server config.Server `mapstructure:"-"` 166 167 // Privacy configuration. 168 Privacy privacy.Config `mapstructure:"-"` 169 170 // Security configuration. 171 Security security.Config `mapstructure:"-"` 172 173 // Services configuration. 174 Services services.Config `mapstructure:"-"` 175 176 // User provided parameters. 177 // <docsmeta>{"refs": ["config:languages:params"] }</docsmeta> 178 Params maps.Params `mapstructure:"-"` 179 180 // The languages configuration sections maps a language code (a string) to a configuration object for that language. 181 Languages map[string]langs.LanguageConfig `mapstructure:"-"` 182 183 // UglyURLs configuration. Either a boolean or a sections map. 184 UglyURLs any `mapstructure:"-"` 185 } 186 187 type configCompiler interface { 188 CompileConfig(logger loggers.Logger) error 189 } 190 191 func (c Config) cloneForLang() *Config { 192 x := c 193 x.C = nil 194 copyStringSlice := func(in []string) []string { 195 if in == nil { 196 return nil 197 } 198 out := make([]string, len(in)) 199 copy(out, in) 200 return out 201 } 202 203 // Copy all the slices to avoid sharing. 204 x.DisableKinds = copyStringSlice(x.DisableKinds) 205 x.DisableLanguages = copyStringSlice(x.DisableLanguages) 206 x.MainSections = copyStringSlice(x.MainSections) 207 x.IgnoreErrors = copyStringSlice(x.IgnoreErrors) 208 x.IgnoreFiles = copyStringSlice(x.IgnoreFiles) 209 x.Theme = copyStringSlice(x.Theme) 210 211 // Collapse all static dirs to one. 212 x.StaticDir = x.staticDirs() 213 // These will go away soon ... 214 x.StaticDir0 = nil 215 x.StaticDir1 = nil 216 x.StaticDir2 = nil 217 x.StaticDir3 = nil 218 x.StaticDir4 = nil 219 x.StaticDir5 = nil 220 x.StaticDir6 = nil 221 x.StaticDir7 = nil 222 x.StaticDir8 = nil 223 x.StaticDir9 = nil 224 x.StaticDir10 = nil 225 226 return &x 227 } 228 229 func (c *Config) CompileConfig(logger loggers.Logger) error { 230 var transientErr error 231 s := c.Timeout 232 if _, err := strconv.Atoi(s); err == nil { 233 // A number, assume seconds. 234 s = s + "s" 235 } 236 timeout, err := time.ParseDuration(s) 237 if err != nil { 238 return fmt.Errorf("failed to parse timeout: %s", err) 239 } 240 disabledKinds := make(map[string]bool) 241 for _, kind := range c.DisableKinds { 242 kind = strings.ToLower(kind) 243 if newKind := kinds.IsDeprecatedAndReplacedWith(kind); newKind != "" { 244 logger.Deprecatef(false, "Kind %q used in disableKinds is deprecated, use %q instead.", kind, newKind) 245 // Legacy config. 246 kind = newKind 247 } 248 if kinds.GetKindAny(kind) == "" { 249 logger.Warnf("Unknown kind %q in disableKinds configuration.", kind) 250 continue 251 } 252 disabledKinds[kind] = true 253 } 254 kindOutputFormats := make(map[string]output.Formats) 255 isRssDisabled := disabledKinds["rss"] 256 outputFormats := c.OutputFormats.Config 257 for kind, formats := range c.Outputs { 258 if newKind := kinds.IsDeprecatedAndReplacedWith(kind); newKind != "" { 259 logger.Deprecatef(false, "Kind %q used in outputs configuration is deprecated, use %q instead.", kind, newKind) 260 kind = newKind 261 } 262 if disabledKinds[kind] { 263 continue 264 } 265 if kinds.GetKindAny(kind) == "" { 266 logger.Warnf("Unknown kind %q in outputs configuration.", kind) 267 continue 268 } 269 for _, format := range formats { 270 if isRssDisabled && format == "rss" { 271 // Legacy config. 272 continue 273 } 274 f, found := outputFormats.GetByName(format) 275 if !found { 276 transientErr = fmt.Errorf("unknown output format %q for kind %q", format, kind) 277 continue 278 } 279 kindOutputFormats[kind] = append(kindOutputFormats[kind], f) 280 } 281 } 282 283 disabledLangs := make(map[string]bool) 284 for _, lang := range c.DisableLanguages { 285 if lang == c.DefaultContentLanguage { 286 return fmt.Errorf("cannot disable default content language %q", lang) 287 } 288 disabledLangs[lang] = true 289 } 290 for lang, language := range c.Languages { 291 if language.Disabled { 292 disabledLangs[lang] = true 293 if lang == c.DefaultContentLanguage { 294 return fmt.Errorf("cannot disable default content language %q", lang) 295 } 296 } 297 } 298 299 ignoredErrors := make(map[string]bool) 300 for _, err := range c.IgnoreErrors { 301 ignoredErrors[strings.ToLower(err)] = true 302 } 303 304 baseURL, err := urls.NewBaseURLFromString(c.BaseURL) 305 if err != nil { 306 return err 307 } 308 309 isUglyURL := func(section string) bool { 310 switch v := c.UglyURLs.(type) { 311 case bool: 312 return v 313 case map[string]bool: 314 return v[section] 315 default: 316 return false 317 } 318 } 319 320 ignoreFile := func(s string) bool { 321 return false 322 } 323 if len(c.IgnoreFiles) > 0 { 324 regexps := make([]*regexp.Regexp, len(c.IgnoreFiles)) 325 for i, pattern := range c.IgnoreFiles { 326 var err error 327 regexps[i], err = regexp.Compile(pattern) 328 if err != nil { 329 return fmt.Errorf("failed to compile ignoreFiles pattern %q: %s", pattern, err) 330 } 331 } 332 ignoreFile = func(s string) bool { 333 for _, r := range regexps { 334 if r.MatchString(s) { 335 return true 336 } 337 } 338 return false 339 } 340 } 341 342 var clock time.Time 343 if c.Internal.Clock != "" { 344 var err error 345 clock, err = time.Parse(time.RFC3339, c.Internal.Clock) 346 if err != nil { 347 return fmt.Errorf("failed to parse clock: %s", err) 348 } 349 } 350 351 c.C = &ConfigCompiled{ 352 Timeout: timeout, 353 BaseURL: baseURL, 354 BaseURLLiveReload: baseURL, 355 DisabledKinds: disabledKinds, 356 DisabledLanguages: disabledLangs, 357 IgnoredErrors: ignoredErrors, 358 KindOutputFormats: kindOutputFormats, 359 CreateTitle: helpers.GetTitleFunc(c.TitleCaseStyle), 360 IsUglyURLSection: isUglyURL, 361 IgnoreFile: ignoreFile, 362 MainSections: c.MainSections, 363 Clock: clock, 364 transientErr: transientErr, 365 } 366 367 for _, s := range allDecoderSetups { 368 if getCompiler := s.getCompiler; getCompiler != nil { 369 if err := getCompiler(c).CompileConfig(logger); err != nil { 370 return err 371 } 372 } 373 } 374 375 return nil 376 } 377 378 func (c *Config) IsKindEnabled(kind string) bool { 379 return !c.C.DisabledKinds[kind] 380 } 381 382 func (c *Config) IsLangDisabled(lang string) bool { 383 return c.C.DisabledLanguages[lang] 384 } 385 386 // ConfigCompiled holds values and functions that are derived from the config. 387 type ConfigCompiled struct { 388 Timeout time.Duration 389 BaseURL urls.BaseURL 390 BaseURLLiveReload urls.BaseURL 391 KindOutputFormats map[string]output.Formats 392 DisabledKinds map[string]bool 393 DisabledLanguages map[string]bool 394 IgnoredErrors map[string]bool 395 CreateTitle func(s string) string 396 IsUglyURLSection func(section string) bool 397 IgnoreFile func(filename string) bool 398 MainSections []string 399 Clock time.Time 400 401 // This is set to the last transient error found during config compilation. 402 // With themes/modules we compute the configuration in multiple passes, and 403 // errors with missing output format definitions may resolve itself. 404 transientErr error 405 406 mu sync.Mutex 407 } 408 409 // This may be set after the config is compiled. 410 func (c *ConfigCompiled) SetMainSectionsIfNotSet(sections []string) { 411 c.mu.Lock() 412 defer c.mu.Unlock() 413 if c.MainSections != nil { 414 return 415 } 416 c.MainSections = sections 417 } 418 419 // This is set after the config is compiled by the server command. 420 func (c *ConfigCompiled) SetBaseURL(baseURL, baseURLLiveReload urls.BaseURL) { 421 c.BaseURL = baseURL 422 c.BaseURLLiveReload = baseURLLiveReload 423 } 424 425 // RootConfig holds all the top-level configuration options in Hugo 426 type RootConfig struct { 427 428 // The base URL of the site. 429 // Note that the default value is empty, but Hugo requires a valid URL (e.g. "https://example.com/") to work properly. 430 // <docsmeta>{"identifiers": ["URL"] }</docsmeta> 431 BaseURL string 432 433 // Whether to build content marked as draft.X 434 // <docsmeta>{"identifiers": ["draft"] }</docsmeta> 435 BuildDrafts bool 436 437 // Whether to build content with expiryDate in the past. 438 // <docsmeta>{"identifiers": ["expiryDate"] }</docsmeta> 439 BuildExpired bool 440 441 // Whether to build content with publishDate in the future. 442 // <docsmeta>{"identifiers": ["publishDate"] }</docsmeta> 443 BuildFuture bool 444 445 // Copyright information. 446 Copyright string 447 448 // The language to apply to content without any language indicator. 449 DefaultContentLanguage string 450 451 // By default, we put the default content language in the root and the others below their language ID, e.g. /no/. 452 // Set this to true to put all languages below their language ID. 453 DefaultContentLanguageInSubdir bool 454 455 // Disable creation of alias redirect pages. 456 DisableAliases bool 457 458 // Disable lower casing of path segments. 459 DisablePathToLower bool 460 461 // Disable page kinds from build. 462 DisableKinds []string 463 464 // A list of languages to disable. 465 DisableLanguages []string 466 467 // Disable the injection of the Hugo generator tag on the home page. 468 DisableHugoGeneratorInject bool 469 470 // Disable live reloading in server mode. 471 DisableLiveReload bool 472 473 // Enable replacement in Pages' Content of Emoji shortcodes with their equivalent Unicode characters. 474 // <docsmeta>{"identifiers": ["Content", "Unicode"] }</docsmeta> 475 EnableEmoji bool 476 477 // THe main section(s) of the site. 478 // If not set, Hugo will try to guess this from the content. 479 MainSections []string 480 481 // Enable robots.txt generation. 482 EnableRobotsTXT bool 483 484 // When enabled, Hugo will apply Git version information to each Page if possible, which 485 // can be used to keep lastUpdated in synch and to print version information. 486 // <docsmeta>{"identifiers": ["Page"] }</docsmeta> 487 EnableGitInfo bool 488 489 // Enable to track, calculate and print metrics. 490 TemplateMetrics bool 491 492 // Enable to track, print and calculate metric hints. 493 TemplateMetricsHints bool 494 495 // Enable to disable the build lock file. 496 NoBuildLock bool 497 498 // A list of error IDs to ignore. 499 IgnoreErrors []string 500 501 // A list of regexps that match paths to ignore. 502 // Deprecated: Use the settings on module imports. 503 IgnoreFiles []string 504 505 // Ignore cache. 506 IgnoreCache bool 507 508 // Enable to print greppable placeholders (on the form "[i18n] TRANSLATIONID") for missing translation strings. 509 EnableMissingTranslationPlaceholders bool 510 511 // Enable to panic on warning log entries. This may make it easier to detect the source. 512 PanicOnWarning bool 513 514 // The configured environment. Default is "development" for server and "production" for build. 515 Environment string 516 517 // The default language code. 518 LanguageCode string 519 520 // Enable if the site content has CJK language (Chinese, Japanese, or Korean). This affects how Hugo counts words. 521 HasCJKLanguage bool 522 523 // The default number of pages per page when paginating. 524 Paginate int 525 526 // The path to use when creating pagination URLs, e.g. "page" in /page/2/. 527 PaginatePath string 528 529 // Whether to pluralize default list titles. 530 // Note that this currently only works for English, but you can provide your own title in the content file's front matter. 531 PluralizeListTitles bool 532 533 // Make all relative URLs absolute using the baseURL. 534 // <docsmeta>{"identifiers": ["baseURL"] }</docsmeta> 535 CanonifyURLs bool 536 537 // Enable this to make all relative URLs relative to content root. Note that this does not affect absolute URLs. 538 RelativeURLs bool 539 540 // Removes non-spacing marks from composite characters in content paths. 541 RemovePathAccents bool 542 543 // Whether to track and print unused templates during the build. 544 PrintUnusedTemplates bool 545 546 // Enable to print warnings for missing translation strings. 547 PrintI18nWarnings bool 548 549 // ENable to print warnings for multiple files published to the same destination. 550 PrintPathWarnings bool 551 552 // URL to be used as a placeholder when a page reference cannot be found in ref or relref. Is used as-is. 553 RefLinksNotFoundURL string 554 555 // When using ref or relref to resolve page links and a link cannot be resolved, it will be logged with this log level. 556 // Valid values are ERROR (default) or WARNING. Any ERROR will fail the build (exit -1). 557 RefLinksErrorLevel string 558 559 // This will create a menu with all the sections as menu items and all the sections’ pages as “shadow-members”. 560 SectionPagesMenu string 561 562 // The length of text in words to show in a .Summary. 563 SummaryLength int 564 565 // The site title. 566 Title string 567 568 // The theme(s) to use. 569 // See Modules for more a more flexible way to load themes. 570 Theme []string 571 572 // Timeout for generating page contents, specified as a duration or in seconds. 573 Timeout string 574 575 // The time zone (or location), e.g. Europe/Oslo, used to parse front matter dates without such information and in the time function. 576 TimeZone string 577 578 // Set titleCaseStyle to specify the title style used by the title template function and the automatic section titles in Hugo. 579 // It defaults to AP Stylebook for title casing, but you can also set it to Chicago or Go (every word starts with a capital letter). 580 TitleCaseStyle string 581 582 // The editor used for opening up new content. 583 NewContentEditor string 584 585 // Don't sync modification time of files for the static mounts. 586 NoTimes bool 587 588 // Don't sync modification time of files for the static mounts. 589 NoChmod bool 590 591 // Clean the destination folder before a new build. 592 // This currently only handles static files. 593 CleanDestinationDir bool 594 595 // A Glob pattern of module paths to ignore in the _vendor folder. 596 IgnoreVendorPaths string 597 598 config.CommonDirs `mapstructure:",squash"` 599 600 // The odd constructs below are kept for backwards compatibility. 601 // Deprecated: Use module mount config instead. 602 StaticDir []string 603 // Deprecated: Use module mount config instead. 604 StaticDir0 []string 605 // Deprecated: Use module mount config instead. 606 StaticDir1 []string 607 // Deprecated: Use module mount config instead. 608 StaticDir2 []string 609 // Deprecated: Use module mount config instead. 610 StaticDir3 []string 611 // Deprecated: Use module mount config instead. 612 StaticDir4 []string 613 // Deprecated: Use module mount config instead. 614 StaticDir5 []string 615 // Deprecated: Use module mount config instead. 616 StaticDir6 []string 617 // Deprecated: Use module mount config instead. 618 StaticDir7 []string 619 // Deprecated: Use module mount config instead. 620 StaticDir8 []string 621 // Deprecated: Use module mount config instead. 622 StaticDir9 []string 623 // Deprecated: Use module mount config instead. 624 StaticDir10 []string 625 } 626 627 func (c RootConfig) staticDirs() []string { 628 var dirs []string 629 dirs = append(dirs, c.StaticDir...) 630 dirs = append(dirs, c.StaticDir0...) 631 dirs = append(dirs, c.StaticDir1...) 632 dirs = append(dirs, c.StaticDir2...) 633 dirs = append(dirs, c.StaticDir3...) 634 dirs = append(dirs, c.StaticDir4...) 635 dirs = append(dirs, c.StaticDir5...) 636 dirs = append(dirs, c.StaticDir6...) 637 dirs = append(dirs, c.StaticDir7...) 638 dirs = append(dirs, c.StaticDir8...) 639 dirs = append(dirs, c.StaticDir9...) 640 dirs = append(dirs, c.StaticDir10...) 641 return helpers.UniqueStringsReuse(dirs) 642 } 643 644 type Configs struct { 645 Base *Config 646 LoadingInfo config.LoadConfigResult 647 LanguageConfigMap map[string]*Config 648 LanguageConfigSlice []*Config 649 650 IsMultihost bool 651 Languages langs.Languages 652 LanguagesDefaultFirst langs.Languages 653 654 Modules modules.Modules 655 ModulesClient *modules.Client 656 657 configLangs []config.AllProvider 658 } 659 660 // transientErr returns the last transient error found during config compilation. 661 func (c *Configs) transientErr() error { 662 for _, l := range c.LanguageConfigSlice { 663 if l.C.transientErr != nil { 664 return l.C.transientErr 665 } 666 } 667 return nil 668 } 669 670 func (c *Configs) IsZero() bool { 671 // A config always has at least one language. 672 return c == nil || len(c.Languages) == 0 673 } 674 675 func (c *Configs) Init() error { 676 c.configLangs = make([]config.AllProvider, len(c.Languages)) 677 for i, l := range c.LanguagesDefaultFirst { 678 c.configLangs[i] = ConfigLanguage{ 679 m: c, 680 config: c.LanguageConfigMap[l.Lang], 681 baseConfig: c.LoadingInfo.BaseConfig, 682 language: l, 683 } 684 } 685 686 if len(c.Modules) == 0 { 687 return errors.New("no modules loaded (ned at least the main module)") 688 } 689 690 // Apply default project mounts. 691 if err := modules.ApplyProjectConfigDefaults(c.Modules[0], c.configLangs...); err != nil { 692 return err 693 } 694 695 // We should consolidate this, but to get a full view of the mounts in e.g. "hugo config" we need to 696 // transfer any default mounts added above to the config used to print the config. 697 for _, m := range c.Modules[0].Mounts() { 698 var found bool 699 for _, cm := range c.Base.Module.Mounts { 700 if cm.Source == m.Source && cm.Target == m.Target && cm.Lang == m.Lang { 701 found = true 702 break 703 } 704 } 705 if !found { 706 c.Base.Module.Mounts = append(c.Base.Module.Mounts, m) 707 } 708 } 709 710 // Transfer the changed mounts to the language versions (all share the same mount set, but can be displayed in different languages). 711 for _, l := range c.LanguageConfigSlice { 712 l.Module.Mounts = c.Base.Module.Mounts 713 } 714 715 return nil 716 } 717 718 func (c Configs) ConfigLangs() []config.AllProvider { 719 return c.configLangs 720 } 721 722 func (c Configs) GetFirstLanguageConfig() config.AllProvider { 723 return c.configLangs[0] 724 } 725 726 func (c Configs) GetByLang(lang string) config.AllProvider { 727 for _, l := range c.configLangs { 728 if l.Language().Lang == lang { 729 return l 730 } 731 } 732 return nil 733 } 734 735 // fromLoadConfigResult creates a new Config from res. 736 func fromLoadConfigResult(fs afero.Fs, logger loggers.Logger, res config.LoadConfigResult) (*Configs, error) { 737 if !res.Cfg.IsSet("languages") { 738 // We need at least one 739 lang := res.Cfg.GetString("defaultContentLanguage") 740 res.Cfg.Set("languages", maps.Params{lang: maps.Params{}}) 741 } 742 bcfg := res.BaseConfig 743 cfg := res.Cfg 744 745 all := &Config{} 746 747 err := decodeConfigFromParams(fs, logger, bcfg, cfg, all, nil) 748 if err != nil { 749 return nil, err 750 } 751 752 langConfigMap := make(map[string]*Config) 753 var langConfigs []*Config 754 755 languagesConfig := cfg.GetStringMap("languages") 756 var isMultiHost bool 757 758 if err := all.CompileConfig(logger); err != nil { 759 return nil, err 760 } 761 762 for k, v := range languagesConfig { 763 mergedConfig := config.New() 764 var differentRootKeys []string 765 switch x := v.(type) { 766 case maps.Params: 767 var params maps.Params 768 pv, found := x["params"] 769 if found { 770 params = pv.(maps.Params) 771 } else { 772 params = maps.Params{ 773 maps.MergeStrategyKey: maps.ParamsMergeStrategyDeep, 774 } 775 x["params"] = params 776 } 777 778 for kk, vv := range x { 779 if kk == "_merge" { 780 continue 781 } 782 if kk != maps.MergeStrategyKey && !configLanguageKeys[kk] { 783 // This should have been placed below params. 784 // We accidentally allowed it in the past, so we need to support it a little longer, 785 // But log a warning. 786 if _, found := params[kk]; !found { 787 helpers.Deprecated(fmt.Sprintf("config: languages.%s.%s: custom params on the language top level", k, kk), fmt.Sprintf("Put the value below [languages.%s.params]. See https://gohugo.io/content-management/multilingual/#changes-in-hugo-01120", k), false) 788 params[kk] = vv 789 } 790 } 791 if kk == "baseurl" { 792 // baseURL configure don the language level is a multihost setup. 793 isMultiHost = true 794 } 795 mergedConfig.Set(kk, vv) 796 rootv := cfg.Get(kk) 797 if rootv != nil && cfg.IsSet(kk) { 798 // This overrides a root key and potentially needs a merge. 799 if !reflect.DeepEqual(rootv, vv) { 800 switch vvv := vv.(type) { 801 case maps.Params: 802 differentRootKeys = append(differentRootKeys, kk) 803 804 // Use the language value as base. 805 mergedConfigEntry := xmaps.Clone(vvv) 806 // Merge in the root value. 807 maps.MergeParams(mergedConfigEntry, rootv.(maps.Params)) 808 809 mergedConfig.Set(kk, mergedConfigEntry) 810 default: 811 // Apply new values to the root. 812 differentRootKeys = append(differentRootKeys, "") 813 } 814 } 815 } else { 816 switch vv.(type) { 817 case maps.Params: 818 differentRootKeys = append(differentRootKeys, kk) 819 default: 820 // Apply new values to the root. 821 differentRootKeys = append(differentRootKeys, "") 822 } 823 } 824 } 825 differentRootKeys = helpers.UniqueStringsSorted(differentRootKeys) 826 827 if len(differentRootKeys) == 0 { 828 langConfigMap[k] = all 829 continue 830 } 831 832 // Create a copy of the complete config and replace the root keys with the language specific ones. 833 clone := all.cloneForLang() 834 835 if err := decodeConfigFromParams(fs, logger, bcfg, mergedConfig, clone, differentRootKeys); err != nil { 836 return nil, fmt.Errorf("failed to decode config for language %q: %w", k, err) 837 } 838 if err := clone.CompileConfig(logger); err != nil { 839 return nil, err 840 } 841 842 langConfigMap[k] = clone 843 case maps.ParamsMergeStrategy: 844 default: 845 panic(fmt.Sprintf("unknown type in languages config: %T", v)) 846 847 } 848 } 849 850 var languages langs.Languages 851 defaultContentLanguage := all.DefaultContentLanguage 852 for k, v := range langConfigMap { 853 languageConf := v.Languages[k] 854 language, err := langs.NewLanguage(k, defaultContentLanguage, v.TimeZone, languageConf) 855 if err != nil { 856 return nil, err 857 } 858 languages = append(languages, language) 859 } 860 861 // Sort the sites by language weight (if set) or lang. 862 sort.Slice(languages, func(i, j int) bool { 863 li := languages[i] 864 lj := languages[j] 865 if li.Weight != lj.Weight { 866 return li.Weight < lj.Weight 867 } 868 return li.Lang < lj.Lang 869 }) 870 871 for _, l := range languages { 872 langConfigs = append(langConfigs, langConfigMap[l.Lang]) 873 } 874 875 var languagesDefaultFirst langs.Languages 876 for _, l := range languages { 877 if l.Lang == defaultContentLanguage { 878 languagesDefaultFirst = append(languagesDefaultFirst, l) 879 } 880 } 881 for _, l := range languages { 882 if l.Lang != defaultContentLanguage { 883 languagesDefaultFirst = append(languagesDefaultFirst, l) 884 } 885 } 886 887 bcfg.PublishDir = all.PublishDir 888 res.BaseConfig = bcfg 889 all.CommonDirs.CacheDir = bcfg.CacheDir 890 for _, l := range langConfigs { 891 l.CommonDirs.CacheDir = bcfg.CacheDir 892 } 893 894 cm := &Configs{ 895 Base: all, 896 LanguageConfigMap: langConfigMap, 897 LanguageConfigSlice: langConfigs, 898 LoadingInfo: res, 899 IsMultihost: isMultiHost, 900 Languages: languages, 901 LanguagesDefaultFirst: languagesDefaultFirst, 902 } 903 904 return cm, nil 905 } 906 907 func decodeConfigFromParams(fs afero.Fs, logger loggers.Logger, bcfg config.BaseConfig, p config.Provider, target *Config, keys []string) error { 908 909 var decoderSetups []decodeWeight 910 911 if len(keys) == 0 { 912 for _, v := range allDecoderSetups { 913 decoderSetups = append(decoderSetups, v) 914 } 915 } else { 916 for _, key := range keys { 917 if v, found := allDecoderSetups[key]; found { 918 decoderSetups = append(decoderSetups, v) 919 } else { 920 logger.Warnf("Skip unknown config key %q", key) 921 } 922 } 923 } 924 925 // Sort them to get the dependency order right. 926 sort.Slice(decoderSetups, func(i, j int) bool { 927 ki, kj := decoderSetups[i], decoderSetups[j] 928 if ki.weight == kj.weight { 929 return ki.key < kj.key 930 } 931 return ki.weight < kj.weight 932 }) 933 934 for _, v := range decoderSetups { 935 p := decodeConfig{p: p, c: target, fs: fs, bcfg: bcfg} 936 if err := v.decode(v, p); err != nil { 937 return fmt.Errorf("failed to decode %q: %w", v.key, err) 938 } 939 } 940 941 return nil 942 } 943 944 func createDefaultOutputFormats(allFormats output.Formats) map[string][]string { 945 if len(allFormats) == 0 { 946 panic("no output formats") 947 } 948 rssOut, rssFound := allFormats.GetByName(output.RSSFormat.Name) 949 htmlOut, _ := allFormats.GetByName(output.HTMLFormat.Name) 950 951 defaultListTypes := []string{htmlOut.Name} 952 if rssFound { 953 defaultListTypes = append(defaultListTypes, rssOut.Name) 954 } 955 956 m := map[string][]string{ 957 kinds.KindPage: {htmlOut.Name}, 958 kinds.KindHome: defaultListTypes, 959 kinds.KindSection: defaultListTypes, 960 kinds.KindTerm: defaultListTypes, 961 kinds.KindTaxonomy: defaultListTypes, 962 } 963 964 // May be disabled 965 if rssFound { 966 m["rss"] = []string{rssOut.Name} 967 } 968 969 return m 970 }