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  }