diff options
Diffstat (limited to 'modules/config.go')
-rw-r--r-- | modules/config.go | 335 |
1 files changed, 335 insertions, 0 deletions
diff --git a/modules/config.go b/modules/config.go new file mode 100644 index 000000000..b084863d4 --- /dev/null +++ b/modules/config.go @@ -0,0 +1,335 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package modules + +import ( + "fmt" + "path" + "path/filepath" + "strings" + + "github.com/gohugoio/hugo/common/hugo" + + "github.com/gohugoio/hugo/config" + "github.com/gohugoio/hugo/hugofs/files" + "github.com/gohugoio/hugo/langs" + "github.com/mitchellh/mapstructure" +) + +var DefaultModuleConfig = Config{ + + // Default to direct, which means "git clone" and similar. We + // will investigate proxy settings in more depth later. + // See https://github.com/golang/go/issues/26334 + Proxy: "direct", + + // Comma separated glob list matching paths that should not use the + // proxy configured above. + NoProxy: "none", + + // Comma separated glob list matching paths that should be + // treated as private. + Private: "*.*", +} + +// ApplyProjectConfigDefaults applies default/missing module configuration for +// the main project. +func ApplyProjectConfigDefaults(cfg config.Provider, mod Module) error { + moda := mod.(*moduleAdapter) + + // Map legacy directory config into the new module. + languages := cfg.Get("languagesSortedDefaultFirst").(langs.Languages) + isMultiHost := languages.IsMultihost() + + // To bridge between old and new configuration format we need + // a way to make sure all of the core components are configured on + // the basic level. + componentsConfigured := make(map[string]bool) + for _, mnt := range moda.mounts { + componentsConfigured[mnt.Component()] = true + } + + 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{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] { + + continue + } + + mounts = append(mounts, createMounts(dirKey)...) + + } + + // Add default configuration + for _, dirKey := range dirKeys { + if componentsConfigured[dirKey.component] { + continue + } + mounts = append(mounts, Mount{Source: dirKey.component, Target: dirKey.component}) + } + + // Remove duplicates + seen := make(map[string]bool) + tmp := mounts[:0] + for _, m := range mounts { + key := path.Join(m.Lang, m.Source, m.Target) + if !seen[key] { + tmp = append(tmp, m) + } + seen[key] = true + } + + moda.mounts = tmp + + return nil +} + +// DecodeConfig creates a modules Config from a given Hugo configuration. +func DecodeConfig(cfg config.Provider) (Config, error) { + c := DefaultModuleConfig + + if cfg == nil { + return c, nil + } + + themeSet := cfg.IsSet("theme") + moduleSet := cfg.IsSet("module") + + if moduleSet { + m := cfg.GetStringMap("module") + if err := mapstructure.WeakDecode(m, &c); err != nil { + return c, err + } + + for i, mnt := range c.Mounts { + mnt.Source = filepath.Clean(mnt.Source) + mnt.Target = filepath.Clean(mnt.Target) + c.Mounts[i] = mnt + } + + } + + if themeSet { + imports := config.GetStringSlicePreserveString(cfg, "theme") + for _, imp := range imports { + c.Imports = append(c.Imports, Import{ + Path: imp, + }) + } + + } + + return c, nil +} + +// Config holds a module config. +type Config struct { + Mounts []Mount + Imports []Import + + // Meta info about this module (license information etc.). + Params map[string]interface{} + + // Will be validated against the running Hugo version. + HugoVersion HugoVersion + + // Configures GOPROXY. + Proxy string + // Configures GONOPROXY. + NoProxy string + // Configures GOPRIVATE. + Private string +} + +// HugoVersion holds Hugo binary version requirements for a module. +type HugoVersion struct { + // The minimum Hugo version that this module works with. + Min hugo.VersionString + + // The maxium Hugo version that this module works with. + Max hugo.VersionString + + // Set if the extended version is needed. + Extended bool +} + +func (v HugoVersion) String() string { + extended := "" + if v.Extended { + extended = " extended" + } + + if v.Min != "" && v.Max != "" { + return fmt.Sprintf("%s/%s%s", v.Min, v.Max, extended) + } + + if v.Min != "" { + return fmt.Sprintf("Min %s%s", v.Min, extended) + } + + if v.Max != "" { + return fmt.Sprintf("Max %s%s", v.Max, extended) + } + + return extended +} + +// IsValid reports whether this version is valid compared to the running +// Hugo binary. +func (v HugoVersion) IsValid() bool { + current := hugo.CurrentVersion.Version() + if v.Extended && !hugo.IsExtended { + return false + } + + isValid := true + + if v.Min != "" && current.Compare(v.Min) > 0 { + isValid = false + } + + if v.Max != "" && current.Compare(v.Max) < 0 { + isValid = false + } + + return isValid +} + +type Import struct { + Path string // Module path + IgnoreConfig bool // Ignore any config.toml found. + Disabled bool // Turn off this module. + 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" + + Lang string // any language code associated with this mount. +} + +func (m Mount) Component() string { + return strings.Split(m.Target, fileSeparator)[0] +} + +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) + +} |