diff options
author | Bjørn Erik Pedersen <[email protected]> | 2023-01-04 18:24:36 +0100 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2023-05-16 18:01:29 +0200 |
commit | 241b21b0fd34d91fccb2ce69874110dceae6f926 (patch) | |
tree | d4e0118eac7e9c42f065815447a70805f8d6ad3e /modules | |
parent | 6aededf6b42011c3039f5f66487a89a8dd65e0e7 (diff) | |
download | hugo-241b21b0fd34d91fccb2ce69874110dceae6f926.tar.gz hugo-241b21b0fd34d91fccb2ce69874110dceae6f926.zip |
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
Diffstat (limited to 'modules')
-rw-r--r-- | modules/client.go | 4 | ||||
-rw-r--r-- | modules/collect.go | 34 | ||||
-rw-r--r-- | modules/config.go | 254 |
3 files changed, 133 insertions, 159 deletions
diff --git a/modules/client.go b/modules/client.go index 1fff787d1..59f6b25d3 100644 --- a/modules/client.go +++ b/modules/client.go @@ -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 diff --git a/modules/collect.go b/modules/collect.go index fcde1d379..c69e7e7ee 100644 --- a/modules/collect.go +++ b/modules/collect.go @@ -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 { diff --git a/modules/config.go b/modules/config.go index 9d516e841..f8faf7969 100644 --- a/modules/config.go +++ b/modules/config.go @@ -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}, - } + var mounts []Mount - createMountsFor := func(d dirKeyComponent, cfg config.Provider) []Mount { - var lang string - if language, ok := cfg.(*langs.Language); ok { - lang = language.Lang + for _, component := range []string{ + files.ComponentFolderContent, + files.ComponentFolderData, + files.ComponentFolderLayouts, + files.ComponentFolderI18n, + files.ComponentFolderArchetypes, + files.ComponentFolderAssets, + files.ComponentFolderStatic, + } { + if componentsConfigured[component] { + continue } - // 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 + 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 } - for _, dir := range staticDirs { - mounts = append(mounts, Mount{Lang: lang, Source: dir, Target: d.component}) + var perLang bool + switch component { + case files.ComponentFolderContent, files.ComponentFolderStatic: + perLang = true + default: + } + if i > 0 && !perLang { + continue } - 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 - } - } + var lang string + if perLang && !dropLang { + lang = cfg.Language().Lang + } - 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}) - } + // 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 + } - componentsConfigured[d.component] = len(seen) > 0 - - } else { - for _, language := range languages { - mounts = append(mounts, createMountsFor(d, language)...) - } + if dir != "" { + mounts = append(mounts, Mount{Lang: lang, Source: dir, Target: component}) } - } else { - mounts = append(mounts, createMountsFor(d, cfg)...) } - - return mounts } - var mounts []Mount - for _, dirKey := range dirKeys { - if componentsConfigured[dirKey.component] { - continue - } - - mounts = append(mounts, createMounts(dirKey)...) + moda.mounts = append(moda.mounts, mounts...) - } - - // Add default configuration - for _, dirKey := range dirKeys { - if componentsConfigured[dirKey.component] { + // 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) -} |