summaryrefslogtreecommitdiffstats
path: root/modules/config.go
diff options
context:
space:
mode:
Diffstat (limited to 'modules/config.go')
-rw-r--r--modules/config.go335
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)
+
+}