mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
parent
7cac19b1e3
commit
596e0e98e4
6 changed files with 216 additions and 70 deletions
|
@ -57,8 +57,7 @@ func benchmark(cmd *cobra.Command, args []string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for i := 0; i < benchmarkTimes; i++ {
|
for i := 0; i < benchmarkTimes; i++ {
|
||||||
_ = buildSites()
|
_ = resetAndbuildSites(false)
|
||||||
Hugo.Reset()
|
|
||||||
}
|
}
|
||||||
pprof.WriteHeapProfile(f)
|
pprof.WriteHeapProfile(f)
|
||||||
f.Close()
|
f.Close()
|
||||||
|
@ -76,8 +75,7 @@ func benchmark(cmd *cobra.Command, args []string) error {
|
||||||
pprof.StartCPUProfile(f)
|
pprof.StartCPUProfile(f)
|
||||||
defer pprof.StopCPUProfile()
|
defer pprof.StopCPUProfile()
|
||||||
for i := 0; i < benchmarkTimes; i++ {
|
for i := 0; i < benchmarkTimes; i++ {
|
||||||
_ = buildSites()
|
_ = resetAndbuildSites(false)
|
||||||
Hugo.Reset()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -419,14 +419,6 @@ func InitializeConfig(subCmdVs ...*cobra.Command) error {
|
||||||
helpers.HugoReleaseVersion(), minVersion)
|
helpers.HugoReleaseVersion(), minVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
h, err := hugolib.NewHugoSitesFromConfiguration()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
//TODO(bep) ml refactor ...
|
|
||||||
Hugo = h
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -444,8 +436,7 @@ func watchConfig() {
|
||||||
viper.OnConfigChange(func(e fsnotify.Event) {
|
viper.OnConfigChange(func(e fsnotify.Event) {
|
||||||
fmt.Println("Config file changed:", e.Name)
|
fmt.Println("Config file changed:", e.Name)
|
||||||
// Force a full rebuild
|
// Force a full rebuild
|
||||||
Hugo.Reset()
|
utils.CheckErr(reCreateAndbuildSites(true))
|
||||||
utils.CheckErr(buildSites(true))
|
|
||||||
if !viper.GetBool("DisableLiveReload") {
|
if !viper.GetBool("DisableLiveReload") {
|
||||||
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
|
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
|
||||||
livereload.ForceRefresh()
|
livereload.ForceRefresh()
|
||||||
|
@ -638,13 +629,39 @@ func getDirList() []string {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildSites(watching ...bool) (err error) {
|
func reCreateAndbuildSites(watching bool) (err error) {
|
||||||
fmt.Println("Started building sites ...")
|
fmt.Println("Started building sites ...")
|
||||||
w := len(watching) > 0 && watching[0]
|
return Hugo.Build(hugolib.BuildCfg{CreateSitesFromConfig: true, Watching: watching, PrintStats: true})
|
||||||
return Hugo.Build(hugolib.BuildCfg{Watching: w, PrintStats: true})
|
}
|
||||||
|
|
||||||
|
func resetAndbuildSites(watching bool) (err error) {
|
||||||
|
fmt.Println("Started building sites ...")
|
||||||
|
return Hugo.Build(hugolib.BuildCfg{ResetState: true, Watching: watching, PrintStats: true})
|
||||||
|
}
|
||||||
|
|
||||||
|
func initSites() error {
|
||||||
|
if Hugo != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
h, err := hugolib.NewHugoSitesFromConfiguration()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
Hugo = h
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildSites(watching bool) (err error) {
|
||||||
|
initSites()
|
||||||
|
fmt.Println("Started building sites ...")
|
||||||
|
return Hugo.Build(hugolib.BuildCfg{Watching: watching, PrintStats: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
func rebuildSites(events []fsnotify.Event) error {
|
func rebuildSites(events []fsnotify.Event) error {
|
||||||
|
initSites()
|
||||||
return Hugo.Rebuild(hugolib.BuildCfg{PrintStats: true}, events...)
|
return Hugo.Rebuild(hugolib.BuildCfg{PrintStats: true}, events...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,24 +40,25 @@ type HugoSites struct {
|
||||||
// NewHugoSites creates a new collection of sites given the input sites, building
|
// NewHugoSites creates a new collection of sites given the input sites, building
|
||||||
// a language configuration based on those.
|
// a language configuration based on those.
|
||||||
func NewHugoSites(sites ...*Site) (*HugoSites, error) {
|
func NewHugoSites(sites ...*Site) (*HugoSites, error) {
|
||||||
languages := make(Languages, len(sites))
|
langConfig, err := newMultiLingualFromSites(sites...)
|
||||||
for i, s := range sites {
|
|
||||||
if s.Language == nil {
|
if err != nil {
|
||||||
return nil, errors.New("Missing language for site")
|
return nil, err
|
||||||
}
|
|
||||||
languages[i] = s.Language
|
|
||||||
}
|
}
|
||||||
defaultLang := viper.GetString("DefaultContentLanguage")
|
|
||||||
if defaultLang == "" {
|
|
||||||
defaultLang = "en"
|
|
||||||
}
|
|
||||||
langConfig := &Multilingual{Languages: languages, DefaultLang: NewLanguage(defaultLang)}
|
|
||||||
|
|
||||||
return &HugoSites{Multilingual: langConfig, Sites: sites}, nil
|
return &HugoSites{Multilingual: langConfig, Sites: sites}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewHugoSitesFromConfiguration creates HugoSites from the global Viper config.
|
// NewHugoSitesFromConfiguration creates HugoSites from the global Viper config.
|
||||||
func NewHugoSitesFromConfiguration() (*HugoSites, error) {
|
func NewHugoSitesFromConfiguration() (*HugoSites, error) {
|
||||||
|
sites, err := createSitesFromConfig()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewHugoSites(sites...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSitesFromConfig() ([]*Site, error) {
|
||||||
var sites []*Site
|
var sites []*Site
|
||||||
multilingual := viper.GetStringMap("Languages")
|
multilingual := viper.GetStringMap("Languages")
|
||||||
if len(multilingual) == 0 {
|
if len(multilingual) == 0 {
|
||||||
|
@ -80,19 +81,43 @@ func NewHugoSitesFromConfiguration() (*HugoSites, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewHugoSites(sites...)
|
return sites, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset resets the sites, making it ready for a full rebuild.
|
// Reset resets the sites, making it ready for a full rebuild.
|
||||||
// TODO(bep) multilingo
|
// TODO(bep) multilingo
|
||||||
func (h HugoSites) Reset() {
|
func (h *HugoSites) reset() {
|
||||||
for i, s := range h.Sites {
|
for i, s := range h.Sites {
|
||||||
h.Sites[i] = s.Reset()
|
h.Sites[i] = s.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h HugoSites) toSiteInfos() []*SiteInfo {
|
func (h *HugoSites) reCreateFromConfig() error {
|
||||||
|
oldSite := h.Sites[0]
|
||||||
|
sites, err := createSitesFromConfig()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
langConfig, err := newMultiLingualFromSites(sites...)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
h.Sites = sites
|
||||||
|
h.Multilingual = langConfig
|
||||||
|
|
||||||
|
for _, s := range h.Sites {
|
||||||
|
// TODO(bep) ml Tmpl
|
||||||
|
s.Tmpl = oldSite.Tmpl
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *HugoSites) toSiteInfos() []*SiteInfo {
|
||||||
infos := make([]*SiteInfo, len(h.Sites))
|
infos := make([]*SiteInfo, len(h.Sites))
|
||||||
for i, s := range h.Sites {
|
for i, s := range h.Sites {
|
||||||
infos[i] = &s.Info
|
infos[i] = &s.Info
|
||||||
|
@ -106,6 +131,11 @@ type BuildCfg struct {
|
||||||
Watching bool
|
Watching bool
|
||||||
// Print build stats at the end of a build
|
// Print build stats at the end of a build
|
||||||
PrintStats bool
|
PrintStats bool
|
||||||
|
// Reset site state before build. Use to force full rebuilds.
|
||||||
|
ResetState bool
|
||||||
|
// Re-creates the sites from configuration before a build.
|
||||||
|
// This is needed if new languages are added.
|
||||||
|
CreateSitesFromConfig bool
|
||||||
// Skip rendering. Useful for testing.
|
// Skip rendering. Useful for testing.
|
||||||
SkipRender bool
|
SkipRender bool
|
||||||
// Use this to add templates to use for rendering.
|
// Use this to add templates to use for rendering.
|
||||||
|
@ -114,14 +144,20 @@ type BuildCfg struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build builds all sites.
|
// Build builds all sites.
|
||||||
func (h HugoSites) Build(config BuildCfg) error {
|
func (h *HugoSites) Build(config BuildCfg) error {
|
||||||
|
|
||||||
if h.Sites == nil || len(h.Sites) == 0 {
|
|
||||||
return errors.New("No site(s) to build")
|
|
||||||
}
|
|
||||||
|
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
|
|
||||||
|
if config.ResetState {
|
||||||
|
h.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.CreateSitesFromConfig {
|
||||||
|
if err := h.reCreateFromConfig(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We should probably refactor the Site and pull up most of the logic from there to here,
|
// We should probably refactor the Site and pull up most of the logic from there to here,
|
||||||
// but that seems like a daunting task.
|
// but that seems like a daunting task.
|
||||||
// So for now, if there are more than one site (language),
|
// So for now, if there are more than one site (language),
|
||||||
|
@ -143,6 +179,7 @@ func (h HugoSites) Build(config BuildCfg) error {
|
||||||
if len(h.Sites) > 1 {
|
if len(h.Sites) > 1 {
|
||||||
// Initialize the rest
|
// Initialize the rest
|
||||||
for _, site := range h.Sites[1:] {
|
for _, site := range h.Sites[1:] {
|
||||||
|
// TODO(bep) ml Tmpl
|
||||||
site.Tmpl = firstSite.Tmpl
|
site.Tmpl = firstSite.Tmpl
|
||||||
site.initializeSiteInfo()
|
site.initializeSiteInfo()
|
||||||
}
|
}
|
||||||
|
@ -184,9 +221,23 @@ func (h HugoSites) Build(config BuildCfg) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rebuild rebuilds all sites.
|
// Rebuild rebuilds all sites.
|
||||||
func (h HugoSites) Rebuild(config BuildCfg, events ...fsnotify.Event) error {
|
func (h *HugoSites) Rebuild(config BuildCfg, events ...fsnotify.Event) error {
|
||||||
t0 := time.Now()
|
t0 := time.Now()
|
||||||
|
|
||||||
|
if config.CreateSitesFromConfig {
|
||||||
|
return errors.New("Rebuild does not support 'CreateSitesFromConfig'. Use Build.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.ResetState {
|
||||||
|
return errors.New("Rebuild does not support 'ResetState'. Use Build.")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, s := range h.Sites {
|
||||||
|
// TODO(bep) ml
|
||||||
|
s.Multilingual = h.Multilingual
|
||||||
|
s.RunMode.Watching = config.Watching
|
||||||
|
}
|
||||||
|
|
||||||
firstSite := h.Sites[0]
|
firstSite := h.Sites[0]
|
||||||
|
|
||||||
for _, s := range h.Sites {
|
for _, s := range h.Sites {
|
||||||
|
|
|
@ -39,7 +39,7 @@ func testCommonResetState() {
|
||||||
|
|
||||||
func TestMultiSitesBuild(t *testing.T) {
|
func TestMultiSitesBuild(t *testing.T) {
|
||||||
testCommonResetState()
|
testCommonResetState()
|
||||||
sites := createMultiTestSites(t)
|
sites := createMultiTestSites(t, multiSiteTomlConfig)
|
||||||
|
|
||||||
err := sites.Build(BuildCfg{})
|
err := sites.Build(BuildCfg{})
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ func TestMultiSitesBuild(t *testing.T) {
|
||||||
|
|
||||||
func TestMultiSitesRebuild(t *testing.T) {
|
func TestMultiSitesRebuild(t *testing.T) {
|
||||||
testCommonResetState()
|
testCommonResetState()
|
||||||
sites := createMultiTestSites(t)
|
sites := createMultiTestSites(t, multiSiteTomlConfig)
|
||||||
cfg := BuildCfg{}
|
cfg := BuildCfg{}
|
||||||
|
|
||||||
err := sites.Build(cfg)
|
err := sites.Build(cfg)
|
||||||
|
@ -299,10 +299,95 @@ func TestMultiSitesRebuild(t *testing.T) {
|
||||||
|
|
||||||
this.assertFunc(t)
|
this.assertFunc(t)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAddNewLanguage(t *testing.T) {
|
||||||
|
testCommonResetState()
|
||||||
|
|
||||||
|
sites := createMultiTestSites(t, multiSiteTomlConfig)
|
||||||
|
cfg := BuildCfg{}
|
||||||
|
|
||||||
|
err := sites.Build(cfg)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to build sites: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
newConfig := multiSiteTomlConfig + `
|
||||||
|
|
||||||
|
[Languages.no]
|
||||||
|
weight = 15
|
||||||
|
title = "Norsk"
|
||||||
|
`
|
||||||
|
|
||||||
|
writeNewContentFile(t, "Norwegian Contentfile", "2016-01-01", "content/sect/doc1.no.md", 10)
|
||||||
|
// replace the config
|
||||||
|
writeSource(t, "multilangconfig.toml", newConfig)
|
||||||
|
|
||||||
|
// Watching does not work with in-memory fs, so we trigger a reload manually
|
||||||
|
require.NoError(t, viper.ReadInConfig())
|
||||||
|
|
||||||
|
err = sites.Build(BuildCfg{CreateSitesFromConfig: true})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to rebuild sites: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
require.Len(t, sites.Sites, 3, fmt.Sprintf("Len %d", len(sites.Sites)))
|
||||||
|
|
||||||
|
// The Norwegian site should be put in the middle (language weight=15)
|
||||||
|
enSite := sites.Sites[0]
|
||||||
|
noSite := sites.Sites[1]
|
||||||
|
frSite := sites.Sites[2]
|
||||||
|
require.True(t, enSite.Language.Lang == "en", enSite.Language.Lang)
|
||||||
|
require.True(t, noSite.Language.Lang == "no", noSite.Language.Lang)
|
||||||
|
require.True(t, frSite.Language.Lang == "fr", frSite.Language.Lang)
|
||||||
|
|
||||||
|
require.Len(t, enSite.Pages, 3)
|
||||||
|
require.Len(t, frSite.Pages, 3)
|
||||||
|
|
||||||
|
// Veriy Norwegian site
|
||||||
|
require.Len(t, noSite.Pages, 1)
|
||||||
|
noPage := noSite.Pages[0]
|
||||||
|
require.Equal(t, "Norwegian Contentfile", noPage.Title)
|
||||||
|
require.Equal(t, "no", noPage.Lang())
|
||||||
|
require.Len(t, noPage.Translations(), 2)
|
||||||
|
require.Len(t, noPage.AllTranslations(), 3)
|
||||||
|
require.Equal(t, "en", noPage.Translations()[0].Lang())
|
||||||
|
//noFile := readDestination(t, "/public/no/doc1/index.html")
|
||||||
|
//require.True(t, strings.Contains("foo", noFile), noFile)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMultiTestSites(t *testing.T) *HugoSites {
|
var multiSiteTomlConfig = `
|
||||||
|
DefaultExtension = "html"
|
||||||
|
baseurl = "http://example.com/blog"
|
||||||
|
DisableSitemap = false
|
||||||
|
DisableRSS = false
|
||||||
|
RSSUri = "index.xml"
|
||||||
|
|
||||||
|
paginate = 2
|
||||||
|
DefaultContentLanguage = "fr"
|
||||||
|
|
||||||
|
[permalinks]
|
||||||
|
other = "/somewhere/else/:filename"
|
||||||
|
|
||||||
|
[Taxonomies]
|
||||||
|
tag = "tags"
|
||||||
|
|
||||||
|
[Languages]
|
||||||
|
[Languages.en]
|
||||||
|
weight = 10
|
||||||
|
title = "English"
|
||||||
|
|
||||||
|
[Languages.fr]
|
||||||
|
weight = 20
|
||||||
|
title = "Français"
|
||||||
|
[Languages.fr.Taxonomies]
|
||||||
|
plaque = "plaques"
|
||||||
|
`
|
||||||
|
|
||||||
|
func createMultiTestSites(t *testing.T, tomlConfig string) *HugoSites {
|
||||||
|
|
||||||
// Add some layouts
|
// Add some layouts
|
||||||
if err := afero.WriteFile(hugofs.Source(),
|
if err := afero.WriteFile(hugofs.Source(),
|
||||||
|
@ -445,35 +530,6 @@ draft: true
|
||||||
`)},
|
`)},
|
||||||
}
|
}
|
||||||
|
|
||||||
tomlConfig := `
|
|
||||||
DefaultExtension = "html"
|
|
||||||
baseurl = "http://example.com/blog"
|
|
||||||
DisableSitemap = false
|
|
||||||
DisableRSS = false
|
|
||||||
RSSUri = "index.xml"
|
|
||||||
|
|
||||||
paginate = 2
|
|
||||||
DefaultContentLanguage = "fr"
|
|
||||||
|
|
||||||
|
|
||||||
[permalinks]
|
|
||||||
other = "/somewhere/else/:filename"
|
|
||||||
|
|
||||||
[Taxonomies]
|
|
||||||
tag = "tags"
|
|
||||||
|
|
||||||
[Languages]
|
|
||||||
[Languages.en]
|
|
||||||
weight = 1
|
|
||||||
title = "English"
|
|
||||||
|
|
||||||
[Languages.fr]
|
|
||||||
weight = 2
|
|
||||||
title = "Français"
|
|
||||||
[Languages.fr.Taxonomies]
|
|
||||||
plaque = "plaques"
|
|
||||||
`
|
|
||||||
|
|
||||||
writeSource(t, "multilangconfig.toml", tomlConfig)
|
writeSource(t, "multilangconfig.toml", tomlConfig)
|
||||||
if err := LoadGlobalConfig("", "multilangconfig.toml"); err != nil {
|
if err := LoadGlobalConfig("", "multilangconfig.toml"); err != nil {
|
||||||
t.Fatalf("Failed to load config: %s", err)
|
t.Fatalf("Failed to load config: %s", err)
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
|
@ -63,6 +64,26 @@ func (ml *Multilingual) Language(lang string) *Language {
|
||||||
return ml.langMap[lang]
|
return ml.langMap[lang]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newMultiLingualFromSites(sites ...*Site) (*Multilingual, error) {
|
||||||
|
languages := make(Languages, len(sites))
|
||||||
|
|
||||||
|
for i, s := range sites {
|
||||||
|
if s.Language == nil {
|
||||||
|
return nil, errors.New("Missing language for site")
|
||||||
|
}
|
||||||
|
languages[i] = s.Language
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultLang := viper.GetString("DefaultContentLanguage")
|
||||||
|
|
||||||
|
if defaultLang == "" {
|
||||||
|
defaultLang = "en"
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Multilingual{Languages: languages, DefaultLang: NewLanguage(defaultLang)}, nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func (ml *Multilingual) enabled() bool {
|
func (ml *Multilingual) enabled() bool {
|
||||||
return len(ml.Languages) > 1
|
return len(ml.Languages) > 1
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,6 +195,9 @@ func (n *Node) Language() *Language {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *Node) Lang() string {
|
func (n *Node) Lang() string {
|
||||||
|
// When set, Language can be different from lang in the case where there is a
|
||||||
|
// content file (doc.sv.md) with language indicator, but there is no language
|
||||||
|
// config for that language. Then the language will fall back on the site default.
|
||||||
if n.Language() != nil {
|
if n.Language() != nil {
|
||||||
return n.Language().Lang
|
return n.Language().Lang
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue