mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-07 20:30:36 -05:00
Misc config loading fixes
The main motivation behind this is simplicity and correctnes, but the new small config library is also faster: ``` BenchmarkDefaultConfigProvider/Viper-16 252418 4546 ns/op 2720 B/op 30 allocs/op BenchmarkDefaultConfigProvider/Custom-16 450756 2651 ns/op 1008 B/op 6 allocs/op ``` Fixes #8633 Fixes #8618 Fixes #8630 Updates #8591 Closes #6680 Closes #5192
This commit is contained in:
parent
a886dd53b8
commit
d392893cd7
107 changed files with 2159 additions and 1060 deletions
7
cache/filecache/filecache_config.go
vendored
7
cache/filecache/filecache_config.go
vendored
|
@ -19,6 +19,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
@ -123,6 +125,9 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
|
|||
_, isOsFs := fs.(*afero.OsFs)
|
||||
|
||||
for k, v := range m {
|
||||
if _, ok := v.(maps.Params); !ok {
|
||||
continue
|
||||
}
|
||||
cc := defaultCacheConfig
|
||||
|
||||
dc := &mapstructure.DecoderConfig{
|
||||
|
@ -137,7 +142,7 @@ func DecodeConfig(fs afero.Fs, cfg config.Provider) (Configs, error) {
|
|||
}
|
||||
|
||||
if err := decoder.Decode(v); err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to decode filecache config")
|
||||
}
|
||||
|
||||
if cc.Dir == "" {
|
||||
|
|
5
cache/filecache/filecache_config_test.go
vendored
5
cache/filecache/filecache_config_test.go
vendored
|
@ -25,7 +25,6 @@ import (
|
|||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestDecodeConfig(t *testing.T) {
|
||||
|
@ -178,8 +177,8 @@ dir = "/"
|
|||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
}
|
||||
|
||||
func newTestConfig() *viper.Viper {
|
||||
cfg := viper.New()
|
||||
func newTestConfig() config.Provider {
|
||||
cfg := config.New()
|
||||
cfg.Set("workingDir", filepath.FromSlash("/my/cool/hugoproject"))
|
||||
cfg.Set("contentDir", "content")
|
||||
cfg.Set("dataDir", "data")
|
||||
|
|
|
@ -410,7 +410,5 @@ func (c *commandeer) loadConfig(mustHaveConfigFile, running bool) error {
|
|||
}
|
||||
config.Set("cacheDir", cacheDir)
|
||||
|
||||
cfg.Logger.Infoln("Using config file:", config.ConfigFileUsed())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
@ -29,7 +31,6 @@ import (
|
|||
"github.com/gohugoio/hugo/common/types"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
@ -166,7 +167,7 @@ func TestFlags(t *testing.T) {
|
|||
name: "ignoreVendor as bool",
|
||||
args: []string{"server", "--ignoreVendor"},
|
||||
check: func(c *qt.C, cmd *serverCmd) {
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cmd.flagsToConfig(cfg)
|
||||
c.Assert(cfg.Get("ignoreVendor"), qt.Equals, true)
|
||||
},
|
||||
|
@ -176,7 +177,7 @@ func TestFlags(t *testing.T) {
|
|||
name: "ignoreVendorPaths",
|
||||
args: []string{"server", "--ignoreVendorPaths=github.com/**"},
|
||||
check: func(c *qt.C, cmd *serverCmd) {
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cmd.flagsToConfig(cfg)
|
||||
c.Assert(cfg.Get("ignoreVendorPaths"), qt.Equals, "github.com/**")
|
||||
},
|
||||
|
@ -216,7 +217,7 @@ func TestFlags(t *testing.T) {
|
|||
c.Assert(sc.serverPort, qt.Equals, 1366)
|
||||
c.Assert(sc.environment, qt.Equals, "testing")
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
sc.flagsToConfig(cfg)
|
||||
c.Assert(cfg.GetString("publishDir"), qt.Equals, "/tmp/mydestination")
|
||||
c.Assert(cfg.GetString("contentDir"), qt.Equals, "mycontent")
|
||||
|
|
|
@ -22,13 +22,14 @@ import (
|
|||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var _ cmder = (*configCmd)(nil)
|
||||
|
@ -81,7 +82,7 @@ func (c *configCmd) printConfig(cmd *cobra.Command, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
allSettings := cfg.Cfg.(*viper.Viper).AllSettings()
|
||||
allSettings := cfg.Cfg.Get("").(maps.Params)
|
||||
|
||||
// We need to clean up this, but we store objects in the config that
|
||||
// isn't really interesting to the end user, so filter these.
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
|
||||
_errors "github.com/pkg/errors"
|
||||
|
@ -29,7 +30,6 @@ import (
|
|||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/spf13/cobra"
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var _ cmder = (*newSiteCmd)(nil)
|
||||
|
@ -123,7 +123,7 @@ func (n *newSiteCmd) newSite(cmd *cobra.Command, args []string) error {
|
|||
|
||||
forceNew, _ := cmd.Flags().GetBool("force")
|
||||
|
||||
return n.doNewSite(hugofs.NewDefault(viper.New()), createpath, forceNew)
|
||||
return n.doNewSite(hugofs.NewDefault(config.New()), createpath, forceNew)
|
||||
}
|
||||
|
||||
func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
|
||||
|
|
|
@ -22,10 +22,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestServer(t *testing.T) {
|
||||
|
@ -101,7 +101,7 @@ func TestFixURL(t *testing.T) {
|
|||
t.Run(test.TestName, func(t *testing.T) {
|
||||
b := newCommandsBuilder()
|
||||
s := b.newServerCmd()
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
baseURL := test.CLIBaseURL
|
||||
v.Set("baseURL", test.CfgBaseURL)
|
||||
s.serverAppend = test.AppendPort
|
||||
|
|
|
@ -18,53 +18,65 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/gobwas/glob"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// ToLower makes all the keys in the given map lower cased and will do so
|
||||
// recursively.
|
||||
// Notes:
|
||||
// * This will modify the map given.
|
||||
// * Any nested map[interface{}]interface{} will be converted to Params.
|
||||
func ToLower(m Params) {
|
||||
for k, v := range m {
|
||||
var retyped bool
|
||||
switch v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
var p Params = cast.ToStringMap(v)
|
||||
v = p
|
||||
ToLower(p)
|
||||
retyped = true
|
||||
case map[string]interface{}:
|
||||
var p Params = v.(map[string]interface{})
|
||||
v = p
|
||||
ToLower(p)
|
||||
retyped = true
|
||||
}
|
||||
|
||||
lKey := strings.ToLower(k)
|
||||
if retyped || k != lKey {
|
||||
delete(m, k)
|
||||
m[lKey] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ToStringMapE converts in to map[string]interface{}.
|
||||
func ToStringMapE(in interface{}) (map[string]interface{}, error) {
|
||||
switch in.(type) {
|
||||
switch vv := in.(type) {
|
||||
case Params:
|
||||
return in.(Params), nil
|
||||
return vv, nil
|
||||
case map[string]string:
|
||||
var m = map[string]interface{}{}
|
||||
for k, v := range vv {
|
||||
m[k] = v
|
||||
}
|
||||
return m, nil
|
||||
|
||||
default:
|
||||
return cast.ToStringMapE(in)
|
||||
}
|
||||
}
|
||||
|
||||
// ToParamsAndPrepare converts in to Params and prepares it for use.
|
||||
// See PrepareParams.
|
||||
func ToParamsAndPrepare(in interface{}) (Params, bool) {
|
||||
m, err := ToStringMapE(in)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
PrepareParams(m)
|
||||
return m, true
|
||||
}
|
||||
|
||||
// ToStringMap converts in to map[string]interface{}.
|
||||
func ToStringMap(in interface{}) map[string]interface{} {
|
||||
m, _ := ToStringMapE(in)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToStringMapStringE converts in to map[string]string.
|
||||
func ToStringMapStringE(in interface{}) (map[string]string, error) {
|
||||
m, err := ToStringMapE(in)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cast.ToStringMapStringE(m)
|
||||
}
|
||||
|
||||
// ToStringMapString converts in to map[string]string.
|
||||
func ToStringMapString(in interface{}) map[string]string {
|
||||
m, _ := ToStringMapStringE(in)
|
||||
return m
|
||||
}
|
||||
|
||||
// ToStringMapBool converts in to bool.
|
||||
func ToStringMapBool(in interface{}) map[string]bool {
|
||||
m, _ := ToStringMapE(in)
|
||||
return cast.ToStringMapBool(m)
|
||||
}
|
||||
|
||||
// ToSliceStringMap converts in to []map[string]interface{}.
|
||||
func ToSliceStringMap(in interface{}) ([]map[string]interface{}, error) {
|
||||
switch v := in.(type) {
|
||||
case []map[string]interface{}:
|
||||
|
@ -127,9 +139,8 @@ func (KeyRenamer) keyPath(k1, k2 string) string {
|
|||
k1, k2 = strings.ToLower(k1), strings.ToLower(k2)
|
||||
if k1 == "" {
|
||||
return k2
|
||||
} else {
|
||||
return k1 + "/" + k2
|
||||
}
|
||||
return k1 + "/" + k2
|
||||
}
|
||||
|
||||
func (r KeyRenamer) renamePath(parentKeyPath string, m map[string]interface{}) {
|
||||
|
|
|
@ -67,7 +67,7 @@ func TestToLower(t *testing.T) {
|
|||
for i, test := range tests {
|
||||
t.Run(fmt.Sprint(i), func(t *testing.T) {
|
||||
// ToLower modifies input.
|
||||
ToLower(test.input)
|
||||
PrepareParams(test.input)
|
||||
if !reflect.DeepEqual(test.expected, test.input) {
|
||||
t.Errorf("[%d] Expected\n%#v, got\n%#v\n", i, test.expected, test.input)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package maps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
|
@ -29,6 +30,95 @@ func (p Params) Get(indices ...string) interface{} {
|
|||
return v
|
||||
}
|
||||
|
||||
// Set overwrites values in p with values in pp for common or new keys.
|
||||
// This is done recursively.
|
||||
func (p Params) Set(pp Params) {
|
||||
for k, v := range pp {
|
||||
vv, found := p[k]
|
||||
if !found {
|
||||
p[k] = v
|
||||
} else {
|
||||
switch vvv := vv.(type) {
|
||||
case Params:
|
||||
if pv, ok := v.(Params); ok {
|
||||
vvv.Set(pv)
|
||||
} else {
|
||||
p[k] = v
|
||||
}
|
||||
default:
|
||||
p[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Merge transfers values from pp to p for new keys.
|
||||
// This is done recursively.
|
||||
func (p Params) Merge(pp Params) {
|
||||
p.merge("", pp)
|
||||
}
|
||||
|
||||
func (p Params) merge(ps ParamsMergeStrategy, pp Params) {
|
||||
ns, found := p.GetMergeStrategy()
|
||||
|
||||
var ms = ns
|
||||
if !found && ps != "" {
|
||||
ms = ps
|
||||
}
|
||||
|
||||
noUpdate := ms == ParamsMergeStrategyNone
|
||||
noUpdate = noUpdate || (ps != "" && ps == ParamsMergeStrategyShallow)
|
||||
|
||||
for k, v := range pp {
|
||||
|
||||
if k == mergeStrategyKey {
|
||||
continue
|
||||
}
|
||||
vv, found := p[k]
|
||||
|
||||
if found {
|
||||
// Key matches, if both sides are Params, we try to merge.
|
||||
if vvv, ok := vv.(Params); ok {
|
||||
if pv, ok := v.(Params); ok {
|
||||
vvv.merge(ms, pv)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else if !noUpdate {
|
||||
p[k] = v
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (p Params) GetMergeStrategy() (ParamsMergeStrategy, bool) {
|
||||
if v, found := p[mergeStrategyKey]; found {
|
||||
if s, ok := v.(ParamsMergeStrategy); ok {
|
||||
return s, true
|
||||
}
|
||||
}
|
||||
return ParamsMergeStrategyShallow, false
|
||||
}
|
||||
|
||||
func (p Params) DeleteMergeStrategy() bool {
|
||||
if _, found := p[mergeStrategyKey]; found {
|
||||
delete(p, mergeStrategyKey)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p Params) SetDefaultMergeStrategy(s ParamsMergeStrategy) {
|
||||
switch s {
|
||||
case ParamsMergeStrategyDeep, ParamsMergeStrategyNone, ParamsMergeStrategyShallow:
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid merge strategy %q", s))
|
||||
}
|
||||
p[mergeStrategyKey] = s
|
||||
}
|
||||
|
||||
func getNested(m map[string]interface{}, indices []string) (interface{}, string, map[string]interface{}) {
|
||||
if len(indices) == 0 {
|
||||
return nil, "", nil
|
||||
|
@ -108,3 +198,61 @@ func GetNestedParamFn(keyStr, separator string, lookupFn func(key string) interf
|
|||
|
||||
return nil, "", nil, nil
|
||||
}
|
||||
|
||||
// ParamsMergeStrategy tells what strategy to use in Params.Merge.
|
||||
type ParamsMergeStrategy string
|
||||
|
||||
const (
|
||||
// Do not merge.
|
||||
ParamsMergeStrategyNone ParamsMergeStrategy = "none"
|
||||
// Only add new keys.
|
||||
ParamsMergeStrategyShallow ParamsMergeStrategy = "shallow"
|
||||
// Add new keys, merge existing.
|
||||
ParamsMergeStrategyDeep ParamsMergeStrategy = "deep"
|
||||
|
||||
mergeStrategyKey = "_merge"
|
||||
)
|
||||
|
||||
func toMergeStrategy(v interface{}) ParamsMergeStrategy {
|
||||
s := ParamsMergeStrategy(cast.ToString(v))
|
||||
switch s {
|
||||
case ParamsMergeStrategyDeep, ParamsMergeStrategyNone, ParamsMergeStrategyShallow:
|
||||
return s
|
||||
default:
|
||||
return ParamsMergeStrategyDeep
|
||||
}
|
||||
}
|
||||
|
||||
// PrepareParams
|
||||
// * makes all the keys in the given map lower cased and will do so
|
||||
// * This will modify the map given.
|
||||
// * Any nested map[interface{}]interface{} will be converted to Params.
|
||||
// * Any _merge value will be converted to proper type and value.
|
||||
func PrepareParams(m Params) {
|
||||
for k, v := range m {
|
||||
var retyped bool
|
||||
lKey := strings.ToLower(k)
|
||||
if lKey == mergeStrategyKey {
|
||||
v = toMergeStrategy(v)
|
||||
retyped = true
|
||||
} else {
|
||||
switch v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
var p Params = cast.ToStringMap(v)
|
||||
v = p
|
||||
PrepareParams(p)
|
||||
retyped = true
|
||||
case map[string]interface{}:
|
||||
var p Params = v.(map[string]interface{})
|
||||
v = p
|
||||
PrepareParams(p)
|
||||
retyped = true
|
||||
}
|
||||
}
|
||||
|
||||
if retyped || k != lKey {
|
||||
delete(m, k)
|
||||
m[lKey] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -69,3 +69,90 @@ func TestGetNestedParamFnNestedNewKey(t *testing.T) {
|
|||
c.Assert(nestedKey, qt.Equals, "new")
|
||||
c.Assert(owner, qt.DeepEquals, nested)
|
||||
}
|
||||
|
||||
func TestParamsSetAndMerge(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
createParamsPair := func() (Params, Params) {
|
||||
p1 := Params{"a": "av", "c": "cv", "nested": Params{"al2": "al2v", "cl2": "cl2v"}}
|
||||
p2 := Params{"b": "bv", "a": "abv", "nested": Params{"bl2": "bl2v", "al2": "al2bv"}, mergeStrategyKey: ParamsMergeStrategyDeep}
|
||||
return p1, p2
|
||||
}
|
||||
|
||||
p1, p2 := createParamsPair()
|
||||
|
||||
p1.Set(p2)
|
||||
|
||||
c.Assert(p1, qt.DeepEquals, Params{
|
||||
"a": "abv",
|
||||
"c": "cv",
|
||||
"nested": Params{
|
||||
"al2": "al2bv",
|
||||
"cl2": "cl2v",
|
||||
"bl2": "bl2v",
|
||||
},
|
||||
"b": "bv",
|
||||
mergeStrategyKey: ParamsMergeStrategyDeep,
|
||||
})
|
||||
|
||||
p1, p2 = createParamsPair()
|
||||
|
||||
p1.Merge(p2)
|
||||
|
||||
// Default is to do a shallow merge.
|
||||
c.Assert(p1, qt.DeepEquals, Params{
|
||||
"c": "cv",
|
||||
"nested": Params{
|
||||
"al2": "al2v",
|
||||
"cl2": "cl2v",
|
||||
},
|
||||
"b": "bv",
|
||||
"a": "av",
|
||||
})
|
||||
|
||||
p1, p2 = createParamsPair()
|
||||
p1.SetDefaultMergeStrategy(ParamsMergeStrategyNone)
|
||||
p1.Merge(p2)
|
||||
p1.DeleteMergeStrategy()
|
||||
|
||||
c.Assert(p1, qt.DeepEquals, Params{
|
||||
"a": "av",
|
||||
"c": "cv",
|
||||
"nested": Params{
|
||||
"al2": "al2v",
|
||||
"cl2": "cl2v",
|
||||
},
|
||||
})
|
||||
|
||||
p1, p2 = createParamsPair()
|
||||
p1.SetDefaultMergeStrategy(ParamsMergeStrategyShallow)
|
||||
p1.Merge(p2)
|
||||
p1.DeleteMergeStrategy()
|
||||
|
||||
c.Assert(p1, qt.DeepEquals, Params{
|
||||
"a": "av",
|
||||
"c": "cv",
|
||||
"nested": Params{
|
||||
"al2": "al2v",
|
||||
"cl2": "cl2v",
|
||||
},
|
||||
"b": "bv",
|
||||
})
|
||||
|
||||
p1, p2 = createParamsPair()
|
||||
p1.SetDefaultMergeStrategy(ParamsMergeStrategyDeep)
|
||||
p1.Merge(p2)
|
||||
p1.DeleteMergeStrategy()
|
||||
|
||||
c.Assert(p1, qt.DeepEquals, Params{
|
||||
"nested": Params{
|
||||
"al2": "al2v",
|
||||
"cl2": "cl2v",
|
||||
"bl2": "bl2v",
|
||||
},
|
||||
"b": "bv",
|
||||
"a": "av",
|
||||
"c": "cv",
|
||||
})
|
||||
|
||||
}
|
||||
|
|
|
@ -21,14 +21,12 @@ import (
|
|||
"github.com/gohugoio/hugo/common/types"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestBuild(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
v := viper.New()
|
||||
v := New()
|
||||
v.Set("build", map[string]interface{}{
|
||||
"useResourceCacheWhen": "always",
|
||||
})
|
||||
|
|
113
config/compositeConfig.go
Normal file
113
config/compositeConfig.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
// Copyright 2021 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 config
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
)
|
||||
|
||||
// NewCompositeConfig creates a new composite Provider with a read-only base
|
||||
// and a writeable layer.
|
||||
func NewCompositeConfig(base, layer Provider) Provider {
|
||||
return &compositeConfig{
|
||||
base: base,
|
||||
layer: layer,
|
||||
}
|
||||
}
|
||||
|
||||
// compositeConfig contains a read only config base with
|
||||
// a possibly writeable config layer on top.
|
||||
type compositeConfig struct {
|
||||
base Provider
|
||||
layer Provider
|
||||
}
|
||||
|
||||
func (c *compositeConfig) GetBool(key string) bool {
|
||||
if c.layer.IsSet(key) {
|
||||
return c.layer.GetBool(key)
|
||||
}
|
||||
return c.base.GetBool(key)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) GetInt(key string) int {
|
||||
if c.layer.IsSet(key) {
|
||||
return c.layer.GetInt(key)
|
||||
}
|
||||
return c.base.GetInt(key)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) Merge(key string, value interface{}) {
|
||||
c.layer.Merge(key, value)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) GetParams(key string) maps.Params {
|
||||
if c.layer.IsSet(key) {
|
||||
return c.layer.GetParams(key)
|
||||
}
|
||||
return c.base.GetParams(key)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) GetStringMap(key string) map[string]interface{} {
|
||||
if c.layer.IsSet(key) {
|
||||
return c.layer.GetStringMap(key)
|
||||
}
|
||||
return c.base.GetStringMap(key)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) GetStringMapString(key string) map[string]string {
|
||||
if c.layer.IsSet(key) {
|
||||
return c.layer.GetStringMapString(key)
|
||||
}
|
||||
return c.base.GetStringMapString(key)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) GetStringSlice(key string) []string {
|
||||
if c.layer.IsSet(key) {
|
||||
return c.layer.GetStringSlice(key)
|
||||
}
|
||||
return c.base.GetStringSlice(key)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) Get(key string) interface{} {
|
||||
if c.layer.IsSet(key) {
|
||||
return c.layer.Get(key)
|
||||
}
|
||||
return c.base.Get(key)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) IsSet(key string) bool {
|
||||
if c.layer.IsSet(key) {
|
||||
return true
|
||||
}
|
||||
return c.base.IsSet(key)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) GetString(key string) string {
|
||||
if c.layer.IsSet(key) {
|
||||
return c.layer.GetString(key)
|
||||
}
|
||||
return c.base.GetString(key)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) Set(key string, value interface{}) {
|
||||
c.layer.Set(key, value)
|
||||
}
|
||||
|
||||
func (c *compositeConfig) WalkParams(walkFn func(params ...KeyParams) bool) {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (c *compositeConfig) SetDefaultMergeStrategy() {
|
||||
panic("not supported")
|
||||
}
|
40
config/compositeConfig_test.go
Normal file
40
config/compositeConfig_test.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2021 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 config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestCompositeConfig(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Run("Set and get", func(c *qt.C) {
|
||||
base, layer := New(), New()
|
||||
cfg := NewCompositeConfig(base, layer)
|
||||
|
||||
layer.Set("a1", "av")
|
||||
base.Set("b1", "bv")
|
||||
cfg.Set("c1", "cv")
|
||||
|
||||
c.Assert(cfg.Get("a1"), qt.Equals, "av")
|
||||
c.Assert(cfg.Get("b1"), qt.Equals, "bv")
|
||||
c.Assert(cfg.Get("c1"), qt.Equals, "cv")
|
||||
c.Assert(cfg.IsSet("c1"), qt.IsTrue)
|
||||
c.Assert(layer.IsSet("c1"), qt.IsTrue)
|
||||
c.Assert(base.IsSet("c1"), qt.IsFalse)
|
||||
})
|
||||
}
|
|
@ -20,7 +20,6 @@ import (
|
|||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/parser/metadecoders"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -43,15 +42,11 @@ func IsValidConfigFilename(filename string) bool {
|
|||
|
||||
// FromConfigString creates a config from the given YAML, JSON or TOML config. This is useful in tests.
|
||||
func FromConfigString(config, configType string) (Provider, error) {
|
||||
v := newViper()
|
||||
m, err := readConfig(metadecoders.FormatFromString(configType), []byte(config))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v.MergeConfigMap(m)
|
||||
|
||||
return v, nil
|
||||
return NewFrom(m), nil
|
||||
}
|
||||
|
||||
// FromFile loads the configuration from the given filename.
|
||||
|
@ -60,15 +55,7 @@ func FromFile(fs afero.Fs, filename string) (Provider, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
v := newViper()
|
||||
|
||||
err = v.MergeConfigMap(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return v, nil
|
||||
return NewFrom(m), nil
|
||||
}
|
||||
|
||||
// FromFileToMap is the same as FromFile, but it returns the config values
|
||||
|
@ -116,9 +103,3 @@ func init() {
|
|||
func RenameKeys(m map[string]interface{}) {
|
||||
keyAliases.Rename(m)
|
||||
}
|
||||
|
||||
func newViper() *viper.Viper {
|
||||
v := viper.New()
|
||||
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
)
|
||||
|
||||
|
@ -22,11 +23,15 @@ type Provider interface {
|
|||
GetString(key string) string
|
||||
GetInt(key string) int
|
||||
GetBool(key string) bool
|
||||
GetParams(key string) maps.Params
|
||||
GetStringMap(key string) map[string]interface{}
|
||||
GetStringMapString(key string) map[string]string
|
||||
GetStringSlice(key string) []string
|
||||
Get(key string) interface{}
|
||||
Set(key string, value interface{})
|
||||
Merge(key string, value interface{})
|
||||
SetDefaultMergeStrategy()
|
||||
WalkParams(walkFn func(params ...KeyParams) bool)
|
||||
IsSet(key string) bool
|
||||
}
|
||||
|
||||
|
|
|
@ -17,12 +17,11 @@ import (
|
|||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestGetStringSlicePreserveString(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := New()
|
||||
|
||||
s := "This is a string"
|
||||
sSlice := []string{"This", "is", "a", "slice"}
|
||||
|
|
372
config/defaultConfigProvider.go
Normal file
372
config/defaultConfigProvider.go
Normal file
|
@ -0,0 +1,372 @@
|
|||
// Copyright 2021 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 config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
// ConfigRootKeysSet contains all of the config map root keys.
|
||||
// TODO(bep) use this for something (docs etc.)
|
||||
ConfigRootKeysSet = map[string]bool{
|
||||
"build": true,
|
||||
"caches": true,
|
||||
"frontmatter": true,
|
||||
"languages": true,
|
||||
"imaging": true,
|
||||
"markup": true,
|
||||
"mediatypes": true,
|
||||
"menus": true,
|
||||
"minify": true,
|
||||
"module": true,
|
||||
"outputformats": true,
|
||||
"params": true,
|
||||
"permalinks": true,
|
||||
"related": true,
|
||||
"sitemap": true,
|
||||
"taxonomies": true,
|
||||
}
|
||||
|
||||
// ConfigRootKeys is a sorted version of ConfigRootKeysSet.
|
||||
ConfigRootKeys []string
|
||||
)
|
||||
|
||||
func init() {
|
||||
for k := range ConfigRootKeysSet {
|
||||
ConfigRootKeys = append(ConfigRootKeys, k)
|
||||
}
|
||||
sort.Strings(ConfigRootKeys)
|
||||
}
|
||||
|
||||
// New creates a Provider backed by an empty maps.Params.
|
||||
func New() Provider {
|
||||
return &defaultConfigProvider{
|
||||
root: make(maps.Params),
|
||||
}
|
||||
}
|
||||
|
||||
// NewFrom creates a Provider backed by params.
|
||||
func NewFrom(params maps.Params) Provider {
|
||||
maps.PrepareParams(params)
|
||||
return &defaultConfigProvider{
|
||||
root: params,
|
||||
}
|
||||
}
|
||||
|
||||
// defaultConfigProvider is a Provider backed by a map where all keys are lower case.
|
||||
// All methods are thread safe.
|
||||
type defaultConfigProvider struct {
|
||||
mu sync.RWMutex
|
||||
root maps.Params
|
||||
|
||||
keyCache sync.Map
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) Get(k string) interface{} {
|
||||
if k == "" {
|
||||
return c.root
|
||||
}
|
||||
c.mu.RLock()
|
||||
key, m := c.getNestedKeyAndMap(strings.ToLower(k), false)
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
v := m[key]
|
||||
c.mu.RUnlock()
|
||||
return v
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) GetBool(k string) bool {
|
||||
v := c.Get(k)
|
||||
return cast.ToBool(v)
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) GetInt(k string) int {
|
||||
v := c.Get(k)
|
||||
return cast.ToInt(v)
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) IsSet(k string) bool {
|
||||
var found bool
|
||||
c.mu.RLock()
|
||||
key, m := c.getNestedKeyAndMap(strings.ToLower(k), false)
|
||||
if m != nil {
|
||||
_, found = m[key]
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
return found
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) GetString(k string) string {
|
||||
v := c.Get(k)
|
||||
return cast.ToString(v)
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) GetParams(k string) maps.Params {
|
||||
v := c.Get(k)
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
return v.(maps.Params)
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) GetStringMap(k string) map[string]interface{} {
|
||||
v := c.Get(k)
|
||||
return maps.ToStringMap(v)
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) GetStringMapString(k string) map[string]string {
|
||||
v := c.Get(k)
|
||||
return maps.ToStringMapString(v)
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) GetStringSlice(k string) []string {
|
||||
v := c.Get(k)
|
||||
return cast.ToStringSlice(v)
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) Set(k string, v interface{}) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
k = strings.ToLower(k)
|
||||
|
||||
if k == "" {
|
||||
if p, ok := maps.ToParamsAndPrepare(v); ok {
|
||||
// Set the values directly in root.
|
||||
c.root.Set(p)
|
||||
} else {
|
||||
c.root[k] = v
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
case map[string]interface{}:
|
||||
var p maps.Params = vv
|
||||
v = p
|
||||
maps.PrepareParams(p)
|
||||
}
|
||||
|
||||
key, m := c.getNestedKeyAndMap(k, true)
|
||||
|
||||
if existing, found := m[key]; found {
|
||||
if p1, ok := existing.(maps.Params); ok {
|
||||
if p2, ok := v.(maps.Params); ok {
|
||||
p1.Set(p2)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m[key] = v
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) Merge(k string, v interface{}) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
k = strings.ToLower(k)
|
||||
|
||||
if k == "" {
|
||||
rs, f := c.root.GetMergeStrategy()
|
||||
if f && rs == maps.ParamsMergeStrategyNone {
|
||||
// The user has set a "no merge" strategy on this,
|
||||
// nothing more to do.
|
||||
return
|
||||
}
|
||||
|
||||
if p, ok := maps.ToParamsAndPrepare(v); ok {
|
||||
// As there may be keys in p not in root, we need to handle
|
||||
// those as a special case.
|
||||
for kk, vv := range p {
|
||||
if pp, ok := vv.(maps.Params); ok {
|
||||
if ppp, ok := c.root[kk]; ok {
|
||||
ppp.(maps.Params).Merge(pp)
|
||||
} else {
|
||||
// We need to use the default merge strategy for
|
||||
// this key.
|
||||
np := make(maps.Params)
|
||||
strategy := c.determineMergeStrategy(KeyParams{Key: "", Params: c.root}, KeyParams{Key: kk, Params: np})
|
||||
np.SetDefaultMergeStrategy(strategy)
|
||||
np.Merge(pp)
|
||||
if len(np) > 0 {
|
||||
c.root[kk] = np
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Merge the rest.
|
||||
c.root.Merge(p)
|
||||
} else {
|
||||
panic(fmt.Sprintf("unsupported type %T received in Merge", v))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
switch vv := v.(type) {
|
||||
case map[string]interface{}:
|
||||
var p maps.Params = vv
|
||||
v = p
|
||||
maps.PrepareParams(p)
|
||||
}
|
||||
|
||||
key, m := c.getNestedKeyAndMap(k, true)
|
||||
|
||||
if existing, found := m[key]; found {
|
||||
if p1, ok := existing.(maps.Params); ok {
|
||||
if p2, ok := v.(maps.Params); ok {
|
||||
p1.Merge(p2)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m[key] = v
|
||||
}
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) WalkParams(walkFn func(params ...KeyParams) bool) {
|
||||
var walk func(params ...KeyParams)
|
||||
walk = func(params ...KeyParams) {
|
||||
if walkFn(params...) {
|
||||
return
|
||||
}
|
||||
p1 := params[len(params)-1]
|
||||
i := len(params)
|
||||
for k, v := range p1.Params {
|
||||
if p2, ok := v.(maps.Params); ok {
|
||||
paramsplus1 := make([]KeyParams, i+1)
|
||||
copy(paramsplus1, params)
|
||||
paramsplus1[i] = KeyParams{Key: k, Params: p2}
|
||||
walk(paramsplus1...)
|
||||
}
|
||||
}
|
||||
}
|
||||
walk(KeyParams{Key: "", Params: c.root})
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) determineMergeStrategy(params ...KeyParams) maps.ParamsMergeStrategy {
|
||||
if len(params) == 0 {
|
||||
return maps.ParamsMergeStrategyNone
|
||||
}
|
||||
|
||||
var (
|
||||
strategy maps.ParamsMergeStrategy
|
||||
prevIsRoot bool
|
||||
curr = params[len(params)-1]
|
||||
)
|
||||
|
||||
if len(params) > 1 {
|
||||
prev := params[len(params)-2]
|
||||
prevIsRoot = prev.Key == ""
|
||||
|
||||
// Inherit from parent (but not from the root unless it's set by user).
|
||||
s, found := prev.Params.GetMergeStrategy()
|
||||
if !prevIsRoot && !found {
|
||||
panic("invalid state, merge strategy not set on parent")
|
||||
}
|
||||
if found || !prevIsRoot {
|
||||
strategy = s
|
||||
}
|
||||
}
|
||||
|
||||
switch curr.Key {
|
||||
case "":
|
||||
// Don't set a merge strategy on the root unless set by user.
|
||||
// This will be handled as a special case.
|
||||
case "params":
|
||||
strategy = maps.ParamsMergeStrategyDeep
|
||||
case "outputformats", "mediatypes":
|
||||
if prevIsRoot {
|
||||
strategy = maps.ParamsMergeStrategyShallow
|
||||
}
|
||||
case "menus":
|
||||
isMenuKey := prevIsRoot
|
||||
if !isMenuKey {
|
||||
// Can also be set below languages.
|
||||
// root > languages > en > menus
|
||||
if len(params) == 4 && params[1].Key == "languages" {
|
||||
isMenuKey = true
|
||||
}
|
||||
}
|
||||
if isMenuKey {
|
||||
strategy = maps.ParamsMergeStrategyShallow
|
||||
}
|
||||
default:
|
||||
if strategy == "" {
|
||||
strategy = maps.ParamsMergeStrategyNone
|
||||
}
|
||||
}
|
||||
|
||||
return strategy
|
||||
}
|
||||
|
||||
type KeyParams struct {
|
||||
Key string
|
||||
Params maps.Params
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) SetDefaultMergeStrategy() {
|
||||
c.WalkParams(func(params ...KeyParams) bool {
|
||||
if len(params) == 0 {
|
||||
return false
|
||||
}
|
||||
p := params[len(params)-1].Params
|
||||
var found bool
|
||||
if _, found = p.GetMergeStrategy(); found {
|
||||
// Set by user.
|
||||
return false
|
||||
}
|
||||
strategy := c.determineMergeStrategy(params...)
|
||||
if strategy != "" {
|
||||
p.SetDefaultMergeStrategy(strategy)
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (c *defaultConfigProvider) getNestedKeyAndMap(key string, create bool) (string, maps.Params) {
|
||||
var parts []string
|
||||
v, ok := c.keyCache.Load(key)
|
||||
if ok {
|
||||
parts = v.([]string)
|
||||
} else {
|
||||
parts = strings.Split(key, ".")
|
||||
c.keyCache.Store(key, parts)
|
||||
}
|
||||
current := c.root
|
||||
for i := 0; i < len(parts)-1; i++ {
|
||||
next, found := current[parts[i]]
|
||||
if !found {
|
||||
if create {
|
||||
next = make(maps.Params)
|
||||
current[parts[i]] = next
|
||||
} else {
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
current = next.(maps.Params)
|
||||
}
|
||||
return parts[len(parts)-1], current
|
||||
}
|
315
config/defaultConfigProvider_test.go
Normal file
315
config/defaultConfigProvider_test.go
Normal file
|
@ -0,0 +1,315 @@
|
|||
// Copyright 2021 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 config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gohugoio/hugo/common/para"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestDefaultConfigProvider(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Run("Set and get", func(c *qt.C) {
|
||||
cfg := New()
|
||||
var k string
|
||||
var v interface{}
|
||||
|
||||
k, v = "foo", "bar"
|
||||
cfg.Set(k, v)
|
||||
c.Assert(cfg.Get(k), qt.Equals, v)
|
||||
c.Assert(cfg.Get(strings.ToUpper(k)), qt.Equals, v)
|
||||
c.Assert(cfg.GetString(k), qt.Equals, v)
|
||||
|
||||
k, v = "foo", 42
|
||||
cfg.Set(k, v)
|
||||
c.Assert(cfg.Get(k), qt.Equals, v)
|
||||
c.Assert(cfg.GetInt(k), qt.Equals, v)
|
||||
|
||||
c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{
|
||||
"foo": 42,
|
||||
})
|
||||
})
|
||||
|
||||
c.Run("Set and get map", func(c *qt.C) {
|
||||
cfg := New()
|
||||
|
||||
cfg.Set("foo", map[string]interface{}{
|
||||
"bar": "baz",
|
||||
})
|
||||
|
||||
c.Assert(cfg.Get("foo"), qt.DeepEquals, maps.Params{
|
||||
"bar": "baz",
|
||||
})
|
||||
|
||||
c.Assert(cfg.GetStringMap("foo"), qt.DeepEquals, map[string]interface{}{"bar": string("baz")})
|
||||
c.Assert(cfg.GetStringMapString("foo"), qt.DeepEquals, map[string]string{"bar": string("baz")})
|
||||
})
|
||||
|
||||
c.Run("Set and get nested", func(c *qt.C) {
|
||||
cfg := New()
|
||||
|
||||
cfg.Set("a", map[string]interface{}{
|
||||
"B": "bv",
|
||||
})
|
||||
cfg.Set("a.c", "cv")
|
||||
|
||||
c.Assert(cfg.Get("a"), qt.DeepEquals, maps.Params{
|
||||
"b": "bv",
|
||||
"c": "cv",
|
||||
})
|
||||
c.Assert(cfg.Get("a.c"), qt.Equals, "cv")
|
||||
|
||||
cfg.Set("b.a", "av")
|
||||
c.Assert(cfg.Get("b"), qt.DeepEquals, maps.Params{
|
||||
"a": "av",
|
||||
})
|
||||
|
||||
cfg.Set("b", map[string]interface{}{
|
||||
"b": "bv",
|
||||
})
|
||||
|
||||
c.Assert(cfg.Get("b"), qt.DeepEquals, maps.Params{
|
||||
"a": "av",
|
||||
"b": "bv",
|
||||
})
|
||||
|
||||
cfg = New()
|
||||
|
||||
cfg.Set("a", "av")
|
||||
|
||||
cfg.Set("", map[string]interface{}{
|
||||
"a": "av2",
|
||||
"b": "bv2",
|
||||
})
|
||||
|
||||
c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{
|
||||
"a": "av2",
|
||||
"b": "bv2",
|
||||
})
|
||||
|
||||
cfg = New()
|
||||
|
||||
cfg.Set("a", "av")
|
||||
|
||||
cfg.Set("", map[string]interface{}{
|
||||
"b": "bv2",
|
||||
})
|
||||
|
||||
c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{
|
||||
"a": "av",
|
||||
"b": "bv2",
|
||||
})
|
||||
|
||||
cfg = New()
|
||||
|
||||
cfg.Set("", map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"a": "av",
|
||||
},
|
||||
})
|
||||
|
||||
cfg.Set("", map[string]interface{}{
|
||||
"foo": map[string]interface{}{
|
||||
"b": "bv2",
|
||||
},
|
||||
})
|
||||
|
||||
c.Assert(cfg.Get("foo"), qt.DeepEquals, maps.Params{
|
||||
"a": "av",
|
||||
"b": "bv2",
|
||||
})
|
||||
})
|
||||
|
||||
c.Run("Merge default strategy", func(c *qt.C) {
|
||||
cfg := New()
|
||||
|
||||
cfg.Set("a", map[string]interface{}{
|
||||
"B": "bv",
|
||||
})
|
||||
|
||||
cfg.Merge("a", map[string]interface{}{
|
||||
"B": "bv2",
|
||||
"c": "cv2",
|
||||
})
|
||||
|
||||
c.Assert(cfg.Get("a"), qt.DeepEquals, maps.Params{
|
||||
"b": "bv",
|
||||
"c": "cv2",
|
||||
})
|
||||
|
||||
cfg = New()
|
||||
|
||||
cfg.Set("a", "av")
|
||||
|
||||
cfg.Merge("", map[string]interface{}{
|
||||
"a": "av2",
|
||||
"b": "bv2",
|
||||
})
|
||||
|
||||
c.Assert(cfg.Get(""), qt.DeepEquals, maps.Params{
|
||||
"a": "av",
|
||||
"b": "bv2",
|
||||
})
|
||||
})
|
||||
|
||||
c.Run("Merge shallow", func(c *qt.C) {
|
||||
cfg := New()
|
||||
|
||||
cfg.Set("a", map[string]interface{}{
|
||||
"_merge": "shallow",
|
||||
"B": "bv",
|
||||
"c": map[string]interface{}{
|
||||
"b": "bv",
|
||||
},
|
||||
})
|
||||
|
||||
cfg.Merge("a", map[string]interface{}{
|
||||
"c": map[string]interface{}{
|
||||
"d": "dv2",
|
||||
},
|
||||
"e": "ev2",
|
||||
})
|
||||
|
||||
c.Assert(cfg.Get("a"), qt.DeepEquals, maps.Params{
|
||||
"e": "ev2",
|
||||
"_merge": maps.ParamsMergeStrategyShallow,
|
||||
"b": "bv",
|
||||
"c": maps.Params{
|
||||
"b": "bv",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
c.Run("IsSet", func(c *qt.C) {
|
||||
cfg := New()
|
||||
|
||||
cfg.Set("a", map[string]interface{}{
|
||||
"B": "bv",
|
||||
})
|
||||
|
||||
c.Assert(cfg.IsSet("A"), qt.IsTrue)
|
||||
c.Assert(cfg.IsSet("a.b"), qt.IsTrue)
|
||||
c.Assert(cfg.IsSet("z"), qt.IsFalse)
|
||||
})
|
||||
|
||||
c.Run("Para", func(c *qt.C) {
|
||||
cfg := New()
|
||||
p := para.New(4)
|
||||
r, _ := p.Start(context.Background())
|
||||
|
||||
setAndGet := func(k string, v int) error {
|
||||
vs := strconv.Itoa(v)
|
||||
cfg.Set(k, v)
|
||||
err := errors.New("get failed")
|
||||
if cfg.Get(k) != v {
|
||||
return err
|
||||
}
|
||||
if cfg.GetInt(k) != v {
|
||||
return err
|
||||
}
|
||||
if cfg.GetString(k) != vs {
|
||||
return err
|
||||
}
|
||||
if !cfg.IsSet(k) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := 0; i < 20; i++ {
|
||||
i := i
|
||||
r.Run(func() error {
|
||||
const v = 42
|
||||
k := fmt.Sprintf("k%d", i)
|
||||
if err := setAndGet(k, v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m := maps.Params{
|
||||
"new": 42,
|
||||
}
|
||||
|
||||
cfg.Merge("", m)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
c.Assert(r.Wait(), qt.IsNil)
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkDefaultConfigProvider(b *testing.B) {
|
||||
type cfger interface {
|
||||
Get(key string) interface{}
|
||||
Set(key string, value interface{})
|
||||
IsSet(key string) bool
|
||||
}
|
||||
|
||||
newMap := func() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"a": map[string]interface{}{
|
||||
"b": map[string]interface{}{
|
||||
"c": 32,
|
||||
"d": 43,
|
||||
},
|
||||
},
|
||||
"b": 62,
|
||||
}
|
||||
}
|
||||
|
||||
runMethods := func(b *testing.B, cfg cfger) {
|
||||
m := newMap()
|
||||
cfg.Set("mymap", m)
|
||||
cfg.Set("num", 32)
|
||||
if !(cfg.IsSet("mymap") && cfg.IsSet("mymap.a") && cfg.IsSet("mymap.a.b") && cfg.IsSet("mymap.a.b.c")) {
|
||||
b.Fatal("IsSet failed")
|
||||
}
|
||||
|
||||
if cfg.Get("num") != 32 {
|
||||
b.Fatal("Get failed")
|
||||
}
|
||||
|
||||
if cfg.Get("mymap.a.b.c") != 32 {
|
||||
b.Fatal("Get failed")
|
||||
}
|
||||
}
|
||||
|
||||
b.Run("Viper", func(b *testing.B) {
|
||||
v := viper.New()
|
||||
for i := 0; i < b.N; i++ {
|
||||
runMethods(b, v)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Custom", func(b *testing.B) {
|
||||
cfg := New()
|
||||
for i := 0; i < b.N; i++ {
|
||||
runMethods(b, cfg)
|
||||
}
|
||||
})
|
||||
}
|
45
config/docshelper.go
Normal file
45
config/docshelper.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2021 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 config
|
||||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/docshelper"
|
||||
)
|
||||
|
||||
// This is is just some helpers used to create some JSON used in the Hugo docs.
|
||||
func init() {
|
||||
docsProvider := func() docshelper.DocProvider {
|
||||
|
||||
cfg := New()
|
||||
for _, configRoot := range ConfigRootKeys {
|
||||
cfg.Set(configRoot, make(maps.Params))
|
||||
}
|
||||
lang := maps.Params{
|
||||
"en": maps.Params{
|
||||
"menus": maps.Params{},
|
||||
"params": maps.Params{},
|
||||
},
|
||||
}
|
||||
cfg.Set("languages", lang)
|
||||
cfg.SetDefaultMergeStrategy()
|
||||
|
||||
configHelpers := map[string]interface{}{
|
||||
"mergeStrategy": cfg.Get(""),
|
||||
}
|
||||
return docshelper.DocProvider{"config": configHelpers}
|
||||
}
|
||||
|
||||
docshelper.AddDocProviderFunc(docsProvider)
|
||||
}
|
|
@ -18,7 +18,6 @@ import (
|
|||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestDecodeConfigFromTOML(t *testing.T) {
|
||||
|
@ -94,7 +93,7 @@ PrivacyENhanced = true
|
|||
func TestDecodeConfigDefault(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
pc, err := DecodeConfig(viper.New())
|
||||
pc, err := DecodeConfig(config.New())
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(pc, qt.Not(qt.IsNil))
|
||||
c.Assert(pc.YouTube.PrivacyEnhanced, qt.Equals, false)
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
)
|
||||
|
||||
func TestDecodeConfigFromTOML(t *testing.T) {
|
||||
|
@ -55,7 +55,7 @@ disableInlineCSS = true
|
|||
func TestUseSettingsFromRootIfSet(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("disqusShortname", "root_short")
|
||||
cfg.Set("googleAnalytics", "ga_root")
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
|
||||
"github.com/gohugoio/hugo/hugolib"
|
||||
|
@ -30,7 +32,6 @@ import (
|
|||
"github.com/gohugoio/hugo/create"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestNewContent(t *testing.T) {
|
||||
|
@ -245,7 +246,7 @@ func readFileFromFs(t *testing.T, fs afero.Fs, filename string) string {
|
|||
return string(b)
|
||||
}
|
||||
|
||||
func newTestCfg(c *qt.C, mm afero.Fs) (*viper.Viper, *hugofs.Fs) {
|
||||
func newTestCfg(c *qt.C, mm afero.Fs) (config.Provider, *hugofs.Fs) {
|
||||
cfg := `
|
||||
|
||||
theme = "mytheme"
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
)
|
||||
|
||||
func TestDecodeConfigFromTOML(t *testing.T) {
|
||||
|
@ -164,7 +164,7 @@ Pattern = "[" # invalid regular expression
|
|||
func TestDecodeConfigDefault(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
dcfg, err := decodeConfig(viper.New())
|
||||
dcfg, err := decodeConfig(config.New())
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(len(dcfg.Targets), qt.Equals, 0)
|
||||
c.Assert(len(dcfg.Matchers), qt.Equals, 0)
|
||||
|
|
|
@ -7,7 +7,7 @@ footnotereturnlinkcontents = "↩"
|
|||
languageCode = "en-us"
|
||||
title = "Hugo"
|
||||
|
||||
ignoreErrors = ["error-remote-getjson"]
|
||||
ignoreErrors = ["error-remote-getjson", "err-missing-instagram-accesstoken"]
|
||||
|
||||
|
||||
googleAnalytics = "UA-7131036-4"
|
||||
|
|
|
@ -80,6 +80,26 @@ Considering the structure above, when running `hugo --environment staging`, Hugo
|
|||
{{% note %}}
|
||||
Default environments are __development__ with `hugo server` and __production__ with `hugo`.
|
||||
{{%/ note %}}
|
||||
|
||||
## Merge Configuration from Themes
|
||||
|
||||
{{< new-in "0.84.0" >}} The configuration merge described below was improved in Hugo 0.84.0 and made fully configurable. The big change/improvement was that we now, by default, do deep merging of `params` maps from themes.
|
||||
|
||||
The configuration value for `_merge` can be one of:
|
||||
|
||||
none
|
||||
: No merge.
|
||||
|
||||
shallow
|
||||
: Only add values for new keys.
|
||||
|
||||
shallow
|
||||
: Add values for new keys, merge existing.
|
||||
|
||||
Note that you don't need to be so verbose as in the default setup below; a `_merge` value higher up will be inherited if not set.
|
||||
|
||||
{{< code-toggle config="mergeStrategy" skipHeader=true />}}
|
||||
|
||||
## All Configuration Settings
|
||||
|
||||
The following is the full list of Hugo-defined variables with their default
|
||||
|
|
|
@ -1587,6 +1587,65 @@
|
|||
"preserveTOC": false
|
||||
}
|
||||
},
|
||||
"mergeStrategy": {
|
||||
"build": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"caches": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"frontmatter": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"imaging": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"languages": {
|
||||
"_merge": "none",
|
||||
"en": {
|
||||
"_merge": "none",
|
||||
"menus": {
|
||||
"_merge": "shallow"
|
||||
},
|
||||
"params": {
|
||||
"_merge": "deep"
|
||||
}
|
||||
}
|
||||
},
|
||||
"markup": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"mediatypes": {
|
||||
"_merge": "shallow"
|
||||
},
|
||||
"menus": {
|
||||
"_merge": "shallow"
|
||||
},
|
||||
"minify": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"module": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"outputformats": {
|
||||
"_merge": "shallow"
|
||||
},
|
||||
"params": {
|
||||
"_merge": "deep"
|
||||
},
|
||||
"permalinks": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"related": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"sitemap": {
|
||||
"_merge": "none"
|
||||
},
|
||||
"taxonomies": {
|
||||
"_merge": "none"
|
||||
}
|
||||
},
|
||||
"minify": {
|
||||
"minifyOutput": false,
|
||||
"disableHTML": false,
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
{{ $file := .Get "file" }}
|
||||
{{ $code := "" }}
|
||||
{{ with .Get "config" }}
|
||||
{{ $file = $file | default "config" }}
|
||||
{{ $sections := (split . ".") }}
|
||||
{{ $configSection := index $.Site.Data.docs.config $sections }}
|
||||
{{ $code = dict $sections $configSection }}
|
||||
{{ $file = $file | default "config" }}
|
||||
{{ $sections := (split . ".") }}
|
||||
{{ $configSection := index $.Site.Data.docs.config $sections }}
|
||||
{{ $code = dict $sections $configSection }}
|
||||
{{ if $.Get "skipHeader"}}
|
||||
{{ $code = $configSection }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ $code = $.Inner }}
|
||||
{{ $code = $.Inner }}
|
||||
{{ end }}
|
||||
{{ $langs := (slice "yaml" "toml" "json") }}
|
||||
<div class="code relative" {{ with $file }}id="{{ . | urlize}}"{{ end }}>
|
||||
<div class="code-nav flex flex-nowrap items-stretch">
|
||||
{{- with $file -}}
|
||||
<div class="san-serif f6 dib lh-solid pl2 pv2 mr2">{{ . }}.</div>
|
||||
<div class="san-serif f6 dib lh-solid pl2 pv2 mr2">
|
||||
{{ . }}.
|
||||
</div>
|
||||
{{- end -}}
|
||||
{{ range $langs }}
|
||||
<button data-toggle-tab="{{ . }}" class="tab-button {{ cond (eq . "yaml") "active" ""}} ba san-serif f6 dib lh-solid ph2 pv2">{{ . }}</button>
|
||||
<button data-toggle-tab="{{ . }}" class="tab-button {{ cond (eq . "yaml") "active" ""}} ba san-serif f6 dib lh-solid ph2 pv2">
|
||||
{{ . }}
|
||||
</button>
|
||||
|
||||
{{ end }}
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
|
@ -24,8 +32,7 @@
|
|||
{{ highlight ($code | transform.Remarshal . | safeHTML) . ""}}
|
||||
</div>
|
||||
{{ if ne ($.Get "copy") "false" }}
|
||||
<button class="needs-js copy copy-toggle bg-accent-color-dark f6 absolute top-0 right-0 lh-solid hover-bg-primary-color-dark bn white ph3 pv2" title="Copy this code to your clipboard." data-clipboard-action="copy" aria-label="copy button">
|
||||
</button>
|
||||
<button class="needs-js copy copy-toggle bg-accent-color-dark f6 absolute top-0 right-0 lh-solid hover-bg-primary-color-dark bn white ph3 pv2" title="Copy this code to your clipboard." data-clipboard-action="copy" aria-label="copy button"></button>
|
||||
{{/* Functionality located within filesaver.js The copy here is located in the css with .copy class so it can be replaced with JS on success */}}
|
||||
{{end}}
|
||||
{{ end }}
|
||||
|
|
2
go.mod
2
go.mod
|
@ -55,7 +55,7 @@ require (
|
|||
github.com/spf13/fsync v0.9.0
|
||||
github.com/spf13/jwalterweatherman v1.1.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/spf13/viper v1.7.0
|
||||
github.com/tdewolff/minify/v2 v2.9.16
|
||||
github.com/yuin/goldmark v1.3.5
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20200307114337-60d527fdb691
|
||||
|
|
1
go.sum
1
go.sum
|
@ -642,6 +642,7 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0
|
|||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
|
|
|
@ -19,12 +19,11 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
|
@ -103,7 +102,7 @@ func TestBytesToHTML(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestNewContentSpec(t *testing.T) {
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
c := qt.New(t)
|
||||
|
||||
cfg.Set("summaryLength", 32)
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
|
||||
|
@ -29,7 +29,7 @@ import (
|
|||
|
||||
func TestResolveMarkup(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
spec, err := NewContentSpec(cfg, loggers.NewErrorLogger(), afero.NewMemMapFs())
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ import (
|
|||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestMakePath(t *testing.T) {
|
||||
|
@ -490,8 +489,6 @@ func TestExists(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAbsPathify(t *testing.T) {
|
||||
defer viper.Reset()
|
||||
|
||||
type test struct {
|
||||
inPath, workingDir, expected string
|
||||
}
|
||||
|
@ -511,7 +508,6 @@ func TestAbsPathify(t *testing.T) {
|
|||
}
|
||||
|
||||
for i, d := range data {
|
||||
viper.Reset()
|
||||
// todo see comment in AbsPathify
|
||||
ps := newTestDefaultPathSpec("workingDir", d.workingDir)
|
||||
|
||||
|
|
|
@ -2,24 +2,24 @@ package helpers
|
|||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
)
|
||||
|
||||
func newTestPathSpec(fs *hugofs.Fs, v *viper.Viper) *PathSpec {
|
||||
func newTestPathSpec(fs *hugofs.Fs, v config.Provider) *PathSpec {
|
||||
l := langs.NewDefaultLanguage(v)
|
||||
ps, _ := NewPathSpec(fs, l, nil)
|
||||
return ps
|
||||
}
|
||||
|
||||
func newTestDefaultPathSpec(configKeyValues ...interface{}) *PathSpec {
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
fs := hugofs.NewMem(v)
|
||||
cfg := newTestCfgFor(fs)
|
||||
cfg := newTestCfg()
|
||||
|
||||
for i := 0; i < len(configKeyValues); i += 2 {
|
||||
cfg.Set(configKeyValues[i].(string), configKeyValues[i+1])
|
||||
|
@ -27,15 +27,8 @@ func newTestDefaultPathSpec(configKeyValues ...interface{}) *PathSpec {
|
|||
return newTestPathSpec(fs, cfg)
|
||||
}
|
||||
|
||||
func newTestCfgFor(fs *hugofs.Fs) *viper.Viper {
|
||||
v := newTestCfg()
|
||||
v.SetFs(fs.Source)
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func newTestCfg() *viper.Viper {
|
||||
v := viper.New()
|
||||
func newTestCfg() config.Provider {
|
||||
v := config.New()
|
||||
v.Set("contentDir", "content")
|
||||
v.Set("dataDir", "data")
|
||||
v.Set("i18nDir", "i18n")
|
||||
|
@ -56,7 +49,7 @@ func newTestCfg() *viper.Viper {
|
|||
}
|
||||
|
||||
func newTestContentSpec() *ContentSpec {
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
spec, err := NewContentSpec(v, loggers.NewErrorLogger(), afero.NewMemMapFs())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
|
@ -16,15 +16,16 @@ package hugofs
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestNewDefault(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
f := NewDefault(v)
|
||||
|
||||
c.Assert(f.Source, qt.Not(qt.IsNil))
|
||||
|
@ -35,7 +36,7 @@ func TestNewDefault(t *testing.T) {
|
|||
|
||||
func TestNewMem(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
f := NewMem(v)
|
||||
|
||||
c.Assert(f.Source, qt.Not(qt.IsNil))
|
||||
|
@ -48,7 +49,7 @@ func TestNewMem(t *testing.T) {
|
|||
|
||||
func TestWorkingDir(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
|
||||
v.Set("workingDir", "/a/b/")
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import (
|
|||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
|
@ -29,7 +29,7 @@ import (
|
|||
|
||||
func TestLanguageRootMapping(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("contentDir", "content")
|
||||
|
||||
fs := NewBaseFileDecorator(afero.NewMemMapFs())
|
||||
|
|
|
@ -43,34 +43,136 @@ import (
|
|||
"github.com/gohugoio/hugo/config/services"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// SiteConfig represents the config in .Site.Config.
|
||||
type SiteConfig struct {
|
||||
// This contains all privacy related settings that can be used to
|
||||
// make the YouTube template etc. GDPR compliant.
|
||||
Privacy privacy.Config
|
||||
var ErrNoConfigFile = errors.New("Unable to locate config file or config directory. Perhaps you need to create a new site.\n Run `hugo help new` for details.\n")
|
||||
|
||||
// Services contains config for services such as Google Analytics etc.
|
||||
Services services.Config
|
||||
// LoadConfig loads Hugo configuration into a new Viper and then adds
|
||||
// a set of defaults.
|
||||
func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provider) error) (config.Provider, []string, error) {
|
||||
|
||||
if d.Environment == "" {
|
||||
d.Environment = hugo.EnvironmentProduction
|
||||
}
|
||||
|
||||
if len(d.Environ) == 0 {
|
||||
d.Environ = os.Environ()
|
||||
}
|
||||
|
||||
var configFiles []string
|
||||
|
||||
l := configLoader{ConfigSourceDescriptor: d, cfg: config.New()}
|
||||
|
||||
if err := l.applyConfigDefaults(); err != nil {
|
||||
return l.cfg, configFiles, err
|
||||
}
|
||||
|
||||
for _, name := range d.configFilenames() {
|
||||
var filename string
|
||||
filename, err := l.loadConfig(name)
|
||||
if err == nil {
|
||||
configFiles = append(configFiles, filename)
|
||||
} else if err != ErrNoConfigFile {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if d.AbsConfigDir != "" {
|
||||
dirnames, err := l.loadConfigFromConfigDir()
|
||||
if err == nil {
|
||||
configFiles = append(configFiles, dirnames...)
|
||||
} else if err != ErrNoConfigFile {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(bep) improve this. This is currently needed to get the merge correctly.
|
||||
if l.cfg.IsSet("languages") {
|
||||
langs := l.cfg.GetParams("languages")
|
||||
for _, lang := range langs {
|
||||
langp := lang.(maps.Params)
|
||||
if _, ok := langp["menus"]; !ok {
|
||||
langp["menus"] = make(maps.Params)
|
||||
}
|
||||
if _, ok := langp["params"]; !ok {
|
||||
langp["params"] = make(maps.Params)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
l.cfg.SetDefaultMergeStrategy()
|
||||
|
||||
// We create languages based on the settings, so we need to make sure that
|
||||
// all configuration is loaded/set before doing that.
|
||||
for _, d := range doWithConfig {
|
||||
if err := d(l.cfg); err != nil {
|
||||
return l.cfg, configFiles, err
|
||||
}
|
||||
}
|
||||
|
||||
// We made this a Glob pattern in Hugo 0.75, we don't need both.
|
||||
if l.cfg.GetBool("ignoreVendor") {
|
||||
helpers.Deprecated("--ignoreVendor", "--ignoreVendorPaths **", false)
|
||||
l.cfg.Set("ignoreVendorPaths", "**")
|
||||
}
|
||||
|
||||
// Some settings are used before we're done collecting all settings,
|
||||
// so apply OS environment both before and after.
|
||||
if err := l.applyOsEnvOverrides(d.Environ); err != nil {
|
||||
return l.cfg, configFiles, err
|
||||
}
|
||||
|
||||
modulesConfig, err := l.loadModulesConfig()
|
||||
if err != nil {
|
||||
return l.cfg, configFiles, err
|
||||
}
|
||||
|
||||
// Need to run these after the modules are loaded, but before
|
||||
// they are finalized.
|
||||
collectHook := func(m *modules.ModulesConfig) error {
|
||||
// We don't need the merge strategy configuration anymore,
|
||||
// remove it so it doesn't accidentaly show up in other settings.
|
||||
l.cfg.WalkParams(func(params ...config.KeyParams) bool {
|
||||
params[len(params)-1].Params.DeleteMergeStrategy()
|
||||
return false
|
||||
})
|
||||
|
||||
if err := l.loadLanguageSettings(nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mods := m.ActiveModules
|
||||
|
||||
// Apply default project mounts.
|
||||
if err := modules.ApplyProjectConfigDefaults(l.cfg, mods[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
_, modulesConfigFiles, err := l.collectModules(modulesConfig, l.cfg, collectHook)
|
||||
if err != nil {
|
||||
return l.cfg, configFiles, err
|
||||
}
|
||||
|
||||
configFiles = append(configFiles, modulesConfigFiles...)
|
||||
|
||||
if err := l.applyOsEnvOverrides(d.Environ); err != nil {
|
||||
return l.cfg, configFiles, err
|
||||
}
|
||||
|
||||
if err = l.applyConfigAliases(); err != nil {
|
||||
return l.cfg, configFiles, err
|
||||
}
|
||||
|
||||
return l.cfg, configFiles, err
|
||||
}
|
||||
|
||||
func loadSiteConfig(cfg config.Provider) (scfg SiteConfig, err error) {
|
||||
privacyConfig, err := privacy.DecodeConfig(cfg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
servicesConfig, err := services.DecodeConfig(cfg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
scfg.Privacy = privacyConfig
|
||||
scfg.Services = servicesConfig
|
||||
|
||||
return
|
||||
// LoadConfigDefault is a convenience method to load the default "config.toml" config.
|
||||
func LoadConfigDefault(fs afero.Fs) (config.Provider, error) {
|
||||
v, _, err := LoadConfig(ConfigSourceDescriptor{Fs: fs, Filename: "config.toml"})
|
||||
return v, err
|
||||
}
|
||||
|
||||
// ConfigSourceDescriptor describes where to find the config (e.g. config.toml etc.).
|
||||
|
@ -98,13 +200,6 @@ type ConfigSourceDescriptor struct {
|
|||
Environ []string
|
||||
}
|
||||
|
||||
func (d ConfigSourceDescriptor) configFilenames() []string {
|
||||
if d.Filename == "" {
|
||||
return []string{"config"}
|
||||
}
|
||||
return strings.Split(d.Filename, ",")
|
||||
}
|
||||
|
||||
func (d ConfigSourceDescriptor) configFileDir() string {
|
||||
if d.Path != "" {
|
||||
return d.Path
|
||||
|
@ -112,65 +207,97 @@ func (d ConfigSourceDescriptor) configFileDir() string {
|
|||
return d.WorkingDir
|
||||
}
|
||||
|
||||
// LoadConfigDefault is a convenience method to load the default "config.toml" config.
|
||||
func LoadConfigDefault(fs afero.Fs) (*viper.Viper, error) {
|
||||
v, _, err := LoadConfig(ConfigSourceDescriptor{Fs: fs, Filename: "config.toml"})
|
||||
return v, err
|
||||
func (d ConfigSourceDescriptor) configFilenames() []string {
|
||||
if d.Filename == "" {
|
||||
return []string{"config"}
|
||||
}
|
||||
return strings.Split(d.Filename, ",")
|
||||
}
|
||||
|
||||
var ErrNoConfigFile = errors.New("Unable to locate config file or config directory. Perhaps you need to create a new site.\n Run `hugo help new` for details.\n")
|
||||
// SiteConfig represents the config in .Site.Config.
|
||||
type SiteConfig struct {
|
||||
// This contains all privacy related settings that can be used to
|
||||
// make the YouTube template etc. GDPR compliant.
|
||||
Privacy privacy.Config
|
||||
|
||||
// LoadConfig loads Hugo configuration into a new Viper and then adds
|
||||
// a set of defaults.
|
||||
func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provider) error) (*viper.Viper, []string, error) {
|
||||
if d.Environment == "" {
|
||||
d.Environment = hugo.EnvironmentProduction
|
||||
}
|
||||
// Services contains config for services such as Google Analytics etc.
|
||||
Services services.Config
|
||||
}
|
||||
|
||||
if len(d.Environ) == 0 {
|
||||
d.Environ = os.Environ()
|
||||
}
|
||||
type configLoader struct {
|
||||
cfg config.Provider
|
||||
ConfigSourceDescriptor
|
||||
}
|
||||
|
||||
var configFiles []string
|
||||
// Handle some legacy values.
|
||||
func (l configLoader) applyConfigAliases() error {
|
||||
aliases := []types.KeyValueStr{{Key: "taxonomies", Value: "indexes"}}
|
||||
|
||||
v := viper.New()
|
||||
l := configLoader{ConfigSourceDescriptor: d}
|
||||
|
||||
for _, name := range d.configFilenames() {
|
||||
var filename string
|
||||
filename, err := l.loadConfig(name, v)
|
||||
if err == nil {
|
||||
configFiles = append(configFiles, filename)
|
||||
} else if err != ErrNoConfigFile {
|
||||
return nil, nil, err
|
||||
for _, alias := range aliases {
|
||||
if l.cfg.IsSet(alias.Key) {
|
||||
vv := l.cfg.Get(alias.Key)
|
||||
l.cfg.Set(alias.Value, vv)
|
||||
}
|
||||
}
|
||||
|
||||
if d.AbsConfigDir != "" {
|
||||
dirnames, err := l.loadConfigFromConfigDir(v)
|
||||
if err == nil {
|
||||
configFiles = append(configFiles, dirnames...)
|
||||
} else if err != ErrNoConfigFile {
|
||||
return nil, nil, err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l configLoader) applyConfigDefaults() error {
|
||||
defaultSettings := maps.Params{
|
||||
"cleanDestinationDir": false,
|
||||
"watch": false,
|
||||
"resourceDir": "resources",
|
||||
"publishDir": "public",
|
||||
"themesDir": "themes",
|
||||
"buildDrafts": false,
|
||||
"buildFuture": false,
|
||||
"buildExpired": false,
|
||||
"environment": hugo.EnvironmentProduction,
|
||||
"uglyURLs": false,
|
||||
"verbose": false,
|
||||
"ignoreCache": false,
|
||||
"canonifyURLs": false,
|
||||
"relativeURLs": false,
|
||||
"removePathAccents": false,
|
||||
"titleCaseStyle": "AP",
|
||||
"taxonomies": map[string]string{"tag": "tags", "category": "categories"},
|
||||
"permalinks": make(map[string]string),
|
||||
"sitemap": config.Sitemap{Priority: -1, Filename: "sitemap.xml"},
|
||||
"disableLiveReload": false,
|
||||
"pluralizeListTitles": true,
|
||||
"forceSyncStatic": false,
|
||||
"footnoteAnchorPrefix": "",
|
||||
"footnoteReturnLinkContents": "",
|
||||
"newContentEditor": "",
|
||||
"paginate": 10,
|
||||
"paginatePath": "page",
|
||||
"summaryLength": 70,
|
||||
"rssLimit": -1,
|
||||
"sectionPagesMenu": "",
|
||||
"disablePathToLower": false,
|
||||
"hasCJKLanguage": false,
|
||||
"enableEmoji": false,
|
||||
"pygmentsCodeFencesGuessSyntax": false,
|
||||
"defaultContentLanguage": "en",
|
||||
"defaultContentLanguageInSubdir": false,
|
||||
"enableMissingTranslationPlaceholders": false,
|
||||
"enableGitInfo": false,
|
||||
"ignoreFiles": make([]string, 0),
|
||||
"disableAliases": false,
|
||||
"debug": false,
|
||||
"disableFastRender": false,
|
||||
"timeout": "30s",
|
||||
"enableInlineShortcodes": false,
|
||||
}
|
||||
|
||||
if err := loadDefaultSettingsFor(v); err != nil {
|
||||
return v, configFiles, err
|
||||
}
|
||||
l.cfg.Merge("", defaultSettings)
|
||||
|
||||
// We create languages based on the settings, so we need to make sure that
|
||||
// all configuration is loaded/set before doing that.
|
||||
for _, d := range doWithConfig {
|
||||
if err := d(v); err != nil {
|
||||
return v, configFiles, err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is invoked both after we load the main config and at the end
|
||||
// to support OS env override of config options used in the module collector.
|
||||
applyOsEnvOverrides := func() error {
|
||||
if d.Environ == nil {
|
||||
func (l configLoader) applyOsEnvOverrides(environ []string) error {
|
||||
if len(environ) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -180,7 +307,7 @@ func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provid
|
|||
// The delimiter is the following rune, usually "_".
|
||||
const hugoEnvPrefix = "HUGO"
|
||||
var hugoEnv []types.KeyValueStr
|
||||
for _, v := range d.Environ {
|
||||
for _, v := range environ {
|
||||
key, val := config.SplitEnvVar(v)
|
||||
if strings.HasPrefix(key, hugoEnvPrefix) {
|
||||
delimiterAndKey := strings.TrimPrefix(key, hugoEnvPrefix)
|
||||
|
@ -202,7 +329,7 @@ func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provid
|
|||
}
|
||||
|
||||
for _, env := range hugoEnv {
|
||||
existing, nestedKey, owner, err := maps.GetNestedParamFn(env.Key, delim, v.Get)
|
||||
existing, nestedKey, owner, err := maps.GetNestedParamFn(env.Key, delim, l.cfg.Get)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -216,74 +343,90 @@ func LoadConfig(d ConfigSourceDescriptor, doWithConfig ...func(cfg config.Provid
|
|||
if owner != nil {
|
||||
owner[nestedKey] = val
|
||||
} else {
|
||||
v.Set(env.Key, val)
|
||||
l.cfg.Set(env.Key, val)
|
||||
}
|
||||
} else if nestedKey != "" {
|
||||
owner[nestedKey] = env.Value
|
||||
} else {
|
||||
v.Set(strings.ReplaceAll(env.Key, delim, "."), env.Value)
|
||||
// The container does not exist yet.
|
||||
l.cfg.Set(strings.ReplaceAll(env.Key, delim, "."), env.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l configLoader) collectModules(modConfig modules.Config, v1 config.Provider, hookBeforeFinalize func(m *modules.ModulesConfig) error) (modules.Modules, []string, error) {
|
||||
workingDir := l.WorkingDir
|
||||
if workingDir == "" {
|
||||
workingDir = v1.GetString("workingDir")
|
||||
}
|
||||
|
||||
if err := applyOsEnvOverrides(); err != nil {
|
||||
return v, configFiles, err
|
||||
themesDir := paths.AbsPathify(l.WorkingDir, v1.GetString("themesDir"))
|
||||
|
||||
var ignoreVendor glob.Glob
|
||||
if s := v1.GetString("ignoreVendorPaths"); s != "" {
|
||||
ignoreVendor, _ = hglob.GetGlob(hglob.NormalizePath(s))
|
||||
}
|
||||
|
||||
// We made this a Glob pattern in Hugo 0.75, we don't need both.
|
||||
if v.GetBool("ignoreVendor") {
|
||||
helpers.Deprecated("--ignoreVendor", "--ignoreVendorPaths **", false)
|
||||
v.Set("ignoreVendorPaths", "**")
|
||||
}
|
||||
|
||||
modulesConfig, err := l.loadModulesConfig(v)
|
||||
filecacheConfigs, err := filecache.DecodeConfig(l.Fs, v1)
|
||||
if err != nil {
|
||||
return v, configFiles, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Need to run these after the modules are loaded, but before
|
||||
// they are finalized.
|
||||
collectHook := func(m *modules.ModulesConfig) error {
|
||||
if err := loadLanguageSettings(v, nil); err != nil {
|
||||
return err
|
||||
v1.Set("filecacheConfigs", filecacheConfigs)
|
||||
|
||||
var configFilenames []string
|
||||
|
||||
hook := func(m *modules.ModulesConfig) error {
|
||||
for _, tc := range m.ActiveModules {
|
||||
if tc.ConfigFilename() != "" {
|
||||
if tc.Watch() {
|
||||
configFilenames = append(configFilenames, tc.ConfigFilename())
|
||||
}
|
||||
|
||||
mods := m.ActiveModules
|
||||
// Merge from theme config into v1 based on configured
|
||||
// merge strategy.
|
||||
v1.Merge("", tc.Cfg().Get(""))
|
||||
|
||||
// Apply default project mounts.
|
||||
if err := modules.ApplyProjectConfigDefaults(v, mods[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if hookBeforeFinalize != nil {
|
||||
return hookBeforeFinalize(m)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
_, modulesConfigFiles, err := l.collectModules(modulesConfig, v, collectHook)
|
||||
modulesClient := modules.NewClient(modules.ClientConfig{
|
||||
Fs: l.Fs,
|
||||
Logger: l.Logger,
|
||||
HookBeforeFinalize: hook,
|
||||
WorkingDir: workingDir,
|
||||
ThemesDir: themesDir,
|
||||
CacheDir: filecacheConfigs.CacheDirModules(),
|
||||
ModuleConfig: modConfig,
|
||||
IgnoreVendor: ignoreVendor,
|
||||
})
|
||||
|
||||
if err == nil && len(modulesConfigFiles) > 0 {
|
||||
configFiles = append(configFiles, modulesConfigFiles...)
|
||||
v1.Set("modulesClient", modulesClient)
|
||||
|
||||
moduleConfig, err := modulesClient.Collect()
|
||||
|
||||
// Avoid recreating these later.
|
||||
v1.Set("allModules", moduleConfig.ActiveModules)
|
||||
|
||||
if moduleConfig.GoModulesFilename != "" {
|
||||
// We want to watch this for changes and trigger rebuild on version
|
||||
// changes etc.
|
||||
configFilenames = append(configFilenames, moduleConfig.GoModulesFilename)
|
||||
}
|
||||
|
||||
if err := applyOsEnvOverrides(); err != nil {
|
||||
return v, configFiles, err
|
||||
}
|
||||
|
||||
return v, configFiles, err
|
||||
return moduleConfig.ActiveModules, configFilenames, err
|
||||
}
|
||||
|
||||
func loadLanguageSettings(cfg config.Provider, oldLangs langs.Languages) error {
|
||||
_, err := langs.LoadLanguageSettings(cfg, oldLangs)
|
||||
return err
|
||||
}
|
||||
|
||||
type configLoader struct {
|
||||
ConfigSourceDescriptor
|
||||
}
|
||||
|
||||
func (l configLoader) loadConfig(configName string, v *viper.Viper) (string, error) {
|
||||
func (l configLoader) loadConfig(configName string) (string, error) {
|
||||
baseDir := l.configFileDir()
|
||||
var baseFilename string
|
||||
if filepath.IsAbs(configName) {
|
||||
|
@ -318,24 +461,13 @@ func (l configLoader) loadConfig(configName string, v *viper.Viper) (string, err
|
|||
return "", l.wrapFileError(err, filename)
|
||||
}
|
||||
|
||||
if err = v.MergeConfigMap(m); err != nil {
|
||||
return "", l.wrapFileError(err, filename)
|
||||
}
|
||||
// Set overwrites keys of the same name, recursively.
|
||||
l.cfg.Set("", m)
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
|
||||
func (l configLoader) wrapFileError(err error, filename string) error {
|
||||
err, _ = herrors.WithFileContextForFile(
|
||||
err,
|
||||
filename,
|
||||
filename,
|
||||
l.Fs,
|
||||
herrors.SimpleLineMatcher)
|
||||
return err
|
||||
}
|
||||
|
||||
func (l configLoader) loadConfigFromConfigDir(v *viper.Viper) ([]string, error) {
|
||||
func (l configLoader) loadConfigFromConfigDir() ([]string, error) {
|
||||
sourceFs := l.Fs
|
||||
configDir := l.AbsConfigDir
|
||||
|
||||
|
@ -421,9 +553,8 @@ func (l configLoader) loadConfigFromConfigDir(v *viper.Viper) ([]string, error)
|
|||
// Migrate menu => menus etc.
|
||||
config.RenameKeys(root)
|
||||
|
||||
if err := v.MergeConfigMap(root); err != nil {
|
||||
return l.wrapFileError(err, path)
|
||||
}
|
||||
// Set will overwrite keys with the same name, recursively.
|
||||
l.cfg.Set("", root)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
@ -436,8 +567,13 @@ func (l configLoader) loadConfigFromConfigDir(v *viper.Viper) ([]string, error)
|
|||
return dirnames, nil
|
||||
}
|
||||
|
||||
func (l configLoader) loadModulesConfig(v1 *viper.Viper) (modules.Config, error) {
|
||||
modConfig, err := modules.DecodeConfig(v1)
|
||||
func (l configLoader) loadLanguageSettings(oldLangs langs.Languages) error {
|
||||
_, err := langs.LoadLanguageSettings(l.cfg, oldLangs)
|
||||
return err
|
||||
}
|
||||
|
||||
func (l configLoader) loadModulesConfig() (modules.Config, error) {
|
||||
modConfig, err := modules.DecodeConfig(l.cfg)
|
||||
if err != nil {
|
||||
return modules.Config{}, err
|
||||
}
|
||||
|
@ -445,211 +581,29 @@ func (l configLoader) loadModulesConfig(v1 *viper.Viper) (modules.Config, error)
|
|||
return modConfig, nil
|
||||
}
|
||||
|
||||
func (l configLoader) collectModules(modConfig modules.Config, v1 *viper.Viper, hookBeforeFinalize func(m *modules.ModulesConfig) error) (modules.Modules, []string, error) {
|
||||
workingDir := l.WorkingDir
|
||||
if workingDir == "" {
|
||||
workingDir = v1.GetString("workingDir")
|
||||
}
|
||||
|
||||
themesDir := paths.AbsPathify(l.WorkingDir, v1.GetString("themesDir"))
|
||||
|
||||
var ignoreVendor glob.Glob
|
||||
if s := v1.GetString("ignoreVendorPaths"); s != "" {
|
||||
ignoreVendor, _ = hglob.GetGlob(hglob.NormalizePath(s))
|
||||
}
|
||||
|
||||
filecacheConfigs, err := filecache.DecodeConfig(l.Fs, v1)
|
||||
func (configLoader) loadSiteConfig(cfg config.Provider) (scfg SiteConfig, err error) {
|
||||
privacyConfig, err := privacy.DecodeConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return
|
||||
}
|
||||
|
||||
v1.Set("filecacheConfigs", filecacheConfigs)
|
||||
|
||||
var configFilenames []string
|
||||
|
||||
hook := func(m *modules.ModulesConfig) error {
|
||||
for _, tc := range m.ActiveModules {
|
||||
if tc.ConfigFilename() != "" {
|
||||
if tc.Watch() {
|
||||
configFilenames = append(configFilenames, tc.ConfigFilename())
|
||||
servicesConfig, err := services.DecodeConfig(cfg)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if err := l.applyThemeConfig(v1, tc); err != nil {
|
||||
|
||||
scfg.Privacy = privacyConfig
|
||||
scfg.Services = servicesConfig
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (l configLoader) wrapFileError(err error, filename string) error {
|
||||
err, _ = herrors.WithFileContextForFile(
|
||||
err,
|
||||
filename,
|
||||
filename,
|
||||
l.Fs,
|
||||
herrors.SimpleLineMatcher)
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if hookBeforeFinalize != nil {
|
||||
return hookBeforeFinalize(m)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
modulesClient := modules.NewClient(modules.ClientConfig{
|
||||
Fs: l.Fs,
|
||||
Logger: l.Logger,
|
||||
HookBeforeFinalize: hook,
|
||||
WorkingDir: workingDir,
|
||||
ThemesDir: themesDir,
|
||||
CacheDir: filecacheConfigs.CacheDirModules(),
|
||||
ModuleConfig: modConfig,
|
||||
IgnoreVendor: ignoreVendor,
|
||||
})
|
||||
|
||||
v1.Set("modulesClient", modulesClient)
|
||||
|
||||
moduleConfig, err := modulesClient.Collect()
|
||||
|
||||
// Avoid recreating these later.
|
||||
v1.Set("allModules", moduleConfig.ActiveModules)
|
||||
|
||||
if moduleConfig.GoModulesFilename != "" {
|
||||
// We want to watch this for changes and trigger rebuild on version
|
||||
// changes etc.
|
||||
configFilenames = append(configFilenames, moduleConfig.GoModulesFilename)
|
||||
}
|
||||
|
||||
return moduleConfig.ActiveModules, configFilenames, err
|
||||
}
|
||||
|
||||
func (l configLoader) applyThemeConfig(v1 *viper.Viper, theme modules.Module) error {
|
||||
const (
|
||||
paramsKey = "params"
|
||||
languagesKey = "languages"
|
||||
menuKey = "menus"
|
||||
)
|
||||
|
||||
v2 := theme.Cfg()
|
||||
|
||||
for _, key := range []string{paramsKey, "outputformats", "mediatypes"} {
|
||||
l.mergeStringMapKeepLeft("", key, v1, v2)
|
||||
}
|
||||
|
||||
// Only add params and new menu entries, we do not add language definitions.
|
||||
if v1.IsSet(languagesKey) && v2.IsSet(languagesKey) {
|
||||
v1Langs := v1.GetStringMap(languagesKey)
|
||||
for k := range v1Langs {
|
||||
langParamsKey := languagesKey + "." + k + "." + paramsKey
|
||||
l.mergeStringMapKeepLeft(paramsKey, langParamsKey, v1, v2)
|
||||
}
|
||||
v2Langs := v2.GetStringMap(languagesKey)
|
||||
for k := range v2Langs {
|
||||
if k == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
langMenuKey := languagesKey + "." + k + "." + menuKey
|
||||
if v2.IsSet(langMenuKey) {
|
||||
// Only add if not in the main config.
|
||||
v2menus := v2.GetStringMap(langMenuKey)
|
||||
for k, v := range v2menus {
|
||||
menuEntry := menuKey + "." + k
|
||||
menuLangEntry := langMenuKey + "." + k
|
||||
if !v1.IsSet(menuEntry) && !v1.IsSet(menuLangEntry) {
|
||||
v1.Set(menuLangEntry, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add menu definitions from theme not found in project
|
||||
if v2.IsSet(menuKey) {
|
||||
v2menus := v2.GetStringMap(menuKey)
|
||||
for k, v := range v2menus {
|
||||
menuEntry := menuKey + "." + k
|
||||
if !v1.IsSet(menuEntry) {
|
||||
v1.SetDefault(menuEntry, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (configLoader) mergeStringMapKeepLeft(rootKey, key string, v1, v2 config.Provider) {
|
||||
if !v2.IsSet(key) {
|
||||
return
|
||||
}
|
||||
|
||||
if !v1.IsSet(key) && !(rootKey != "" && rootKey != key && v1.IsSet(rootKey)) {
|
||||
v1.Set(key, v2.Get(key))
|
||||
return
|
||||
}
|
||||
|
||||
m1 := v1.GetStringMap(key)
|
||||
m2 := v2.GetStringMap(key)
|
||||
|
||||
for k, v := range m2 {
|
||||
if _, found := m1[k]; !found {
|
||||
if rootKey != "" && v1.IsSet(rootKey+"."+k) {
|
||||
continue
|
||||
}
|
||||
m1[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadDefaultSettingsFor(v *viper.Viper) error {
|
||||
v.RegisterAlias("indexes", "taxonomies")
|
||||
|
||||
/*
|
||||
|
||||
TODO(bep) from 0.56 these are configured as module mounts.
|
||||
v.SetDefault("contentDir", "content")
|
||||
v.SetDefault("layoutDir", "layouts")
|
||||
v.SetDefault("assetDir", "assets")
|
||||
v.SetDefault("staticDir", "static")
|
||||
v.SetDefault("dataDir", "data")
|
||||
v.SetDefault("i18nDir", "i18n")
|
||||
v.SetDefault("archetypeDir", "archetypes")
|
||||
*/
|
||||
|
||||
v.SetDefault("cleanDestinationDir", false)
|
||||
v.SetDefault("watch", false)
|
||||
v.SetDefault("resourceDir", "resources")
|
||||
v.SetDefault("publishDir", "public")
|
||||
v.SetDefault("themesDir", "themes")
|
||||
v.SetDefault("buildDrafts", false)
|
||||
v.SetDefault("buildFuture", false)
|
||||
v.SetDefault("buildExpired", false)
|
||||
v.SetDefault("environment", hugo.EnvironmentProduction)
|
||||
v.SetDefault("uglyURLs", false)
|
||||
v.SetDefault("verbose", false)
|
||||
v.SetDefault("ignoreCache", false)
|
||||
v.SetDefault("canonifyURLs", false)
|
||||
v.SetDefault("relativeURLs", false)
|
||||
v.SetDefault("removePathAccents", false)
|
||||
v.SetDefault("titleCaseStyle", "AP")
|
||||
v.SetDefault("taxonomies", map[string]string{"tag": "tags", "category": "categories"})
|
||||
v.SetDefault("permalinks", make(map[string]string))
|
||||
v.SetDefault("sitemap", config.Sitemap{Priority: -1, Filename: "sitemap.xml"})
|
||||
v.SetDefault("disableLiveReload", false)
|
||||
v.SetDefault("pluralizeListTitles", true)
|
||||
v.SetDefault("forceSyncStatic", false)
|
||||
v.SetDefault("footnoteAnchorPrefix", "")
|
||||
v.SetDefault("footnoteReturnLinkContents", "")
|
||||
v.SetDefault("newContentEditor", "")
|
||||
v.SetDefault("paginate", 10)
|
||||
v.SetDefault("paginatePath", "page")
|
||||
v.SetDefault("summaryLength", 70)
|
||||
v.SetDefault("rssLimit", -1)
|
||||
v.SetDefault("sectionPagesMenu", "")
|
||||
v.SetDefault("disablePathToLower", false)
|
||||
v.SetDefault("hasCJKLanguage", false)
|
||||
v.SetDefault("enableEmoji", false)
|
||||
v.SetDefault("pygmentsCodeFencesGuessSyntax", false)
|
||||
v.SetDefault("defaultContentLanguage", "en")
|
||||
v.SetDefault("defaultContentLanguageInSubdir", false)
|
||||
v.SetDefault("enableMissingTranslationPlaceholders", false)
|
||||
v.SetDefault("enableGitInfo", false)
|
||||
v.SetDefault("ignoreFiles", make([]string, 0))
|
||||
v.SetDefault("disableAliases", false)
|
||||
v.SetDefault("debug", false)
|
||||
v.SetDefault("disableFastRender", false)
|
||||
v.SetDefault("timeout", "30s")
|
||||
v.SetDefault("enableInlineShortcodes", false)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -17,11 +17,15 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestLoadConfig(t *testing.T) {
|
||||
|
@ -77,12 +81,7 @@ func TestLoadConfigFromTheme(t *testing.T) {
|
|||
|
||||
c := qt.New(t)
|
||||
|
||||
mainConfigBasic := `
|
||||
theme = "test-theme"
|
||||
baseURL = "https://example.com/"
|
||||
|
||||
`
|
||||
mainConfig := `
|
||||
mainConfigTemplate := `
|
||||
theme = "test-theme"
|
||||
baseURL = "https://example.com/"
|
||||
|
||||
|
@ -90,9 +89,12 @@ baseURL = "https://example.com/"
|
|||
date = ["date","publishDate"]
|
||||
|
||||
[params]
|
||||
MERGE_PARAMS
|
||||
p1 = "p1 main"
|
||||
p2 = "p2 main"
|
||||
top = "top"
|
||||
[params.b]
|
||||
b1 = "b1 main"
|
||||
[params.b.c]
|
||||
bc1 = "bc1 main"
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."text/m1"]
|
||||
|
@ -130,7 +132,14 @@ expiryDate = ["date"]
|
|||
[params]
|
||||
p1 = "p1 theme"
|
||||
p2 = "p2 theme"
|
||||
p3 = "p3 theme"
|
||||
[params.b]
|
||||
b1 = "b1 theme"
|
||||
b2 = "b2 theme"
|
||||
[params.b.c]
|
||||
bc1 = "bc1 theme"
|
||||
bc2 = "bc2 theme"
|
||||
[params.b.c.d]
|
||||
bcd1 = "bcd1 theme"
|
||||
|
||||
[mediaTypes]
|
||||
[mediaTypes."text/m1"]
|
||||
|
@ -176,190 +185,137 @@ name = "menu-theme"
|
|||
|
||||
`
|
||||
|
||||
buildForStrategy := func(t testing.TB, s string) *sitesBuilder {
|
||||
mainConfig := strings.ReplaceAll(mainConfigTemplate, "MERGE_PARAMS", s)
|
||||
b := newTestSitesBuilder(t)
|
||||
b.WithConfigFile("toml", mainConfig).WithThemeConfigFile("toml", themeConfig)
|
||||
b.CreateSites().Build(BuildCfg{})
|
||||
return b.CreateSites().Build(BuildCfg{})
|
||||
}
|
||||
|
||||
got := b.Cfg.(*viper.Viper).AllSettings()
|
||||
c.Run("Merge default", func(c *qt.C) {
|
||||
b := buildForStrategy(c, "")
|
||||
|
||||
b.AssertObject(`
|
||||
map[string]interface {}{
|
||||
got := b.Cfg.Get("").(maps.Params)
|
||||
|
||||
b.Assert(got["params"], qt.DeepEquals, maps.Params{
|
||||
"b": maps.Params{
|
||||
"b1": "b1 main",
|
||||
"c": maps.Params{
|
||||
"bc1": "bc1 main",
|
||||
"bc2": "bc2 theme",
|
||||
"d": maps.Params{"bcd1": string("bcd1 theme")},
|
||||
},
|
||||
"b2": "b2 theme",
|
||||
},
|
||||
"p2": "p2 theme",
|
||||
"p1": "p1 main",
|
||||
"p2": "p2 main",
|
||||
"p3": "p3 theme",
|
||||
"top": "top",
|
||||
}`, got["params"])
|
||||
})
|
||||
|
||||
b.AssertObject(`
|
||||
map[string]interface {}{
|
||||
"date": []interface {}{
|
||||
"date",
|
||||
"publishDate",
|
||||
},
|
||||
}`, got["frontmatter"])
|
||||
|
||||
b.AssertObject(`
|
||||
map[string]interface {}{
|
||||
"text/m1": map[string]interface {}{
|
||||
"suffixes": []interface {}{
|
||||
"m1main",
|
||||
},
|
||||
},
|
||||
"text/m2": map[string]interface {}{
|
||||
"suffixes": []interface {}{
|
||||
b.Assert(got["mediatypes"], qt.DeepEquals, maps.Params{
|
||||
"text/m2": maps.Params{
|
||||
"suffixes": []interface{}{
|
||||
"m2theme",
|
||||
},
|
||||
},
|
||||
}`, got["mediatypes"])
|
||||
"text/m1": maps.Params{
|
||||
"suffixes": []interface{}{
|
||||
"m1main",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
b.AssertObject(`
|
||||
map[string]interface {}{
|
||||
"o1": map[string]interface {}{
|
||||
var eq = qt.CmpEquals(
|
||||
cmp.Comparer(func(m1, m2 media.Type) bool {
|
||||
if m1.SubType != m2.SubType {
|
||||
return false
|
||||
}
|
||||
return m1.FirstSuffix == m2.FirstSuffix
|
||||
}),
|
||||
)
|
||||
|
||||
mediaTypes := b.H.Sites[0].mediaTypesConfig
|
||||
m1, _ := mediaTypes.GetByType("text/m1")
|
||||
m2, _ := mediaTypes.GetByType("text/m2")
|
||||
|
||||
b.Assert(got["outputformats"], eq, maps.Params{
|
||||
"o1": maps.Params{
|
||||
"mediatype": m1,
|
||||
"basename": "o1main",
|
||||
"mediatype": Type{
|
||||
MainType: "text",
|
||||
SubType: "m1",
|
||||
Delimiter: ".",
|
||||
FirstSuffix: SuffixInfo{
|
||||
Suffix: "m1main",
|
||||
FullSuffix: ".m1main",
|
||||
},
|
||||
},
|
||||
},
|
||||
"o2": map[string]interface {}{
|
||||
"o2": maps.Params{
|
||||
"basename": "o2theme",
|
||||
"mediatype": Type{
|
||||
MainType: "text",
|
||||
SubType: "m2",
|
||||
Delimiter: ".",
|
||||
FirstSuffix: SuffixInfo{
|
||||
Suffix: "m2theme",
|
||||
FullSuffix: ".m2theme",
|
||||
"mediatype": m2,
|
||||
},
|
||||
},
|
||||
},
|
||||
}`, got["outputformats"])
|
||||
})
|
||||
|
||||
b.AssertObject(`map[string]interface {}{
|
||||
"en": map[string]interface {}{
|
||||
b.Assert(got["languages"], qt.DeepEquals, maps.Params{
|
||||
"en": maps.Params{
|
||||
"languagename": "English",
|
||||
"menus": map[string]interface {}{
|
||||
"theme": []map[string]interface {}{
|
||||
map[string]interface {}{
|
||||
"params": maps.Params{
|
||||
"pl2": "p2-en-theme",
|
||||
"pl1": "p1-en-main",
|
||||
},
|
||||
"menus": maps.Params{
|
||||
"main": []map[string]interface{}{
|
||||
{
|
||||
"name": "menu-lang-en-main",
|
||||
},
|
||||
},
|
||||
"theme": []map[string]interface{}{
|
||||
{
|
||||
"name": "menu-lang-en-theme",
|
||||
},
|
||||
},
|
||||
},
|
||||
"params": map[string]interface {}{
|
||||
"pl1": "p1-en-main",
|
||||
"pl2": "p2-en-theme",
|
||||
},
|
||||
},
|
||||
"nb": map[string]interface {}{
|
||||
"nb": maps.Params{
|
||||
"languagename": "Norsk",
|
||||
"menus": map[string]interface {}{
|
||||
"theme": []map[string]interface {}{
|
||||
map[string]interface {}{
|
||||
"name": "menu-lang-nb-theme",
|
||||
},
|
||||
},
|
||||
},
|
||||
"params": map[string]interface {}{
|
||||
"params": maps.Params{
|
||||
"top": "top-nb-theme",
|
||||
"pl1": "p1-nb-main",
|
||||
"pl2": "p2-nb-theme",
|
||||
},
|
||||
},
|
||||
}
|
||||
`, got["languages"])
|
||||
|
||||
b.AssertObject(`
|
||||
map[string]interface {}{
|
||||
"main": []map[string]interface {}{
|
||||
map[string]interface {}{
|
||||
"name": "menu-main-main",
|
||||
"menus": maps.Params{
|
||||
"main": []map[string]interface{}{
|
||||
{
|
||||
"name": "menu-lang-nb-main",
|
||||
},
|
||||
},
|
||||
"thememenu": []map[string]interface {}{
|
||||
map[string]interface {}{
|
||||
"name": "menu-theme",
|
||||
"theme": []map[string]interface{}{
|
||||
{
|
||||
"name": "menu-lang-nb-theme",
|
||||
},
|
||||
},
|
||||
"top": []map[string]interface {}{
|
||||
map[string]interface {}{
|
||||
"name": "menu-top-main",
|
||||
"top": []map[string]interface{}{
|
||||
{
|
||||
"name": "menu-lang-nb-top",
|
||||
},
|
||||
},
|
||||
}
|
||||
`, got["menus"])
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
c.Assert(got["baseurl"], qt.Equals, "https://example.com/")
|
||||
})
|
||||
|
||||
if true {
|
||||
return
|
||||
}
|
||||
// Test variants with only values from theme
|
||||
b = newTestSitesBuilder(t)
|
||||
b.WithConfigFile("toml", mainConfigBasic).WithThemeConfigFile("toml", themeConfig)
|
||||
b.CreateSites().Build(BuildCfg{})
|
||||
c.Run("Merge shallow", func(c *qt.C) {
|
||||
b := buildForStrategy(c, fmt.Sprintf("_merge=%q", "shallow"))
|
||||
|
||||
got = b.Cfg.(*viper.Viper).AllSettings()
|
||||
got := b.Cfg.Get("").(maps.Params)
|
||||
|
||||
b.AssertObject(`map[string]interface {}{
|
||||
"p1": "p1 theme",
|
||||
// Shallow merge, only add new keys to params.
|
||||
b.Assert(got["params"], qt.DeepEquals, maps.Params{
|
||||
"p1": "p1 main",
|
||||
"b": maps.Params{
|
||||
"b1": "b1 main",
|
||||
"c": maps.Params{
|
||||
"bc1": "bc1 main",
|
||||
},
|
||||
},
|
||||
"p2": "p2 theme",
|
||||
"p3": "p3 theme",
|
||||
"test-theme": map[string]interface {}{
|
||||
"p1": "p1 theme",
|
||||
"p2": "p2 theme",
|
||||
"p3": "p3 theme",
|
||||
},
|
||||
}`, got["params"])
|
||||
})
|
||||
})
|
||||
|
||||
c.Assert(got["languages"], qt.IsNil)
|
||||
b.AssertObject(`
|
||||
map[string]interface {}{
|
||||
"text/m1": map[string]interface {}{
|
||||
"suffix": "m1theme",
|
||||
},
|
||||
"text/m2": map[string]interface {}{
|
||||
"suffix": "m2theme",
|
||||
},
|
||||
}`, got["mediatypes"])
|
||||
|
||||
b.AssertObject(`
|
||||
map[string]interface {}{
|
||||
"o1": map[string]interface {}{
|
||||
"basename": "o1theme",
|
||||
"mediatype": Type{
|
||||
MainType: "text",
|
||||
SubType: "m1",
|
||||
Suffix: "m1theme",
|
||||
Delimiter: ".",
|
||||
},
|
||||
},
|
||||
"o2": map[string]interface {}{
|
||||
"basename": "o2theme",
|
||||
"mediatype": Type{
|
||||
MainType: "text",
|
||||
SubType: "m2",
|
||||
Suffix: "m2theme",
|
||||
Delimiter: ".",
|
||||
},
|
||||
},
|
||||
}`, got["outputformats"])
|
||||
b.AssertObject(`
|
||||
map[string]interface {}{
|
||||
"main": []interface {}{
|
||||
map[string]interface {}{
|
||||
"name": "menu-main-theme",
|
||||
},
|
||||
},
|
||||
"thememenu": []interface {}{
|
||||
map[string]interface {}{
|
||||
"name": "menu-theme",
|
||||
},
|
||||
},
|
||||
}`, got["menu"])
|
||||
}
|
||||
|
||||
func TestPrivacyConfig(t *testing.T) {
|
||||
|
@ -490,7 +446,12 @@ intSlice = [5,7,9]
|
|||
floatSlice = [3.14, 5.19]
|
||||
stringSlice = ["a", "b"]
|
||||
|
||||
[outputFormats]
|
||||
[outputFormats.ofbase]
|
||||
mediaType = "text/plain"
|
||||
|
||||
[params]
|
||||
paramWithNoEnvOverride="nooverride"
|
||||
[params.api_config]
|
||||
api_key="default_key"
|
||||
another_key="default another_key"
|
||||
|
@ -504,9 +465,16 @@ quality = 75
|
|||
|
||||
b.WithSourceFile("themes/mytheme/config.toml", `
|
||||
|
||||
[outputFormats]
|
||||
[outputFormats.oftheme]
|
||||
mediaType = "text/plain"
|
||||
[outputFormats.ofbase]
|
||||
mediaType = "application/xml"
|
||||
|
||||
[params]
|
||||
[params.mytheme_section]
|
||||
theme_param="themevalue"
|
||||
theme_param_nooverride="nooverride"
|
||||
[params.mytheme_section2]
|
||||
theme_param="themevalue2"
|
||||
|
||||
|
@ -530,14 +498,16 @@ theme_param="themevalue2"
|
|||
"HUGOxPARAMSxMYTHEME_SECTION2xTHEME_PARAM", "themevalue2_changed",
|
||||
"HUGO_PARAMS_EMPTY", ``,
|
||||
"HUGO_PARAMS_HTML", `<a target="_blank" />`,
|
||||
//
|
||||
// Issue #8618
|
||||
"HUGO_SERVICES_GOOGLEANALYTICS_ID", `gaid`,
|
||||
"HUGO_PARAMS_A_B_C", "abc",
|
||||
)
|
||||
|
||||
b.Build(BuildCfg{})
|
||||
|
||||
cfg := b.H.Cfg
|
||||
scfg := b.H.Sites[0].siteConfigConfig.Services
|
||||
s := b.H.Sites[0]
|
||||
scfg := s.siteConfigConfig.Services
|
||||
|
||||
c.Assert(cfg.Get("environment"), qt.Equals, "test")
|
||||
c.Assert(cfg.GetBool("enablegitinfo"), qt.Equals, false)
|
||||
|
@ -551,9 +521,23 @@ theme_param="themevalue2"
|
|||
c.Assert(cfg.Get("params.api_config.api_key"), qt.Equals, "new_key")
|
||||
c.Assert(cfg.Get("params.api_config.another_key"), qt.Equals, "default another_key")
|
||||
c.Assert(cfg.Get("params.mytheme_section.theme_param"), qt.Equals, "themevalue_changed")
|
||||
c.Assert(cfg.Get("params.mytheme_section.theme_param_nooverride"), qt.Equals, "nooverride")
|
||||
c.Assert(cfg.Get("params.mytheme_section2.theme_param"), qt.Equals, "themevalue2_changed")
|
||||
c.Assert(cfg.Get("params.empty"), qt.Equals, ``)
|
||||
c.Assert(cfg.Get("params.html"), qt.Equals, `<a target="_blank" />`)
|
||||
|
||||
params := cfg.Get("params").(maps.Params)
|
||||
c.Assert(params["paramwithnoenvoverride"], qt.Equals, "nooverride")
|
||||
c.Assert(cfg.Get("params.paramwithnoenvoverride"), qt.Equals, "nooverride")
|
||||
c.Assert(scfg.GoogleAnalytics.ID, qt.Equals, "gaid")
|
||||
c.Assert(cfg.Get("params.a.b"), qt.DeepEquals, maps.Params{
|
||||
"c": "abc",
|
||||
})
|
||||
|
||||
ofBase, _ := s.outputFormatsConfig.GetByName("ofbase")
|
||||
ofTheme, _ := s.outputFormatsConfig.GetByName("oftheme")
|
||||
|
||||
c.Assert(ofBase.MediaType, qt.Equals, media.TextType)
|
||||
c.Assert(ofTheme.MediaType, qt.Equals, media.TextType)
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ import (
|
|||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/hugolib/paths"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
)
|
||||
|
||||
func initConfig(fs afero.Fs, cfg config.Provider) error {
|
||||
|
@ -76,7 +76,7 @@ func initConfig(fs afero.Fs, cfg config.Provider) error {
|
|||
|
||||
func TestNewBaseFs(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
|
||||
fs := hugofs.NewMem(v)
|
||||
|
||||
|
@ -181,8 +181,8 @@ theme = ["atheme"]
|
|||
}
|
||||
}
|
||||
|
||||
func createConfig() *viper.Viper {
|
||||
v := viper.New()
|
||||
func createConfig() config.Provider {
|
||||
v := config.New()
|
||||
v.Set("contentDir", "mycontent")
|
||||
v.Set("i18nDir", "myi18n")
|
||||
v.Set("staticDir", "mystatic")
|
||||
|
@ -453,7 +453,7 @@ func countFilesAndGetFilenames(fs afero.Fs, dirname string) (int, []string, erro
|
|||
return counter, filenames, nil
|
||||
}
|
||||
|
||||
func setConfigAndWriteSomeFilesTo(fs afero.Fs, v *viper.Viper, key, val string, num int) {
|
||||
func setConfigAndWriteSomeFilesTo(fs afero.Fs, v config.Provider, key, val string, num int) {
|
||||
workingDir := v.GetString("workingDir")
|
||||
v.Set(key, val)
|
||||
fs.Mkdir(val, 0755)
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/modules/npm"
|
||||
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
|
@ -37,7 +38,6 @@ import (
|
|||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/testmodBuilder/mods"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestHugoModulesVariants(t *testing.T) {
|
||||
|
@ -45,7 +45,7 @@ func TestHugoModulesVariants(t *testing.T) {
|
|||
t.Skip("skip (relative) long running modules test when running locally")
|
||||
}
|
||||
|
||||
config := `
|
||||
tomlConfig := `
|
||||
baseURL="https://example.org"
|
||||
workingDir = %q
|
||||
|
||||
|
@ -56,7 +56,7 @@ path="github.com/gohugoio/hugoTestModule2"
|
|||
`
|
||||
|
||||
createConfig := func(workingDir, moduleOpts string) string {
|
||||
return fmt.Sprintf(config, workingDir, moduleOpts)
|
||||
return fmt.Sprintf(tomlConfig, workingDir, moduleOpts)
|
||||
}
|
||||
|
||||
newTestBuilder := func(t testing.TB, moduleOpts string) (*sitesBuilder, func()) {
|
||||
|
@ -65,7 +65,7 @@ path="github.com/gohugoio/hugoTestModule2"
|
|||
b.Assert(err, qt.IsNil)
|
||||
workingDir := filepath.Join(tempDir, "myhugosite")
|
||||
b.Assert(os.MkdirAll(workingDir, 0777), qt.IsNil)
|
||||
b.Fs = hugofs.NewDefault(viper.New())
|
||||
b.Fs = hugofs.NewDefault(config.New())
|
||||
b.WithWorkingDir(workingDir).WithConfigFile("toml", createConfig(workingDir, moduleOpts))
|
||||
b.WithTemplates(
|
||||
"index.html", `
|
||||
|
@ -333,7 +333,7 @@ func TestHugoModulesMatrix(t *testing.T) {
|
|||
for _, m := range testmods[:2] {
|
||||
c := qt.New(t)
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
|
||||
workingDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-modules-test")
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
@ -671,7 +671,7 @@ func TestModulesSymlinks(t *testing.T) {
|
|||
|
||||
c := qt.New(t)
|
||||
// We need to use the OS fs for this.
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
fs := hugofs.NewFrom(hugofs.Os, cfg)
|
||||
|
||||
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-mod-sym")
|
||||
|
@ -839,13 +839,13 @@ workingDir = %q
|
|||
|
||||
`
|
||||
|
||||
config := fmt.Sprintf(configTemplate, workingDir)
|
||||
tomlConfig := fmt.Sprintf(configTemplate, workingDir)
|
||||
|
||||
b := newTestSitesBuilder(t).Running()
|
||||
|
||||
b.Fs = hugofs.NewDefault(viper.New())
|
||||
b.Fs = hugofs.NewDefault(config.New())
|
||||
|
||||
b.WithWorkingDir(workingDir).WithConfigFile("toml", config)
|
||||
b.WithWorkingDir(workingDir).WithConfigFile("toml", tomlConfig)
|
||||
b.WithTemplatesAdded("index.html", `
|
||||
{{ .Title }}
|
||||
{{ .Content }}
|
||||
|
@ -960,16 +960,16 @@ workingDir = %q
|
|||
%s
|
||||
|
||||
`
|
||||
config := fmt.Sprintf(configTemplate, workingDir, mounts)
|
||||
config = strings.Replace(config, "WORKING_DIR", workingDir, -1)
|
||||
tomlConfig := fmt.Sprintf(configTemplate, workingDir, mounts)
|
||||
tomlConfig = strings.Replace(tomlConfig, "WORKING_DIR", workingDir, -1)
|
||||
|
||||
b := newTestSitesBuilder(c).Running()
|
||||
|
||||
b.Fs = hugofs.NewDefault(viper.New())
|
||||
b.Fs = hugofs.NewDefault(config.New())
|
||||
|
||||
os.MkdirAll(filepath.Join(workingDir, "content", "blog"), 0777)
|
||||
|
||||
b.WithWorkingDir(workingDir).WithConfigFile("toml", config)
|
||||
b.WithWorkingDir(workingDir).WithConfigFile("toml", tomlConfig)
|
||||
|
||||
return test{
|
||||
b: b,
|
||||
|
@ -1064,7 +1064,7 @@ func TestSiteWithGoModButNoModules(t *testing.T) {
|
|||
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-no-mod")
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("workingDir", workDir)
|
||||
fs := hugofs.NewFrom(hugofs.Os, cfg)
|
||||
|
||||
|
@ -1090,7 +1090,7 @@ func TestModuleAbsMount(t *testing.T) {
|
|||
absContentDir, clean2, err := htesting.CreateTempDir(hugofs.Os, "hugo-content")
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("workingDir", workDir)
|
||||
fs := hugofs.NewFrom(hugofs.Os, cfg)
|
||||
|
||||
|
|
|
@ -374,7 +374,8 @@ func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
|
|||
s.h = h
|
||||
}
|
||||
|
||||
if err := applyDeps(cfg, sites...); err != nil {
|
||||
var l configLoader
|
||||
if err := l.applyDeps(cfg, sites...); err != nil {
|
||||
return nil, errors.Wrap(err, "add site dependencies")
|
||||
}
|
||||
|
||||
|
@ -407,7 +408,7 @@ func (h *HugoSites) loadGitInfo() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func applyDeps(cfg deps.DepsCfg, sites ...*Site) error {
|
||||
func (l configLoader) applyDeps(cfg deps.DepsCfg, sites ...*Site) error {
|
||||
if cfg.TemplateProvider == nil {
|
||||
cfg.TemplateProvider = tplimpl.DefaultTemplateProvider
|
||||
}
|
||||
|
@ -446,7 +447,7 @@ func applyDeps(cfg deps.DepsCfg, sites ...*Site) error {
|
|||
|
||||
d.Site = s.Info
|
||||
|
||||
siteConfig, err := loadSiteConfig(s.language)
|
||||
siteConfig, err := l.loadSiteConfig(s.language)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "load site config")
|
||||
}
|
||||
|
@ -607,11 +608,12 @@ func (h *HugoSites) withSite(fn func(s *Site) error) error {
|
|||
func (h *HugoSites) createSitesFromConfig(cfg config.Provider) error {
|
||||
oldLangs, _ := h.Cfg.Get("languagesSorted").(langs.Languages)
|
||||
|
||||
if err := loadLanguageSettings(h.Cfg, oldLangs); err != nil {
|
||||
l := configLoader{cfg: h.Cfg}
|
||||
if err := l.loadLanguageSettings(oldLangs); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
depsCfg := deps.DepsCfg{Fs: h.Fs, Cfg: cfg}
|
||||
depsCfg := deps.DepsCfg{Fs: h.Fs, Cfg: l.cfg}
|
||||
|
||||
sites, err := createSitesFromConfig(depsCfg)
|
||||
if err != nil {
|
||||
|
@ -629,7 +631,8 @@ func (h *HugoSites) createSitesFromConfig(cfg config.Provider) error {
|
|||
s.h = h
|
||||
}
|
||||
|
||||
if err := applyDeps(depsCfg, sites...); err != nil {
|
||||
var cl configLoader
|
||||
if err := cl.applyDeps(depsCfg, sites...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -5,9 +5,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/fortytw2/leaktest"
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
|
@ -318,7 +316,7 @@ Some content.
|
|||
// https://github.com/gohugoio/hugo/issues/5375
|
||||
func TestSiteBuildTimeout(t *testing.T) {
|
||||
if !htesting.IsCI() {
|
||||
defer leaktest.CheckTimeout(t, 10*time.Second)()
|
||||
//defer leaktest.CheckTimeout(t, 10*time.Second)()
|
||||
}
|
||||
|
||||
b := newTestSitesBuilder(t)
|
||||
|
|
|
@ -21,11 +21,11 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// We have many tests for the different resize operations etc. in the resource package,
|
||||
|
@ -38,7 +38,7 @@ func TestImageOps(t *testing.T) {
|
|||
defer clean()
|
||||
|
||||
newBuilder := func(timeout interface{}) *sitesBuilder {
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workDir)
|
||||
v.Set("baseURL", "https://example.org")
|
||||
v.Set("timeout", timeout)
|
||||
|
|
|
@ -21,11 +21,10 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
@ -88,7 +87,7 @@ document.body.textContent = greeter(user);`
|
|||
c.Assert(err, qt.IsNil)
|
||||
defer clean()
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workDir)
|
||||
v.Set("disableKinds", []string{"taxonomy", "term", "page"})
|
||||
b := newTestSitesBuilder(t).WithLogger(loggers.NewWarningLogger())
|
||||
|
@ -162,7 +161,7 @@ func TestJSBuild(t *testing.T) {
|
|||
c.Assert(err, qt.IsNil)
|
||||
defer clean()
|
||||
|
||||
config := fmt.Sprintf(`
|
||||
tomlConfig := fmt.Sprintf(`
|
||||
baseURL = "https://example.org"
|
||||
workingDir = %q
|
||||
|
||||
|
@ -177,8 +176,8 @@ path="github.com/gohugoio/hugoTestProjectJSModImports"
|
|||
`, workDir)
|
||||
|
||||
b := newTestSitesBuilder(t)
|
||||
b.Fs = hugofs.NewDefault(viper.New())
|
||||
b.WithWorkingDir(workDir).WithConfigFile("toml", config).WithLogger(loggers.NewInfoLogger())
|
||||
b.Fs = hugofs.NewDefault(config.New())
|
||||
b.WithWorkingDir(workDir).WithConfigFile("toml", tomlConfig).WithLogger(loggers.NewInfoLogger())
|
||||
b.WithSourceFile("go.mod", `module github.com/gohugoio/tests/testHugoModules
|
||||
|
||||
go 1.15
|
||||
|
|
|
@ -16,13 +16,13 @@ package hugolib
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
)
|
||||
|
||||
func TestMinifyPublisher(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("minify", true)
|
||||
v.Set("baseURL", "https://example.org/")
|
||||
|
||||
|
|
|
@ -336,7 +336,7 @@ func (pm *pageMeta) setMetadata(parentBucket *pagesMapBucket, p *pageState, fron
|
|||
|
||||
if frontmatter != nil {
|
||||
// Needed for case insensitive fetching of params values
|
||||
maps.ToLower(frontmatter)
|
||||
maps.PrepareParams(frontmatter)
|
||||
if p.bucket != nil {
|
||||
// Check for any cascade define on itself.
|
||||
if cv, found := frontmatter["cascade"]; found {
|
||||
|
|
|
@ -37,7 +37,6 @@ import (
|
|||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
|
@ -786,7 +785,7 @@ func TestPageWithLastmodFromGitInfo(t *testing.T) {
|
|||
c := qt.New(t)
|
||||
|
||||
// We need to use the OS fs for this.
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
fs := hugofs.NewFrom(hugofs.Os, cfg)
|
||||
fs.Destination = &afero.MemMapFs{}
|
||||
|
||||
|
@ -1066,7 +1065,7 @@ func TestChompBOM(t *testing.T) {
|
|||
|
||||
func TestPageWithEmoji(t *testing.T) {
|
||||
for _, enableEmoji := range []bool{true, false} {
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("enableEmoji", enableEmoji)
|
||||
|
||||
b := newTestSitesBuilder(t).WithViper(v)
|
||||
|
|
|
@ -23,6 +23,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs/files"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
|
@ -35,7 +37,6 @@ import (
|
|||
"github.com/gohugoio/hugo/htesting"
|
||||
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
@ -352,12 +353,11 @@ func TestMultilingualDisableDefaultLanguage(t *testing.T) {
|
|||
|
||||
c := qt.New(t)
|
||||
_, cfg := newTestBundleSourcesMultilingual(t)
|
||||
|
||||
cfg.Set("disableLanguages", []string{"en"})
|
||||
|
||||
err := loadDefaultSettingsFor(cfg)
|
||||
l := configLoader{cfg: cfg}
|
||||
err := l.applyConfigDefaults()
|
||||
c.Assert(err, qt.IsNil)
|
||||
err = loadLanguageSettings(cfg, nil)
|
||||
err = l.loadLanguageSettings(nil)
|
||||
c.Assert(err, qt.Not(qt.IsNil))
|
||||
c.Assert(err.Error(), qt.Contains, "cannot disable default language")
|
||||
}
|
||||
|
@ -397,7 +397,7 @@ func TestPageBundlerSiteWitSymbolicLinksInContent(t *testing.T) {
|
|||
|
||||
c := qt.New(t)
|
||||
// We need to use the OS fs for this.
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
fs := hugofs.NewFrom(hugofs.Os, cfg)
|
||||
|
||||
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugosym")
|
||||
|
@ -696,7 +696,7 @@ Single content.
|
|||
b.AssertFileContent("public/section-not-bundle/single/index.html", "Section Single", "|<p>Single content.</p>")
|
||||
}
|
||||
|
||||
func newTestBundleSources(t testing.TB) (*hugofs.Fs, *viper.Viper) {
|
||||
func newTestBundleSources(t testing.TB) (*hugofs.Fs, config.Provider) {
|
||||
cfg, fs := newTestCfgBasic()
|
||||
c := qt.New(t)
|
||||
|
||||
|
@ -863,7 +863,7 @@ Content for 은행.
|
|||
return fs, cfg
|
||||
}
|
||||
|
||||
func newTestBundleSourcesMultilingual(t *testing.T) (*hugofs.Fs, *viper.Viper) {
|
||||
func newTestBundleSourcesMultilingual(t *testing.T) (*hugofs.Fs, config.Provider) {
|
||||
cfg, fs := newTestCfgBasic()
|
||||
|
||||
workDir := "/work"
|
||||
|
@ -1319,7 +1319,7 @@ func TestPageBundlerHome(t *testing.T) {
|
|||
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-bundler-home")
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("workingDir", workDir)
|
||||
fs := hugofs.NewFrom(hugofs.Os, cfg)
|
||||
|
||||
|
|
|
@ -130,7 +130,7 @@ func (c *pagesCollector) isCascadingEdit(dir contentDirKey) (bool, string) {
|
|||
|
||||
section = s
|
||||
|
||||
maps.ToLower(pf.FrontMatter)
|
||||
maps.PrepareParams(pf.FrontMatter)
|
||||
cascade1, ok := pf.FrontMatter["cascade"]
|
||||
hasCascade := n.p.bucket.cascade != nil && len(n.p.bucket.cascade) > 0
|
||||
if !ok {
|
||||
|
|
|
@ -16,17 +16,16 @@ package paths
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
)
|
||||
|
||||
func TestNewPaths(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
fs := hugofs.NewMem(v)
|
||||
|
||||
v.Set("languages", map[string]interface{}{
|
||||
|
|
|
@ -19,14 +19,14 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/common/hexec"
|
||||
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
@ -91,7 +91,7 @@ class Car2 {
|
|||
var logBuf bytes.Buffer
|
||||
logger := loggers.NewBasicLoggerForWriter(jww.LevelInfo, &logBuf)
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workDir)
|
||||
v.Set("disableKinds", []string{"taxonomy", "term", "page"})
|
||||
b := newTestSitesBuilder(t).WithLogger(logger)
|
||||
|
|
|
@ -20,6 +20,8 @@ import (
|
|||
"math/rand"
|
||||
"os"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass"
|
||||
|
||||
"path/filepath"
|
||||
|
@ -35,8 +37,6 @@ import (
|
|||
|
||||
"github.com/gohugoio/hugo/htesting"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
@ -65,7 +65,7 @@ func TestSCSSWithIncludePaths(t *testing.T) {
|
|||
c.Assert(err, qt.IsNil)
|
||||
defer clean()
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workDir)
|
||||
b := newTestSitesBuilder(c).WithLogger(loggers.NewErrorLogger())
|
||||
// Need to use OS fs for this.
|
||||
|
@ -130,7 +130,7 @@ func TestSCSSWithRegularCSSImport(t *testing.T) {
|
|||
c.Assert(err, qt.IsNil)
|
||||
defer clean()
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workDir)
|
||||
b := newTestSitesBuilder(c).WithLogger(loggers.NewErrorLogger())
|
||||
// Need to use OS fs for this.
|
||||
|
@ -230,7 +230,7 @@ func TestSCSSWithThemeOverrides(t *testing.T) {
|
|||
theme := "mytheme"
|
||||
themesDir := filepath.Join(workDir, "themes")
|
||||
themeDirs := filepath.Join(themesDir, theme)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workDir)
|
||||
v.Set("theme", theme)
|
||||
b := newTestSitesBuilder(c).WithLogger(loggers.NewErrorLogger())
|
||||
|
@ -345,7 +345,7 @@ func TestSCSSWithIncludePathsSass(t *testing.T) {
|
|||
c.Assert(err, qt.IsNil)
|
||||
defer clean1()
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workDir)
|
||||
v.Set("theme", "mytheme")
|
||||
b := newTestSitesBuilder(t).WithLogger(loggers.NewErrorLogger())
|
||||
|
@ -974,7 +974,7 @@ h1 {
|
|||
|
||||
var logBuf bytes.Buffer
|
||||
|
||||
newTestBuilder := func(v *viper.Viper) *sitesBuilder {
|
||||
newTestBuilder := func(v config.Provider) *sitesBuilder {
|
||||
v.Set("workingDir", workDir)
|
||||
v.Set("disableKinds", []string{"taxonomy", "term", "page"})
|
||||
logger := loggers.NewBasicLoggerForWriter(jww.LevelInfo, &logBuf)
|
||||
|
@ -997,7 +997,7 @@ Styles Content: Len: {{ len $styles.Content }}|
|
|||
return b
|
||||
}
|
||||
|
||||
b := newTestBuilder(viper.New())
|
||||
b := newTestBuilder(config.New())
|
||||
|
||||
cssDir := filepath.Join(workDir, "assets", "css", "components")
|
||||
b.Assert(os.MkdirAll(cssDir, 0777), qt.IsNil)
|
||||
|
@ -1049,7 +1049,7 @@ Styles Content: Len: 770878|
|
|||
build := func(s string, shouldFail bool) error {
|
||||
b.Assert(os.RemoveAll(filepath.Join(workDir, "public")), qt.IsNil)
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("build", map[string]interface{}{
|
||||
"useResourceCacheWhen": s,
|
||||
})
|
||||
|
|
|
@ -16,7 +16,7 @@ package hugolib
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
)
|
||||
|
||||
const robotTxtTemplate = `User-agent: Googlebot
|
||||
|
@ -28,7 +28,7 @@ const robotTxtTemplate = `User-agent: Googlebot
|
|||
func TestRobotsTXTOutput(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("baseURL", "http://auth/bub/")
|
||||
cfg.Set("enableRobotsTXT", true)
|
||||
|
||||
|
|
|
@ -20,11 +20,10 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/markup/asciidocext"
|
||||
"github.com/gohugoio/hugo/markup/rst"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gohugoio/hugo/parser/pageparser"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
|
||||
|
@ -1214,7 +1213,7 @@ title: "Hugo Rocks!"
|
|||
func TestShortcodeEmoji(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("enableEmoji", true)
|
||||
|
||||
builder := newTestSitesBuilder(t).WithViper(v)
|
||||
|
@ -1279,7 +1278,7 @@ func TestShortcodeRef(t *testing.T) {
|
|||
t.Run(fmt.Sprintf("plainIDAnchors=%t", plainIDAnchors), func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("baseURL", "https://example.org")
|
||||
v.Set("blackfriday", map[string]interface{}{
|
||||
"plainIDAnchors": plainIDAnchors,
|
||||
|
|
|
@ -77,7 +77,6 @@ import (
|
|||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Site contains all the information relevant for constructing a static
|
||||
|
@ -501,9 +500,9 @@ But this also means that your site configuration may not do what you expect. If
|
|||
var relatedContentConfig related.Config
|
||||
|
||||
if cfg.Language.IsSet("related") {
|
||||
relatedContentConfig, err = related.DecodeConfig(cfg.Language.Get("related"))
|
||||
relatedContentConfig, err = related.DecodeConfig(cfg.Language.GetParams("related"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "failed to decode related config")
|
||||
}
|
||||
} else {
|
||||
relatedContentConfig = related.DefaultConfig
|
||||
|
@ -574,7 +573,8 @@ func NewSite(cfg deps.DepsCfg) (*Site, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err = applyDeps(cfg, s); err != nil {
|
||||
var l configLoader
|
||||
if err = l.applyDeps(cfg, s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
@ -586,11 +586,11 @@ func NewSite(cfg deps.DepsCfg) (*Site, error) {
|
|||
// Note: This is mainly used in single site tests.
|
||||
// TODO(bep) test refactor -- remove
|
||||
func NewSiteDefaultLang(withTemplate ...func(templ tpl.TemplateManager) error) (*Site, error) {
|
||||
v := viper.New()
|
||||
if err := loadDefaultSettingsFor(v); err != nil {
|
||||
l := configLoader{cfg: config.New()}
|
||||
if err := l.applyConfigDefaults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newSiteForLang(langs.NewDefaultLanguage(v), withTemplate...)
|
||||
return newSiteForLang(langs.NewDefaultLanguage(l.cfg), withTemplate...)
|
||||
}
|
||||
|
||||
// NewEnglishSite creates a new site in English language.
|
||||
|
@ -598,11 +598,11 @@ func NewSiteDefaultLang(withTemplate ...func(templ tpl.TemplateManager) error) (
|
|||
// Note: This is mainly used in single site tests.
|
||||
// TODO(bep) test refactor -- remove
|
||||
func NewEnglishSite(withTemplate ...func(templ tpl.TemplateManager) error) (*Site, error) {
|
||||
v := viper.New()
|
||||
if err := loadDefaultSettingsFor(v); err != nil {
|
||||
l := configLoader{cfg: config.New()}
|
||||
if err := l.applyConfigDefaults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newSiteForLang(langs.NewLanguage("en", v), withTemplate...)
|
||||
return newSiteForLang(langs.NewLanguage("en", l.cfg), withTemplate...)
|
||||
}
|
||||
|
||||
// newSiteForLang creates a new site in the given language.
|
||||
|
@ -1314,7 +1314,7 @@ func (s *Site) initializeSiteInfo() error {
|
|||
return vvv
|
||||
}
|
||||
default:
|
||||
m := cast.ToStringMapBool(v)
|
||||
m := maps.ToStringMapBool(v)
|
||||
uglyURLs = func(p page.Page) bool {
|
||||
return m[p.Section()]
|
||||
}
|
||||
|
|
|
@ -19,13 +19,13 @@ import (
|
|||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestSiteWithPageOutputs(t *testing.T) {
|
||||
|
@ -333,7 +333,7 @@ func TestCreateSiteOutputFormats(t *testing.T) {
|
|||
page.KindSection: []string{"JSON"},
|
||||
}
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("outputs", outputsConfig)
|
||||
|
||||
outputs, err := createSiteOutputFormats(output.DefaultFormats, cfg.GetStringMap("outputs"), false)
|
||||
|
@ -358,7 +358,7 @@ func TestCreateSiteOutputFormats(t *testing.T) {
|
|||
// Issue #4528
|
||||
t.Run("Mixed case", func(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
|
||||
outputsConfig := map[string]interface{}{
|
||||
// Note that we in Hugo 0.53.0 renamed this Kind to "taxonomy",
|
||||
|
@ -380,7 +380,7 @@ func TestCreateSiteOutputFormatsInvalidConfig(t *testing.T) {
|
|||
page.KindHome: []string{"FOO", "JSON"},
|
||||
}
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("outputs", outputsConfig)
|
||||
|
||||
_, err := createSiteOutputFormats(output.DefaultFormats, cfg.GetStringMap("outputs"), false)
|
||||
|
@ -394,7 +394,7 @@ func TestCreateSiteOutputFormatsEmptyConfig(t *testing.T) {
|
|||
page.KindHome: []string{},
|
||||
}
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("outputs", outputsConfig)
|
||||
|
||||
outputs, err := createSiteOutputFormats(output.DefaultFormats, cfg.GetStringMap("outputs"), false)
|
||||
|
@ -409,7 +409,7 @@ func TestCreateSiteOutputFormatsCustomFormats(t *testing.T) {
|
|||
page.KindHome: []string{},
|
||||
}
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("outputs", outputsConfig)
|
||||
|
||||
var (
|
||||
|
|
|
@ -23,10 +23,9 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/gobuffalo/flect"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/publisher"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
|
@ -363,7 +362,7 @@ func TestMainSections(t *testing.T) {
|
|||
c := qt.New(t)
|
||||
for _, paramSet := range []bool{false, true} {
|
||||
c.Run(fmt.Sprintf("param-%t", paramSet), func(c *qt.C) {
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
if paramSet {
|
||||
v.Set("params", map[string]interface{}{
|
||||
"mainSections": []string{"a1", "a2"},
|
||||
|
|
|
@ -19,20 +19,19 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/identity"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestTemplateLookupOrder(t *testing.T) {
|
||||
var (
|
||||
fs *hugofs.Fs
|
||||
cfg *viper.Viper
|
||||
cfg config.Provider
|
||||
th testHelper
|
||||
)
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ import (
|
|||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/gohugoio/hugo/common/herrors"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
|
@ -39,7 +40,6 @@ import (
|
|||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/tpl"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
|
||||
|
@ -83,7 +83,7 @@ type sitesBuilder struct {
|
|||
// Default toml
|
||||
configFormat string
|
||||
configFileSet bool
|
||||
viperSet bool
|
||||
configSet bool
|
||||
|
||||
// Default is empty.
|
||||
// TODO(bep) revisit this and consider always setting it to something.
|
||||
|
@ -111,7 +111,7 @@ type filenameContent struct {
|
|||
}
|
||||
|
||||
func newTestSitesBuilder(t testing.TB) *sitesBuilder {
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
fs := hugofs.NewMem(v)
|
||||
|
||||
litterOptions := litter.Options{
|
||||
|
@ -140,7 +140,7 @@ func newTestSitesBuilderFromDepsCfg(t testing.TB, d deps.DepsCfg) *sitesBuilder
|
|||
|
||||
b.WithWorkingDir(workingDir)
|
||||
|
||||
return b.WithViper(d.Cfg.(*viper.Viper))
|
||||
return b.WithViper(d.Cfg.(config.Provider))
|
||||
}
|
||||
|
||||
func (s *sitesBuilder) Running() *sitesBuilder {
|
||||
|
@ -186,26 +186,26 @@ func (s *sitesBuilder) WithConfigTemplate(data interface{}, format, configTempla
|
|||
return s.WithConfigFile(format, b.String())
|
||||
}
|
||||
|
||||
func (s *sitesBuilder) WithViper(v *viper.Viper) *sitesBuilder {
|
||||
func (s *sitesBuilder) WithViper(v config.Provider) *sitesBuilder {
|
||||
s.T.Helper()
|
||||
if s.configFileSet {
|
||||
s.T.Fatal("WithViper: use Viper or config.toml, not both")
|
||||
}
|
||||
defer func() {
|
||||
s.viperSet = true
|
||||
s.configSet = true
|
||||
}()
|
||||
|
||||
// Write to a config file to make sure the tests follow the same code path.
|
||||
var buff bytes.Buffer
|
||||
m := v.AllSettings()
|
||||
m := v.Get("").(maps.Params)
|
||||
s.Assert(parser.InterfaceToConfig(m, metadecoders.TOML, &buff), qt.IsNil)
|
||||
return s.WithConfigFile("toml", buff.String())
|
||||
}
|
||||
|
||||
func (s *sitesBuilder) WithConfigFile(format, conf string) *sitesBuilder {
|
||||
s.T.Helper()
|
||||
if s.viperSet {
|
||||
s.T.Fatal("WithConfigFile: use Viper or config.toml, not both")
|
||||
if s.configSet {
|
||||
s.T.Fatal("WithConfigFile: use config.Config or config.toml, not both")
|
||||
}
|
||||
s.configFileSet = true
|
||||
filename := s.absFilename("config." + format)
|
||||
|
@ -845,14 +845,14 @@ func (th testHelper) replaceDefaultContentLanguageValue(value string) string {
|
|||
return value
|
||||
}
|
||||
|
||||
func loadTestConfig(fs afero.Fs, withConfig ...func(cfg config.Provider) error) (*viper.Viper, error) {
|
||||
func loadTestConfig(fs afero.Fs, withConfig ...func(cfg config.Provider) error) (config.Provider, error) {
|
||||
v, _, err := LoadConfig(ConfigSourceDescriptor{Fs: fs}, withConfig...)
|
||||
return v, err
|
||||
}
|
||||
|
||||
func newTestCfgBasic() (*viper.Viper, *hugofs.Fs) {
|
||||
func newTestCfgBasic() (config.Provider, *hugofs.Fs) {
|
||||
mm := afero.NewMemMapFs()
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("defaultContentLanguageInSubdir", true)
|
||||
|
||||
fs := hugofs.NewFrom(hugofs.NewBaseFileDecorator(mm), v)
|
||||
|
@ -860,7 +860,7 @@ func newTestCfgBasic() (*viper.Viper, *hugofs.Fs) {
|
|||
return v, fs
|
||||
}
|
||||
|
||||
func newTestCfg(withConfig ...func(cfg config.Provider) error) (*viper.Viper, *hugofs.Fs) {
|
||||
func newTestCfg(withConfig ...func(cfg config.Provider) error) (config.Provider, *hugofs.Fs) {
|
||||
mm := afero.NewMemMapFs()
|
||||
|
||||
v, err := loadTestConfig(mm, func(cfg config.Provider) error {
|
||||
|
|
|
@ -43,13 +43,13 @@ func LoadLanguageSettings(cfg config.Provider, oldLangs Languages) (c LanguagesC
|
|||
|
||||
var languages map[string]interface{}
|
||||
|
||||
languagesFromConfig := cfg.GetStringMap("languages")
|
||||
languagesFromConfig := cfg.GetParams("languages")
|
||||
disableLanguages := cfg.GetStringSlice("disableLanguages")
|
||||
|
||||
if len(disableLanguages) == 0 {
|
||||
languages = languagesFromConfig
|
||||
} else {
|
||||
languages = make(map[string]interface{})
|
||||
languages = make(maps.Params)
|
||||
for k, v := range languagesFromConfig {
|
||||
for _, disabled := range disableLanguages {
|
||||
if disabled == defaultLang {
|
||||
|
@ -57,7 +57,7 @@ func LoadLanguageSettings(cfg config.Provider, oldLangs Languages) (c LanguagesC
|
|||
}
|
||||
|
||||
if strings.EqualFold(k, disabled) {
|
||||
v.(map[string]interface{})["disabled"] = true
|
||||
v.(maps.Params)["disabled"] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -193,7 +193,7 @@ func toSortedLanguages(cfg config.Provider, l map[string]interface{}) (Languages
|
|||
case "params":
|
||||
m := maps.ToStringMap(v)
|
||||
// Needed for case insensitive fetching of params values
|
||||
maps.ToLower(m)
|
||||
maps.PrepareParams(m)
|
||||
for k, vv := range m {
|
||||
language.SetParam(k, vv)
|
||||
}
|
||||
|
|
|
@ -28,7 +28,6 @@ import (
|
|||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
|
||||
|
@ -500,9 +499,9 @@ func newDepsConfig(tp *TranslationProvider, cfg config.Provider, fs *hugofs.Fs)
|
|||
}
|
||||
}
|
||||
|
||||
func getConfig() *viper.Viper {
|
||||
v := viper.New()
|
||||
v.SetDefault("defaultContentLanguage", "en")
|
||||
func getConfig() config.Provider {
|
||||
v := config.New()
|
||||
v.Set("defaultContentLanguage", "en")
|
||||
v.Set("contentDir", "content")
|
||||
v.Set("dataDir", "data")
|
||||
v.Set("i18nDir", "i18n")
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
// These are the settings that should only be looked up in the global Viper
|
||||
|
@ -55,18 +54,20 @@ type Language struct {
|
|||
// absolute directory reference. It is what we get.
|
||||
ContentDir string
|
||||
|
||||
// Global config.
|
||||
Cfg config.Provider
|
||||
|
||||
// Language specific config.
|
||||
LocalCfg config.Provider
|
||||
|
||||
// Composite config.
|
||||
config.Provider
|
||||
|
||||
// These are params declared in the [params] section of the language merged with the
|
||||
// site's params, the most specific (language) wins on duplicate keys.
|
||||
params map[string]interface{}
|
||||
paramsMu sync.Mutex
|
||||
paramsSet bool
|
||||
|
||||
// These are config values, i.e. the settings declared outside of the [params] section of the language.
|
||||
// This is the map Hugo looks in when looking for configuration values (baseURL etc.).
|
||||
// Values in this map can also be fetched from the params map above.
|
||||
settings map[string]interface{}
|
||||
}
|
||||
|
||||
func (l *Language) String() string {
|
||||
|
@ -81,9 +82,12 @@ func NewLanguage(lang string, cfg config.Provider) *Language {
|
|||
for k, v := range cfg.GetStringMap("params") {
|
||||
params[k] = v
|
||||
}
|
||||
maps.ToLower(params)
|
||||
maps.PrepareParams(params)
|
||||
|
||||
l := &Language{Lang: lang, ContentDir: cfg.GetString("contentDir"), Cfg: cfg, params: params, settings: make(map[string]interface{})}
|
||||
localCfg := config.New()
|
||||
compositeConfig := config.NewCompositeConfig(cfg, localCfg)
|
||||
|
||||
l := &Language{Lang: lang, ContentDir: cfg.GetString("contentDir"), Cfg: cfg, LocalCfg: localCfg, Provider: compositeConfig, params: params}
|
||||
return l
|
||||
}
|
||||
|
||||
|
@ -133,7 +137,7 @@ func (l *Language) Params() maps.Params {
|
|||
l.paramsMu.Lock()
|
||||
defer l.paramsMu.Unlock()
|
||||
if !l.paramsSet {
|
||||
maps.ToLower(l.params)
|
||||
maps.PrepareParams(l.params)
|
||||
l.paramsSet = true
|
||||
}
|
||||
return l.params
|
||||
|
@ -183,42 +187,6 @@ func (l *Language) SetParam(k string, v interface{}) {
|
|||
l.params[k] = v
|
||||
}
|
||||
|
||||
// GetBool returns the value associated with the key as a boolean.
|
||||
func (l *Language) GetBool(key string) bool { return cast.ToBool(l.Get(key)) }
|
||||
|
||||
// GetString returns the value associated with the key as a string.
|
||||
func (l *Language) GetString(key string) string { return cast.ToString(l.Get(key)) }
|
||||
|
||||
// GetInt returns the value associated with the key as an int.
|
||||
func (l *Language) GetInt(key string) int { return cast.ToInt(l.Get(key)) }
|
||||
|
||||
// GetStringMap returns the value associated with the key as a map of interfaces.
|
||||
func (l *Language) GetStringMap(key string) map[string]interface{} {
|
||||
return maps.ToStringMap(l.Get(key))
|
||||
}
|
||||
|
||||
// GetStringMapString returns the value associated with the key as a map of strings.
|
||||
func (l *Language) GetStringMapString(key string) map[string]string {
|
||||
return cast.ToStringMapString(l.Get(key))
|
||||
}
|
||||
|
||||
// GetStringSlice returns the value associated with the key as a slice of strings.
|
||||
func (l *Language) GetStringSlice(key string) []string {
|
||||
return cast.ToStringSlice(l.Get(key))
|
||||
}
|
||||
|
||||
// Get returns a value associated with the key relying on specified language.
|
||||
// Get is case-insensitive for a key.
|
||||
//
|
||||
// Get returns an interface. For a specific value use one of the Get____ methods.
|
||||
func (l *Language) Get(key string) interface{} {
|
||||
local := l.GetLocal(key)
|
||||
if local != nil {
|
||||
return local
|
||||
}
|
||||
return l.Cfg.Get(key)
|
||||
}
|
||||
|
||||
// GetLocal gets a configuration value set on language level. It will
|
||||
// not fall back to any global value.
|
||||
// It will return nil if a value with the given key cannot be found.
|
||||
|
@ -228,31 +196,29 @@ func (l *Language) GetLocal(key string) interface{} {
|
|||
}
|
||||
key = strings.ToLower(key)
|
||||
if !globalOnlySettings[key] {
|
||||
if v, ok := l.settings[key]; ok {
|
||||
return v
|
||||
}
|
||||
return l.LocalCfg.Get(key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set sets the value for the key in the language's params.
|
||||
func (l *Language) Set(key string, value interface{}) {
|
||||
if l == nil {
|
||||
panic("language not set")
|
||||
func (l *Language) Set(k string, v interface{}) {
|
||||
k = strings.ToLower(k)
|
||||
if globalOnlySettings[k] {
|
||||
return
|
||||
}
|
||||
key = strings.ToLower(key)
|
||||
l.settings[key] = value
|
||||
l.Provider.Set(k, v)
|
||||
}
|
||||
|
||||
// Merge is currently not supported for Language.
|
||||
func (l *Language) Merge(key string, value interface{}) {
|
||||
panic("Not supported")
|
||||
}
|
||||
|
||||
// IsSet checks whether the key is set in the language or the related config store.
|
||||
func (l *Language) IsSet(key string) bool {
|
||||
key = strings.ToLower(key)
|
||||
|
||||
key = strings.ToLower(key)
|
||||
if !globalOnlySettings[key] {
|
||||
if _, ok := l.settings[key]; ok {
|
||||
return true
|
||||
}
|
||||
return l.Provider.IsSet(key)
|
||||
}
|
||||
return l.Cfg.IsSet(key)
|
||||
}
|
||||
|
|
|
@ -16,13 +16,14 @@ package langs
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestGetGlobalOnlySetting(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("defaultContentLanguageInSubdir", true)
|
||||
v.Set("contentDir", "content")
|
||||
v.Set("paginatePath", "page")
|
||||
|
@ -37,7 +38,7 @@ func TestGetGlobalOnlySetting(t *testing.T) {
|
|||
func TestLanguageParams(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("p1", "p1cfg")
|
||||
v.Set("contentDir", "content")
|
||||
|
||||
|
|
|
@ -22,17 +22,17 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/markup/converter"
|
||||
"github.com/gohugoio/hugo/markup/markup_config"
|
||||
"github.com/gohugoio/hugo/markup/tableofcontents"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestAsciidoctorDefaultArgs(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
mconf := markup_config.Default
|
||||
|
||||
p, err := Provider.New(
|
||||
|
@ -57,7 +57,7 @@ func TestAsciidoctorDefaultArgs(t *testing.T) {
|
|||
|
||||
func TestAsciidoctorNonDefaultArgs(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
mconf := markup_config.Default
|
||||
mconf.AsciidocExt.Backend = "manpage"
|
||||
mconf.AsciidocExt.NoHeaderOrFooter = false
|
||||
|
@ -88,7 +88,7 @@ func TestAsciidoctorNonDefaultArgs(t *testing.T) {
|
|||
|
||||
func TestAsciidoctorDisallowedArgs(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
mconf := markup_config.Default
|
||||
mconf.AsciidocExt.Backend = "disallowed-backend"
|
||||
mconf.AsciidocExt.Extensions = []string{"./disallowed-extension"}
|
||||
|
@ -117,7 +117,7 @@ func TestAsciidoctorDisallowedArgs(t *testing.T) {
|
|||
|
||||
func TestAsciidoctorArbitraryExtension(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
mconf := markup_config.Default
|
||||
mconf.AsciidocExt.Extensions = []string{"arbitrary-extension"}
|
||||
p, err := Provider.New(
|
||||
|
@ -142,7 +142,7 @@ func TestAsciidoctorArbitraryExtension(t *testing.T) {
|
|||
|
||||
func TestAsciidoctorDisallowedExtension(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
for _, disallowedExtension := range []string{
|
||||
`foo-bar//`,
|
||||
`foo-bar\\ `,
|
||||
|
@ -177,7 +177,7 @@ func TestAsciidoctorDisallowedExtension(t *testing.T) {
|
|||
|
||||
func TestAsciidoctorWorkingFolderCurrent(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
mconf := markup_config.Default
|
||||
mconf.AsciidocExt.WorkingFolderCurrent = true
|
||||
mconf.AsciidocExt.Trace = false
|
||||
|
@ -208,7 +208,7 @@ func TestAsciidoctorWorkingFolderCurrent(t *testing.T) {
|
|||
|
||||
func TestAsciidoctorWorkingFolderCurrentAndExtensions(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
mconf := markup_config.Default
|
||||
mconf.AsciidocExt.NoHeaderOrFooter = true
|
||||
mconf.AsciidocExt.Extensions = []string{"asciidoctor-html5s", "asciidoctor-diagram"}
|
||||
|
@ -247,7 +247,7 @@ func TestAsciidoctorWorkingFolderCurrentAndExtensions(t *testing.T) {
|
|||
|
||||
func TestAsciidoctorAttributes(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
mconf := markup_config.Default
|
||||
mconf.AsciidocExt.Attributes = map[string]string{"my-base-url": "https://gohugo.io/", "my-attribute-name": "my value"}
|
||||
mconf.AsciidocExt.Trace = false
|
||||
|
|
|
@ -16,7 +16,7 @@ package blackfriday
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/converter"
|
||||
|
||||
|
@ -140,7 +140,7 @@ func TestGetAllFlags(t *testing.T) {
|
|||
func TestConvert(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
p, err := Provider.New(converter.ProviderConfig{
|
||||
Cfg: viper.New(),
|
||||
Cfg: config.New(),
|
||||
})
|
||||
c.Assert(err, qt.IsNil)
|
||||
conv, err := p.New(converter.DocumentContext{})
|
||||
|
@ -153,7 +153,7 @@ func TestConvert(t *testing.T) {
|
|||
func TestGetHTMLRendererAnchors(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
p, err := Provider.New(converter.ProviderConfig{
|
||||
Cfg: viper.New(),
|
||||
Cfg: config.New(),
|
||||
})
|
||||
c.Assert(err, qt.IsNil)
|
||||
conv, err := p.New(converter.DocumentContext{
|
||||
|
|
|
@ -17,16 +17,15 @@ package highlight
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
c.Run("applyLegacyConfig", func(c *qt.C) {
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("pygmentsStyle", "hugo")
|
||||
v.Set("pygmentsUseClasses", false)
|
||||
v.Set("pygmentsCodeFences", false)
|
||||
|
|
|
@ -24,7 +24,6 @@ import (
|
|||
"github.com/gohugoio/hugo/markup/tableofcontents"
|
||||
"github.com/gohugoio/hugo/parser"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
@ -73,7 +72,7 @@ func normalizeConfig(m map[string]interface{}) {
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
vm := cast.ToStringMap(v)
|
||||
vm := maps.ToStringMap(v)
|
||||
// Changed from a bool in 0.81.0
|
||||
if vv, found := vm["attribute"]; found {
|
||||
if vvb, ok := vv.(bool); ok {
|
||||
|
|
|
@ -16,7 +16,7 @@ package markup_config
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
@ -26,7 +26,7 @@ func TestConfig(t *testing.T) {
|
|||
|
||||
c.Run("Decode", func(c *qt.C) {
|
||||
c.Parallel()
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
|
||||
v.Set("markup", map[string]interface{}{
|
||||
"goldmark": map[string]interface{}{
|
||||
|
@ -55,7 +55,7 @@ func TestConfig(t *testing.T) {
|
|||
|
||||
c.Run("legacy", func(c *qt.C) {
|
||||
c.Parallel()
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
|
||||
v.Set("blackfriday", map[string]interface{}{
|
||||
"angledQuotes": true,
|
||||
|
|
|
@ -16,17 +16,15 @@ package markup
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/converter"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/markup/converter"
|
||||
)
|
||||
|
||||
func TestConverterRegistry(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
r, err := NewConverterProvider(converter.ProviderConfig{Cfg: viper.New()})
|
||||
r, err := NewConverterProvider(converter.ProviderConfig{Cfg: config.New()})
|
||||
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert("goldmark", qt.Equals, r.GetMarkupConfig().DefaultMarkdownHandler)
|
||||
|
|
|
@ -16,7 +16,7 @@ package mmark
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
|
||||
|
@ -62,7 +62,7 @@ func TestGetMmarkExtensions(t *testing.T) {
|
|||
|
||||
func TestConvert(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
p, err := Provider.New(converter.ProviderConfig{Cfg: viper.New(), Logger: loggers.NewErrorLogger()})
|
||||
p, err := Provider.New(converter.ProviderConfig{Cfg: config.New(), Logger: loggers.NewErrorLogger()})
|
||||
c.Assert(err, qt.IsNil)
|
||||
conv, err := p.New(converter.DocumentContext{})
|
||||
c.Assert(err, qt.IsNil)
|
||||
|
|
|
@ -16,8 +16,9 @@ package org
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gohugoio/hugo/markup/converter"
|
||||
|
||||
|
@ -28,7 +29,7 @@ func TestConvert(t *testing.T) {
|
|||
c := qt.New(t)
|
||||
p, err := Provider.New(converter.ProviderConfig{
|
||||
Logger: loggers.NewErrorLogger(),
|
||||
Cfg: viper.New(),
|
||||
Cfg: config.New(),
|
||||
})
|
||||
c.Assert(err, qt.IsNil)
|
||||
conv, err := p.New(converter.DocumentContext{})
|
||||
|
|
|
@ -385,8 +385,8 @@ func DecodeTypes(mms ...map[string]interface{}) (Types, error) {
|
|||
return m, err
|
||||
}
|
||||
|
||||
vm := v.(map[string]interface{})
|
||||
maps.ToLower(vm)
|
||||
vm := maps.ToStringMap(v)
|
||||
maps.PrepareParams(vm)
|
||||
_, delimiterSet := vm["delimiter"]
|
||||
_, suffixSet := vm["suffix"]
|
||||
|
||||
|
|
|
@ -99,10 +99,10 @@ func decodeConfig(cfg config.Provider) (conf minifyConfig, err error) {
|
|||
|
||||
// Handle upstream renames.
|
||||
if td, found := m["tdewolff"]; found {
|
||||
tdm := cast.ToStringMap(td)
|
||||
tdm := maps.ToStringMap(td)
|
||||
for _, key := range []string{"css", "svg"} {
|
||||
if v, found := tdm[key]; found {
|
||||
vm := cast.ToStringMap(v)
|
||||
vm := maps.ToStringMap(v)
|
||||
if vv, found := vm["decimal"]; found {
|
||||
vvi := cast.ToInt(vv)
|
||||
if vvi > 0 {
|
||||
|
|
|
@ -16,14 +16,14 @@ package minifiers
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
|
||||
v.Set("minify", map[string]interface{}{
|
||||
"disablexml": true,
|
||||
|
@ -53,7 +53,7 @@ func TestConfig(t *testing.T) {
|
|||
|
||||
func TestConfigLegacy(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
|
||||
// This was a bool < Hugo v0.58.
|
||||
v.Set("minify", true)
|
||||
|
|
|
@ -19,16 +19,15 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/media"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
m, _ := New(media.DefaultTypes, output.DefaultFormats, v)
|
||||
|
||||
var rawJS string
|
||||
|
@ -76,7 +75,7 @@ func TestNew(t *testing.T) {
|
|||
|
||||
func TestConfigureMinify(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("minify", map[string]interface{}{
|
||||
"disablexml": true,
|
||||
"tdewolff": map[string]interface{}{
|
||||
|
@ -110,7 +109,7 @@ func TestConfigureMinify(t *testing.T) {
|
|||
|
||||
func TestJSONRoundTrip(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
m, _ := New(media.DefaultTypes, output.DefaultFormats, v)
|
||||
|
||||
for _, test := range []string{`{
|
||||
|
@ -148,7 +147,7 @@ func TestJSONRoundTrip(t *testing.T) {
|
|||
|
||||
func TestBugs(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
m, _ := New(media.DefaultTypes, output.DefaultFormats, v)
|
||||
|
||||
for _, test := range []struct {
|
||||
|
@ -171,7 +170,7 @@ func TestBugs(t *testing.T) {
|
|||
// Renamed to Precision in v2.7.0. Check that we support both.
|
||||
func TestDecodeConfigDecimalIsNowPrecision(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("minify", map[string]interface{}{
|
||||
"disablexml": true,
|
||||
"tdewolff": map[string]interface{}{
|
||||
|
|
|
@ -424,7 +424,7 @@ func (c *collector) applyThemeConfig(tc *moduleAdapter) error {
|
|||
if err != nil {
|
||||
c.logger.Warnf("Failed to read module config for %q in %q: %s", tc.Path(), themeTOML, err)
|
||||
} else {
|
||||
maps.ToLower(themeCfg)
|
||||
maps.PrepareParams(themeCfg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
)
|
||||
|
@ -122,7 +122,7 @@ func Pack(fs afero.Fs, fis []hugofs.FileMetaInfo) error {
|
|||
var commentsm map[string]interface{}
|
||||
comments, found := b.originalPackageJSON["comments"]
|
||||
if found {
|
||||
commentsm = cast.ToStringMap(comments)
|
||||
commentsm = maps.ToStringMap(comments)
|
||||
} else {
|
||||
commentsm = make(map[string]interface{})
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ func (b *packageBuilder) addm(source string, m map[string]interface{}) {
|
|||
// These packages will be added by order of import (project, module1, module2...),
|
||||
// so that should at least give the project control over the situation.
|
||||
if devDeps, found := m[devDependenciesKey]; found {
|
||||
mm := cast.ToStringMapString(devDeps)
|
||||
mm := maps.ToStringMapString(devDeps)
|
||||
for k, v := range mm {
|
||||
if _, added := b.devDependencies[k]; !added {
|
||||
b.devDependencies[k] = v
|
||||
|
@ -215,7 +215,7 @@ func (b *packageBuilder) addm(source string, m map[string]interface{}) {
|
|||
}
|
||||
|
||||
if deps, found := m[dependenciesKey]; found {
|
||||
mm := cast.ToStringMapString(deps)
|
||||
mm := maps.ToStringMapString(deps)
|
||||
for k, v := range mm {
|
||||
if _, added := b.dependencies[k]; !added {
|
||||
b.dependencies[k] = v
|
||||
|
|
|
@ -368,7 +368,11 @@ func decode(mediaTypes media.Types, input interface{}, output *Format) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return decoder.Decode(input)
|
||||
if err = decoder.Decode(input); err != nil {
|
||||
return errors.Wrap(err, "failed to decode output format configuration")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -22,12 +22,13 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/minifiers"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestClassCollector(t *testing.T) {
|
||||
|
@ -138,7 +139,7 @@ func TestClassCollector(t *testing.T) {
|
|||
if skipMinifyTest[test.name] {
|
||||
c.Skip("skip minify test")
|
||||
}
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
m, _ := minifiers.New(media.DefaultTypes, output.DefaultFormats, v)
|
||||
m.Minify(media.HTMLType, w, strings.NewReader(test.html))
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
"github.com/gohugoio/hugo/common/types"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
@ -404,16 +406,11 @@ func norm(num, min, max int) int {
|
|||
}
|
||||
|
||||
// DecodeConfig decodes a slice of map into Config.
|
||||
func DecodeConfig(in interface{}) (Config, error) {
|
||||
if in == nil {
|
||||
func DecodeConfig(m maps.Params) (Config, error) {
|
||||
if m == nil {
|
||||
return Config{}, errors.New("no related config provided")
|
||||
}
|
||||
|
||||
m, ok := in.(map[string]interface{})
|
||||
if !ok {
|
||||
return Config{}, fmt.Errorf("expected map[string]interface {} got %T", in)
|
||||
}
|
||||
|
||||
if len(m) == 0 {
|
||||
return Config{}, errors.New("empty related config provided")
|
||||
}
|
||||
|
|
|
@ -18,8 +18,9 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
@ -72,7 +73,7 @@ func newTestFd() *FrontMatterDescriptor {
|
|||
func TestFrontMatterNewConfig(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
|
||||
cfg.Set("frontmatter", map[string]interface{}{
|
||||
"date": []string{"publishDate", "LastMod"},
|
||||
|
@ -89,7 +90,7 @@ func TestFrontMatterNewConfig(t *testing.T) {
|
|||
c.Assert(fc.publishDate, qt.DeepEquals, []string{"date"})
|
||||
|
||||
// Default
|
||||
cfg = viper.New()
|
||||
cfg = config.New()
|
||||
fc, err = newFrontmatterConfig(cfg)
|
||||
c.Assert(err, qt.IsNil)
|
||||
c.Assert(fc.date, qt.DeepEquals, []string{"date", "publishdate", "pubdate", "published", "lastmod", "modified"})
|
||||
|
@ -117,7 +118,7 @@ func TestFrontMatterDatesHandlers(t *testing.T) {
|
|||
|
||||
for _, handlerID := range []string{":filename", ":fileModTime", ":git"} {
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
|
||||
cfg.Set("frontmatter", map[string]interface{}{
|
||||
"date": []string{handlerID, "date"},
|
||||
|
@ -157,7 +158,7 @@ func TestFrontMatterDatesCustomConfig(t *testing.T) {
|
|||
|
||||
c := qt.New(t)
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("frontmatter", map[string]interface{}{
|
||||
"date": []string{"mydate"},
|
||||
"lastmod": []string{"publishdate"},
|
||||
|
@ -204,7 +205,7 @@ func TestFrontMatterDatesDefaultKeyword(t *testing.T) {
|
|||
|
||||
c := qt.New(t)
|
||||
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
|
||||
cfg.Set("frontmatter", map[string]interface{}{
|
||||
"date": []string{"mydate", ":default"},
|
||||
|
|
|
@ -18,7 +18,7 @@ import (
|
|||
"html/template"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
|
@ -196,7 +196,7 @@ func doTestPagerNoPages(t *testing.T, paginator *Paginator) {
|
|||
func TestPaginationURLFactory(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("paginatePath", "zoo")
|
||||
|
||||
for _, uglyURLs := range []bool{false, true} {
|
||||
|
|
|
@ -29,7 +29,7 @@ import (
|
|||
"github.com/bep/gitmap"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
|
||||
"github.com/gohugoio/hugo/navigation"
|
||||
|
||||
|
@ -69,7 +69,7 @@ func newTestPageWithFile(filename string) *testPage {
|
|||
}
|
||||
|
||||
func newTestPathSpec() *helpers.PathSpec {
|
||||
return newTestPathSpecFor(viper.New())
|
||||
return newTestPathSpecFor(config.New())
|
||||
}
|
||||
|
||||
func newTestPathSpecFor(cfg config.Provider) *helpers.PathSpec {
|
||||
|
|
|
@ -130,7 +130,7 @@ func AssignMetadata(metadata []map[string]interface{}, resources ...resource.Res
|
|||
if found {
|
||||
m := maps.ToStringMap(params)
|
||||
// Needed for case insensitive fetching of params values
|
||||
maps.ToLower(m)
|
||||
maps.PrepareParams(m)
|
||||
ma.updateParams(m)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,17 +17,17 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/gohugoio/hugo/cache/filecache"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/gohugoio/hugo/output"
|
||||
"github.com/gohugoio/hugo/resources"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func NewTestResourceSpec() (*resources.Spec, error) {
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("baseURL", "https://example.org")
|
||||
cfg.Set("publishDir", "public")
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/afero"
|
||||
|
||||
"github.com/evanw/esbuild/pkg/api"
|
||||
|
||||
|
@ -30,7 +30,6 @@ import (
|
|||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/media"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -348,7 +347,7 @@ func toBuildOptions(opts Options) (buildOptions api.BuildOptions, err error) {
|
|||
|
||||
var defines map[string]string
|
||||
if opts.Defines != nil {
|
||||
defines = cast.ToStringMapString(opts.Defines)
|
||||
defines = maps.ToStringMapString(opts.Defines)
|
||||
}
|
||||
|
||||
// By default we only need to specify outDir and no outFile
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
|
||||
|
@ -22,7 +23,6 @@ import (
|
|||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/resources/resource"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type specDescriptor struct {
|
||||
|
@ -31,8 +31,8 @@ type specDescriptor struct {
|
|||
fs afero.Fs
|
||||
}
|
||||
|
||||
func createTestCfg() *viper.Viper {
|
||||
cfg := viper.New()
|
||||
func createTestCfg() config.Provider {
|
||||
cfg := config.New()
|
||||
cfg.Set("resourceDir", "resources")
|
||||
cfg.Set("contentDir", "content")
|
||||
cfg.Set("dataDir", "data")
|
||||
|
|
|
@ -19,6 +19,8 @@ import (
|
|||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/modules"
|
||||
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
|
@ -28,8 +30,6 @@ import (
|
|||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/helpers"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestEmptySourceFilesystem(t *testing.T) {
|
||||
|
@ -76,8 +76,8 @@ func TestUnicodeNorm(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func newTestConfig() *viper.Viper {
|
||||
v := viper.New()
|
||||
func newTestConfig() config.Provider {
|
||||
v := config.New()
|
||||
v.Set("contentDir", "content")
|
||||
v.Set("dataDir", "data")
|
||||
v.Set("i18nDir", "i18n")
|
||||
|
|
|
@ -15,18 +15,18 @@ package cast
|
|||
|
||||
import (
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/docshelper"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/tpl/internal"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// This file provides documentation support and is randomly put into this package.
|
||||
func init() {
|
||||
docsProvider := func() docshelper.DocProvider {
|
||||
d := &deps.Deps{
|
||||
Cfg: viper.New(),
|
||||
Cfg: config.New(),
|
||||
Log: loggers.NewErrorLogger(),
|
||||
BuildStartListeners: &deps.Listeners{},
|
||||
Site: page.NewDummyHugoSite(newTestConfig()),
|
||||
|
@ -46,8 +46,8 @@ func init() {
|
|||
docshelper.AddDocProviderFunc(docsProvider)
|
||||
}
|
||||
|
||||
func newTestConfig() *viper.Viper {
|
||||
v := viper.New()
|
||||
func newTestConfig() config.Provider {
|
||||
v := config.New()
|
||||
v.Set("contentDir", "content")
|
||||
return v
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
)
|
||||
|
||||
type tstNoStringer struct{}
|
||||
|
@ -986,7 +986,7 @@ func newDeps(cfg config.Provider) *deps.Deps {
|
|||
}
|
||||
|
||||
func newTestNs() *Namespace {
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("contentDir", "content")
|
||||
return New(newDeps(v))
|
||||
}
|
||||
|
|
|
@ -17,10 +17,10 @@ import (
|
|||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/gohugoio/hugo/tpl/internal"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
|
@ -28,7 +28,7 @@ func TestInit(t *testing.T) {
|
|||
var found bool
|
||||
var ns *internal.TemplateFuncsNamespace
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("contentDir", "content")
|
||||
langs.LoadLanguageSettings(v, nil)
|
||||
|
||||
|
|
|
@ -34,12 +34,12 @@ import (
|
|||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/gohugoio/hugo/langs"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
)
|
||||
|
||||
func TestScpGetLocal(t *testing.T) {
|
||||
t.Parallel()
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
fs := hugofs.NewMem(v)
|
||||
ps := helpers.FilePathSeparator
|
||||
|
||||
|
@ -144,7 +144,7 @@ func TestScpGetRemoteParallel(t *testing.T) {
|
|||
c.Assert(err, qt.IsNil)
|
||||
|
||||
for _, ignoreCache := range []bool{false} {
|
||||
cfg := viper.New()
|
||||
cfg := config.New()
|
||||
cfg.Set("ignoreCache", ignoreCache)
|
||||
cfg.Set("contentDir", "content")
|
||||
|
||||
|
@ -223,7 +223,7 @@ func newDeps(cfg config.Provider) *deps.Deps {
|
|||
}
|
||||
|
||||
func newTestNs() *Namespace {
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("contentDir", "content")
|
||||
return New(newDeps(v))
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"errors"
|
||||
"html/template"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
|
@ -71,7 +72,7 @@ func (ns *Namespace) Jsonify(args ...interface{}) (template.HTML, error) {
|
|||
case 2:
|
||||
var opts map[string]string
|
||||
|
||||
opts, err = cast.ToStringMapStringE(args[0])
|
||||
opts, err = maps.ToStringMapStringE(args[0])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
|
|
@ -16,20 +16,21 @@ package hugo
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/tpl/internal"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
c := qt.New(t)
|
||||
var found bool
|
||||
var ns *internal.TemplateFuncsNamespace
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("contentDir", "content")
|
||||
s := page.NewDummyHugoSite(v)
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@ import (
|
|||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type tstNoStringer struct{}
|
||||
|
@ -82,7 +82,7 @@ func TestNSConfig(t *testing.T) {
|
|||
t.Parallel()
|
||||
c := qt.New(t)
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", "/a/b")
|
||||
|
||||
ns := New(&deps.Deps{Fs: hugofs.NewMem(v)})
|
||||
|
|
|
@ -17,11 +17,12 @@ import (
|
|||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/hugofs"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestReadFile(t *testing.T) {
|
||||
|
@ -30,7 +31,7 @@ func TestReadFile(t *testing.T) {
|
|||
|
||||
workingDir := "/home/hugo"
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workingDir)
|
||||
|
||||
// f := newTestFuncsterWithViper(v)
|
||||
|
@ -68,7 +69,7 @@ func TestFileExists(t *testing.T) {
|
|||
|
||||
workingDir := "/home/hugo"
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workingDir)
|
||||
|
||||
ns := New(&deps.Deps{Fs: hugofs.NewMem(v)})
|
||||
|
@ -103,7 +104,7 @@ func TestStat(t *testing.T) {
|
|||
c := qt.New(t)
|
||||
workingDir := "/home/hugo"
|
||||
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("workingDir", workingDir)
|
||||
|
||||
ns := New(&deps.Deps{Fs: hugofs.NewMem(v)})
|
||||
|
|
|
@ -18,11 +18,11 @@ import (
|
|||
"testing"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var ns = New(&deps.Deps{Cfg: viper.New()})
|
||||
var ns = New(&deps.Deps{Cfg: config.New()})
|
||||
|
||||
type tstNoStringer struct{}
|
||||
|
||||
|
|
|
@ -282,7 +282,7 @@ func (ns *Namespace) ToCSS(args ...interface{}) (resource.Resource, error) {
|
|||
}
|
||||
|
||||
if m != nil {
|
||||
maps.ToLower(m)
|
||||
maps.PrepareParams(m)
|
||||
if t, found := m["transpiler"]; found {
|
||||
switch t {
|
||||
case transpilerDart, transpilerLibSass:
|
||||
|
|
|
@ -16,12 +16,13 @@ package site
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
"github.com/gohugoio/hugo/resources/page"
|
||||
"github.com/gohugoio/hugo/tpl/internal"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
|
@ -29,7 +30,7 @@ func TestInit(t *testing.T) {
|
|||
|
||||
var found bool
|
||||
var ns *internal.TemplateFuncsNamespace
|
||||
v := viper.New()
|
||||
v := config.New()
|
||||
v.Set("contentDir", "content")
|
||||
s := page.NewDummyHugoSite(v)
|
||||
|
||||
|
|
|
@ -16,12 +16,13 @@ package strings
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/config"
|
||||
|
||||
"github.com/gohugoio/hugo/htesting/hqt"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
"github.com/gohugoio/hugo/tpl/internal"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
|
@ -30,7 +31,7 @@ func TestInit(t *testing.T) {
|
|||
var ns *internal.TemplateFuncsNamespace
|
||||
|
||||
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
|
||||
ns = nsf(&deps.Deps{Cfg: viper.New()})
|
||||
ns = nsf(&deps.Deps{Cfg: config.New()})
|
||||
if ns.Name == name {
|
||||
found = true
|
||||
break
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue