mirror of
https://github.com/gohugoio/hugo.git
synced 2025-04-26 05:30:54 +03:00
Create a struct with all of Hugo's config options
Primary motivation is documentation, but it will also hopefully simplify the code. Also, * Lower case the default output format names; this is in line with the custom ones (map keys) and how it's treated all the places. This avoids doing `stringds.EqualFold` everywhere. Closes #10896 Closes #10620
This commit is contained in:
parent
6aededf6b4
commit
241b21b0fd
337 changed files with 13377 additions and 14898 deletions
|
@ -433,9 +433,9 @@ func (c *Client) Clean(pattern string) error {
|
|||
if g != nil && !g.Match(m.Path) {
|
||||
continue
|
||||
}
|
||||
_, err = hugofs.MakeReadableAndRemoveAllModulePkgDir(c.fs, m.Dir)
|
||||
dirCount, err := hugofs.MakeReadableAndRemoveAllModulePkgDir(c.fs, m.Dir)
|
||||
if err == nil {
|
||||
c.logger.Printf("hugo: cleaned module cache for %q", m.Path)
|
||||
c.logger.Printf("hugo: removed %d dirs in module cache for %q", dirCount, m.Path)
|
||||
}
|
||||
}
|
||||
return err
|
||||
|
|
|
@ -52,20 +52,6 @@ func IsNotExist(err error) bool {
|
|||
return errors.Is(err, os.ErrNotExist)
|
||||
}
|
||||
|
||||
// CreateProjectModule creates modules from the given config.
|
||||
// This is used in tests only.
|
||||
func CreateProjectModule(cfg config.Provider) (Module, error) {
|
||||
workingDir := cfg.GetString("workingDir")
|
||||
var modConfig Config
|
||||
|
||||
mod := createProjectModule(nil, workingDir, modConfig)
|
||||
if err := ApplyProjectConfigDefaults(cfg, mod); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mod, nil
|
||||
}
|
||||
|
||||
func (h *Client) Collect() (ModulesConfig, error) {
|
||||
mc, coll := h.collect(true)
|
||||
if coll.err != nil {
|
||||
|
@ -90,6 +76,9 @@ func (h *Client) Collect() (ModulesConfig, error) {
|
|||
}
|
||||
|
||||
func (h *Client) collect(tidy bool) (ModulesConfig, *collector) {
|
||||
if h == nil {
|
||||
panic("nil client")
|
||||
}
|
||||
c := &collector{
|
||||
Client: h,
|
||||
}
|
||||
|
@ -133,6 +122,16 @@ type ModulesConfig struct {
|
|||
GoWorkspaceFilename string
|
||||
}
|
||||
|
||||
func (m ModulesConfig) HasConfigFile() bool {
|
||||
for _, mod := range m.ActiveModules {
|
||||
if len(mod.ConfigFilenames()) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (m *ModulesConfig) setActiveMods(logger loggers.Logger) error {
|
||||
var activeMods Modules
|
||||
for _, mod := range m.AllModules {
|
||||
|
@ -230,6 +229,7 @@ func (c *collector) getVendoredDir(path string) (vendoredModule, bool) {
|
|||
}
|
||||
|
||||
func (c *collector) add(owner *moduleAdapter, moduleImport Import, disabled bool) (*moduleAdapter, error) {
|
||||
|
||||
var (
|
||||
mod *goModule
|
||||
moduleDir string
|
||||
|
@ -299,7 +299,7 @@ func (c *collector) add(owner *moduleAdapter, moduleImport Import, disabled bool
|
|||
return nil, nil
|
||||
}
|
||||
if found, _ := afero.Exists(c.fs, moduleDir); !found {
|
||||
c.err = c.wrapModuleNotFound(fmt.Errorf(`module %q not found; either add it as a Hugo Module or store it in %q.`, modulePath, c.ccfg.ThemesDir))
|
||||
c.err = c.wrapModuleNotFound(fmt.Errorf(`module %q not found in % q; either add it as a Hugo Module or store it in %q.`, modulePath, moduleDir, c.ccfg.ThemesDir))
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
@ -347,7 +347,7 @@ func (c *collector) addAndRecurse(owner *moduleAdapter, disabled bool) error {
|
|||
moduleConfig := owner.Config()
|
||||
if owner.projectMod {
|
||||
if err := c.applyMounts(Import{}, owner); err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to apply mounts for project module: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -618,7 +618,7 @@ func (c *collector) mountCommonJSConfig(owner *moduleAdapter, mounts []Mount) ([
|
|||
// Mount the common JS config files.
|
||||
fis, err := afero.ReadDir(c.fs, owner.Dir())
|
||||
if err != nil {
|
||||
return mounts, err
|
||||
return mounts, fmt.Errorf("failed to read dir %q: %q", owner.Dir(), err)
|
||||
}
|
||||
|
||||
for _, fi := range fis {
|
||||
|
|
|
@ -20,10 +20,9 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hugo"
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
|
@ -58,12 +57,9 @@ var DefaultModuleConfig = Config{
|
|||
|
||||
// ApplyProjectConfigDefaults applies default/missing module configuration for
|
||||
// the main project.
|
||||
func ApplyProjectConfigDefaults(cfg config.Provider, mod Module) error {
|
||||
moda := mod.(*moduleAdapter)
|
||||
func ApplyProjectConfigDefaults(mod Module, cfgs ...config.AllProvider) error {
|
||||
|
||||
// Map legacy directory config into the new module.
|
||||
languages := cfg.Get("languagesSortedDefaultFirst").(langs.Languages)
|
||||
isMultiHost := languages.IsMultihost()
|
||||
moda := mod.(*moduleAdapter)
|
||||
|
||||
// To bridge between old and new configuration format we need
|
||||
// a way to make sure all of the core components are configured on
|
||||
|
@ -75,121 +71,92 @@ func ApplyProjectConfigDefaults(cfg config.Provider, mod Module) error {
|
|||
}
|
||||
}
|
||||
|
||||
type dirKeyComponent struct {
|
||||
key string
|
||||
component string
|
||||
multilingual bool
|
||||
}
|
||||
|
||||
dirKeys := []dirKeyComponent{
|
||||
{"contentDir", files.ComponentFolderContent, true},
|
||||
{"dataDir", files.ComponentFolderData, false},
|
||||
{"layoutDir", files.ComponentFolderLayouts, false},
|
||||
{"i18nDir", files.ComponentFolderI18n, false},
|
||||
{"archetypeDir", files.ComponentFolderArchetypes, false},
|
||||
{"assetDir", files.ComponentFolderAssets, false},
|
||||
{"", files.ComponentFolderStatic, isMultiHost},
|
||||
}
|
||||
|
||||
createMountsFor := func(d dirKeyComponent, cfg config.Provider) []Mount {
|
||||
var lang string
|
||||
if language, ok := cfg.(*langs.Language); ok {
|
||||
lang = language.Lang
|
||||
}
|
||||
|
||||
// Static mounts are a little special.
|
||||
if d.component == files.ComponentFolderStatic {
|
||||
var mounts []Mount
|
||||
staticDirs := getStaticDirs(cfg)
|
||||
if len(staticDirs) > 0 {
|
||||
componentsConfigured[d.component] = true
|
||||
}
|
||||
|
||||
for _, dir := range staticDirs {
|
||||
mounts = append(mounts, Mount{Lang: lang, Source: dir, Target: d.component})
|
||||
}
|
||||
|
||||
return mounts
|
||||
|
||||
}
|
||||
|
||||
if cfg.IsSet(d.key) {
|
||||
source := cfg.GetString(d.key)
|
||||
componentsConfigured[d.component] = true
|
||||
|
||||
return []Mount{{
|
||||
// No lang set for layouts etc.
|
||||
Source: source,
|
||||
Target: d.component,
|
||||
}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
createMounts := func(d dirKeyComponent) []Mount {
|
||||
var mounts []Mount
|
||||
if d.multilingual {
|
||||
if d.component == files.ComponentFolderContent {
|
||||
seen := make(map[string]bool)
|
||||
hasContentDir := false
|
||||
for _, language := range languages {
|
||||
if language.ContentDir != "" {
|
||||
hasContentDir = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if hasContentDir {
|
||||
for _, language := range languages {
|
||||
contentDir := language.ContentDir
|
||||
if contentDir == "" {
|
||||
contentDir = files.ComponentFolderContent
|
||||
}
|
||||
if contentDir == "" || seen[contentDir] {
|
||||
continue
|
||||
}
|
||||
seen[contentDir] = true
|
||||
mounts = append(mounts, Mount{Lang: language.Lang, Source: contentDir, Target: d.component})
|
||||
}
|
||||
}
|
||||
|
||||
componentsConfigured[d.component] = len(seen) > 0
|
||||
|
||||
} else {
|
||||
for _, language := range languages {
|
||||
mounts = append(mounts, createMountsFor(d, language)...)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mounts = append(mounts, createMountsFor(d, cfg)...)
|
||||
}
|
||||
|
||||
return mounts
|
||||
}
|
||||
|
||||
var mounts []Mount
|
||||
for _, dirKey := range dirKeys {
|
||||
if componentsConfigured[dirKey.component] {
|
||||
|
||||
for _, component := range []string{
|
||||
files.ComponentFolderContent,
|
||||
files.ComponentFolderData,
|
||||
files.ComponentFolderLayouts,
|
||||
files.ComponentFolderI18n,
|
||||
files.ComponentFolderArchetypes,
|
||||
files.ComponentFolderAssets,
|
||||
files.ComponentFolderStatic,
|
||||
} {
|
||||
if componentsConfigured[component] {
|
||||
continue
|
||||
}
|
||||
|
||||
mounts = append(mounts, createMounts(dirKey)...)
|
||||
first := cfgs[0]
|
||||
dirsBase := first.DirsBase()
|
||||
isMultiHost := first.IsMultihost()
|
||||
|
||||
for i, cfg := range cfgs {
|
||||
dirs := cfg.Dirs()
|
||||
var dir string
|
||||
var dropLang bool
|
||||
switch component {
|
||||
case files.ComponentFolderContent:
|
||||
dir = dirs.ContentDir
|
||||
dropLang = dir == dirsBase.ContentDir
|
||||
case files.ComponentFolderData:
|
||||
dir = dirs.DataDir
|
||||
case files.ComponentFolderLayouts:
|
||||
dir = dirs.LayoutDir
|
||||
case files.ComponentFolderI18n:
|
||||
dir = dirs.I18nDir
|
||||
case files.ComponentFolderArchetypes:
|
||||
dir = dirs.ArcheTypeDir
|
||||
case files.ComponentFolderAssets:
|
||||
dir = dirs.AssetDir
|
||||
case files.ComponentFolderStatic:
|
||||
// For static dirs, we only care about the language in multihost setups.
|
||||
dropLang = !isMultiHost
|
||||
}
|
||||
|
||||
var perLang bool
|
||||
switch component {
|
||||
case files.ComponentFolderContent, files.ComponentFolderStatic:
|
||||
perLang = true
|
||||
default:
|
||||
}
|
||||
if i > 0 && !perLang {
|
||||
continue
|
||||
}
|
||||
|
||||
var lang string
|
||||
if perLang && !dropLang {
|
||||
lang = cfg.Language().Lang
|
||||
}
|
||||
|
||||
// Static mounts are a little special.
|
||||
if component == files.ComponentFolderStatic {
|
||||
staticDirs := cfg.StaticDirs()
|
||||
for _, dir := range staticDirs {
|
||||
mounts = append(mounts, Mount{Lang: lang, Source: dir, Target: component})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if dir != "" {
|
||||
mounts = append(mounts, Mount{Lang: lang, Source: dir, Target: component})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add default configuration
|
||||
for _, dirKey := range dirKeys {
|
||||
if componentsConfigured[dirKey.component] {
|
||||
moda.mounts = append(moda.mounts, mounts...)
|
||||
|
||||
// Temporary: Remove duplicates.
|
||||
seen := make(map[string]bool)
|
||||
var newMounts []Mount
|
||||
for _, m := range moda.mounts {
|
||||
key := m.Source + m.Target + m.Lang
|
||||
if seen[key] {
|
||||
continue
|
||||
}
|
||||
mounts = append(mounts, Mount{Source: dirKey.component, Target: dirKey.component})
|
||||
seen[key] = true
|
||||
newMounts = append(newMounts, m)
|
||||
}
|
||||
|
||||
// Prepend the mounts from configuration.
|
||||
mounts = append(moda.mounts, mounts...)
|
||||
|
||||
moda.mounts = mounts
|
||||
moda.mounts = newMounts
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -275,7 +242,6 @@ func decodeConfig(cfg config.Provider, pathReplacements map[string]string) (Conf
|
|||
Path: imp,
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return c, nil
|
||||
|
@ -283,7 +249,10 @@ func decodeConfig(cfg config.Provider, pathReplacements map[string]string) (Conf
|
|||
|
||||
// Config holds a module config.
|
||||
type Config struct {
|
||||
Mounts []Mount
|
||||
// File system mounts.
|
||||
Mounts []Mount
|
||||
|
||||
// Module imports.
|
||||
Imports []Import
|
||||
|
||||
// Meta info about this module (license information etc.).
|
||||
|
@ -292,8 +261,7 @@ type Config struct {
|
|||
// Will be validated against the running Hugo version.
|
||||
HugoVersion HugoVersion
|
||||
|
||||
// A optional Glob pattern matching module paths to skip when vendoring, e.g.
|
||||
// "github.com/**".
|
||||
// Optional Glob pattern matching module paths to skip when vendoring, e.g. “github.com/**”
|
||||
NoVendor string
|
||||
|
||||
// When enabled, we will pick the vendored module closest to the module
|
||||
|
@ -303,21 +271,31 @@ type Config struct {
|
|||
// so once it is in use it cannot be redefined.
|
||||
VendorClosest bool
|
||||
|
||||
// A comma separated (or a slice) list of module path to directory replacement mapping,
|
||||
// e.g. github.com/bep/my-theme -> ../..,github.com/bep/shortcodes -> /some/path.
|
||||
// This is mostly useful for temporary locally development of a module, and then it makes sense to set it as an
|
||||
// OS environment variable, e.g: env HUGO_MODULE_REPLACEMENTS="github.com/bep/my-theme -> ../..".
|
||||
// Any relative path is relate to themesDir, and absolute paths are allowed.
|
||||
Replacements []string
|
||||
replacementsMap map[string]string
|
||||
|
||||
// Configures GOPROXY.
|
||||
// Defines the proxy server to use to download remote modules. Default is direct, which means “git clone” and similar.
|
||||
// Configures GOPROXY when running the Go command for module operations.
|
||||
Proxy string
|
||||
// Configures GONOPROXY.
|
||||
|
||||
// Comma separated glob list matching paths that should not use the proxy configured above.
|
||||
// Configures GONOPROXY when running the Go command for module operations.
|
||||
NoProxy string
|
||||
// Configures GOPRIVATE.
|
||||
|
||||
// Comma separated glob list matching paths that should be treated as private.
|
||||
// Configures GOPRIVATE when running the Go command for module operations.
|
||||
Private string
|
||||
|
||||
// Defaults to "off".
|
||||
// Set to a work file, e.g. hugo.work, to enable Go "Workspace" mode.
|
||||
// Can be relative to the working directory or absolute.
|
||||
// Requires Go 1.18+
|
||||
// See https://tip.golang.org/doc/go1.18
|
||||
// Requires Go 1.18+.
|
||||
// Note that this can also be set via OS env, e.g. export HUGO_MODULE_WORKSPACE=/my/hugo.work.
|
||||
Workspace string
|
||||
}
|
||||
|
||||
|
@ -387,21 +365,33 @@ func (v HugoVersion) IsValid() bool {
|
|||
}
|
||||
|
||||
type Import struct {
|
||||
Path string // Module path
|
||||
pathProjectReplaced bool // Set when Path is replaced in project config.
|
||||
IgnoreConfig bool // Ignore any config in config.toml (will still follow imports).
|
||||
IgnoreImports bool // Do not follow any configured imports.
|
||||
NoMounts bool // Do not mount any folder in this import.
|
||||
NoVendor bool // Never vendor this import (only allowed in main project).
|
||||
Disable bool // Turn off this module.
|
||||
Mounts []Mount
|
||||
// Module path
|
||||
Path string
|
||||
// Set when Path is replaced in project config.
|
||||
pathProjectReplaced bool
|
||||
// Ignore any config in config.toml (will still follow imports).
|
||||
IgnoreConfig bool
|
||||
// Do not follow any configured imports.
|
||||
IgnoreImports bool
|
||||
// Do not mount any folder in this import.
|
||||
NoMounts bool
|
||||
// Never vendor this import (only allowed in main project).
|
||||
NoVendor bool
|
||||
// Turn off this module.
|
||||
Disable bool
|
||||
// File mounts.
|
||||
Mounts []Mount
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
Source string // relative path in source repo, e.g. "scss"
|
||||
Target string // relative target path, e.g. "assets/bootstrap/scss"
|
||||
// Relative path in source repo, e.g. "scss".
|
||||
Source string
|
||||
|
||||
Lang string // any language code associated with this mount.
|
||||
// Relative target path, e.g. "assets/bootstrap/scss".
|
||||
Target string
|
||||
|
||||
// Any file in this mount will be associated with this language.
|
||||
Lang string
|
||||
|
||||
// Include only files matching the given Glob patterns (string or slice).
|
||||
IncludeFiles any
|
||||
|
@ -423,19 +413,3 @@ func (m Mount) ComponentAndName() (string, string) {
|
|||
c, n, _ := strings.Cut(m.Target, fileSeparator)
|
||||
return c, n
|
||||
}
|
||||
|
||||
func getStaticDirs(cfg config.Provider) []string {
|
||||
var staticDirs []string
|
||||
for i := -1; i <= 10; i++ {
|
||||
staticDirs = append(staticDirs, getStringOrStringSlice(cfg, "staticDir", i)...)
|
||||
}
|
||||
return staticDirs
|
||||
}
|
||||
|
||||
func getStringOrStringSlice(cfg config.Provider, key string, id int) []string {
|
||||
if id >= 0 {
|
||||
key = fmt.Sprintf("%s%d", key, id)
|
||||
}
|
||||
|
||||
return config.GetStringSlicePreserveString(cfg, key)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue