Skip to content

Commit

Permalink
Allow themes to define output formats, media types and params
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Mar 18, 2018
1 parent 3cc3b2c commit f1fc9bd
Show file tree
Hide file tree
Showing 6 changed files with 354 additions and 8 deletions.
8 changes: 7 additions & 1 deletion Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,7 @@
name = "github.com/muesli/smartcrop"
branch = "master"


[[constraint]]
name = "github.com/sanity-io/litter"
version = "1.1.0"
110 changes: 109 additions & 1 deletion hugolib/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package hugolib
import (
"errors"
"fmt"
"path/filepath"

"io"
"strings"
Expand Down Expand Up @@ -85,7 +86,8 @@ func LoadConfig(d ConfigSourceDescriptor) (*viper.Viper, error) {
return v, err
}

return v, nil
return v, loadThemeConfig(fs, v)

}

func loadLanguageSettings(cfg config.Provider, oldLangs helpers.Languages) error {
Expand Down Expand Up @@ -201,6 +203,111 @@ func loadLanguageSettings(cfg config.Provider, oldLangs helpers.Languages) error
return nil
}

func loadThemeConfig(fs afero.Fs, v1 *viper.Viper) error {

theme := v1.GetString("theme")
if theme == "" {
return nil
}

themesDir := v1.GetString("themesDir")
configDir := filepath.Join(themesDir, theme)

var configPath string
var exists bool
var err error

for _, exensionsToCheck := range []string{"toml", "yaml", "yml", "json"} {
configPath = filepath.Join(configDir, "config."+exensionsToCheck)
exists, err = helpers.Exists(configPath, fs)
if err != nil {
return err
}
if exists {
break
}
}

if !exists {
return nil
}

v2 := viper.New()
v2.SetFs(fs)
v2.AutomaticEnv()
v2.SetEnvPrefix("hugo")
v2.SetConfigFile(configPath)

err = v2.ReadInConfig()
if err != nil {
return err
}

for _, key := range []string{"params", "outputformats", "mediatypes"} {
mergeStringMapKeepLeft(key, v1, v2)
}

themeLower := strings.ToLower(theme)
themeParamsNamespace := "params." + themeLower
// Set namespaced params
if v2.IsSet("params") && !v1.IsSet(themeParamsNamespace) {
v1.Set(themeParamsNamespace, v2.Get("params"))
}

// Only add params, we do not add language definitions.
if v1.IsSet("languages") && v2.IsSet("languages") {
v1Langs := v1.GetStringMap("languages")
for k, _ := range v1Langs {
mergeStringMapKeepLeft("languages."+k+".params", v1, v2)
}
v2Langs := v2.GetStringMap("languages")
for k, _ := range v2Langs {
langParamsKey := "languages." + k + ".params"
langParamsThemeNamespace := langParamsKey + "." + themeLower
// Set namespaced params
if v2.IsSet(langParamsKey) && !v1.IsSet(langParamsThemeNamespace) {
v1.Set(langParamsThemeNamespace, v2.Get(langParamsKey))
}
}
}

// Add menu definitions from theme not found in project
if v2.IsSet("menu") {
v2menus := v2.GetStringMap("menu")
for k, v := range v2menus {
if !v1.IsSet("menu." + k) {
v1.Set("menu."+k, v)
}
}
}

return nil

}

func mergeStringMapKeepLeft(key string, v1, v2 *viper.Viper) {
if !v1.IsSet(key) {
if v2.IsSet(key) {
v1.Set(key, v2.Get(key))
}
return
}

if !v2.IsSet(key) {
return
}

m1 := v1.GetStringMap(key)
m2 := v2.GetStringMap(key)

for k, v := range m2 {
if _, found := m1[k]; !found {
m1[k] = v
}
}

}

func loadDefaultSettingsFor(v *viper.Viper) error {

c, err := helpers.NewContentSpec(v)
Expand Down Expand Up @@ -282,4 +389,5 @@ lastmod = ["lastmod" ,":fileModTime", ":default"]
}

return loadLanguageSettings(v, nil)

}
205 changes: 200 additions & 5 deletions hugolib/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import (
"testing"

"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)

func TestLoadConfig(t *testing.T) {
t.Parallel()

assert := require.New(t)

// Add a random config variable for testing.
// side = page in Norwegian.
configContent := `
Expand All @@ -37,13 +39,16 @@ func TestLoadConfig(t *testing.T) {
cfg, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Name: "hugo.toml"})
require.NoError(t, err)

assert.Equal(t, "side", cfg.GetString("paginatePath"))
assert.Equal("side", cfg.GetString("paginatePath"))
// default
assert.Equal(t, "layouts", cfg.GetString("layoutDir"))
assert.Equal("layouts", cfg.GetString("layoutDir"))
}

func TestLoadMultiConfig(t *testing.T) {
t.Parallel()

assert := require.New(t)

// Add a random config variable for testing.
// side = page in Norwegian.
configContentBase := `
Expand All @@ -62,6 +67,196 @@ func TestLoadMultiConfig(t *testing.T) {
cfg, err := LoadConfig(ConfigSourceDescriptor{Fs: mm, Name: "base.toml,override.toml"})
require.NoError(t, err)

assert.Equal(t, "top", cfg.GetString("paginatePath"))
assert.Equal(t, "same", cfg.GetString("DontChange"))
assert.Equal("top", cfg.GetString("paginatePath"))
assert.Equal("same", cfg.GetString("DontChange"))
}

func TestLoadConfigFromTheme(t *testing.T) {
t.Parallel()

assert := require.New(t)

mainConfig := `
theme = "test-theme"
baseURL = "https://example.com/"
[frontmatter]
date = ["date","publishDate"]
[params]
p1 = "p1 main"
p2 = "p2 main"
[mediaTypes]
[mediaTypes."text/m1"]
suffix = "m1main"
[outputFormats.o1]
mediaType = "text/m1"
baseName = "o1main"
[languages]
[languages.en]
languageName = "English"
[languages.en.params]
pl1 = "p1-en-main"
[languages.nb]
languageName = "Norsk"
[languages.nb.params]
pl1 = "p1-nb-main"
[[menu.main]]
name = "menu-main-main"
`

themeConfig := `
baseURL = "http://bep.is/"
# Can not be set in theme.
[frontmatter]
expiryDate = ["date"]
[params]
p1 = "p1 theme"
p2 = "p2 theme"
p3 = "p3 theme"
[mediaTypes]
[mediaTypes."text/m1"]
suffix = "m1theme"
[mediaTypes."text/m2"]
suffix = "m2theme"
[outputFormats.o1]
mediaType = "text/m1"
baseName = "o1theme"
[outputFormats.o2]
mediaType = "text/m2"
baseName = "o2theme"
[languages]
[languages.en]
languageName = "English2"
[languages.en.params]
pl1 = "p1-en-theme"
pl2 = "p2-en-theme"
[languages.nb]
languageName = "Norsk2"
[languages.nb.params]
pl1 = "p1-nb-theme"
pl2 = "p2-nb-theme"
[[menu.main]]
name = "menu-main-theme"
[[menu.thememenu]]
name = "menu-theme"
`

// TODO(bep) config watch
b := newTestSitesBuilder(t)
b.WithConfigFile("toml", mainConfig).WithThemeConfigFile("toml", themeConfig)
b.CreateSites().Build(BuildCfg{})

got := b.Cfg.(*viper.Viper).AllSettings()

b.AssertObject(`
map[string]interface {}{
"p1": "p1 main",
"p2": "p2 main",
"p3": "p3 theme",
"test-theme": map[string]interface {}{
"p1": "p1 theme",
"p2": "p2 theme",
"p3": "p3 theme",
},
}`, got["params"])

b.AssertObject(`
map[string]interface {}{
"date": []interface {}{
"date",
"publishDate",
},
}`, got["frontmatter"])

b.AssertObject(`
map[string]interface {}{
"text/m1": map[string]interface {}{
"suffix": "m1main",
},
"text/m2": map[string]interface {}{
"suffix": "m2theme",
},
}`, got["mediatypes"])

b.AssertObject(`
map[string]interface {}{
"o1": map[string]interface {}{
"basename": "o1main",
"mediatype": Type{
MainType: "text",
SubType: "m1",
Suffix: "m1main",
Delimiter: ".",
},
},
"o2": map[string]interface {}{
"basename": "o2theme",
"mediatype": Type{
MainType: "text",
SubType: "m2",
Suffix: "m2theme",
Delimiter: ".",
},
},
}`, got["outputformats"])

b.AssertObject(`
map[string]interface {}{
"en": map[string]interface {}{
"languagename": "English",
"params": map[string]interface {}{
"pl1": "p1-en-main",
"pl2": "p2-en-theme",
"test-theme": map[string]interface {}{
"pl1": "p1-en-theme",
"pl2": "p2-en-theme",
},
},
},
"nb": map[string]interface {}{
"languagename": "Norsk",
"params": map[string]interface {}{
"pl1": "p1-nb-main",
"pl2": "p2-nb-theme",
"test-theme": map[string]interface {}{
"pl1": "p1-nb-theme",
"pl2": "p2-nb-theme",
},
},
},
}
`, got["languages"])

b.AssertObject(`
map[string]interface {}{
"main": []interface {}{
map[string]interface {}{
"name": "menu-main-main",
},
},
"thememenu": []interface {}{
map[string]interface {}{
"name": "menu-theme",
},
},
}
`, got["menu"])

assert.Equal("https://example.com/", got["baseurl"])

}
Loading

0 comments on commit f1fc9bd

Please sign in to comment.