Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-01-10 12:55:03 +0300
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2017-02-04 07:37:25 +0300
commitc71e1b106e6011d148cac899f83c4685dee33a22 (patch)
treec5c7090f0c2398c7771e4908ebcc97aa7714ffd2
parent0ada40591216572b0e4c6a8ab986b0aa4fb13c13 (diff)
all: Refactor to nonglobal file systems
Updates #2701 Fixes #2951
-rw-r--r--commands/benchmark.go5
-rw-r--r--commands/gendoc.go4
-rw-r--r--commands/genman.go4
-rw-r--r--commands/hugo.go135
-rw-r--r--commands/import_jekyll.go25
-rw-r--r--commands/new.go71
-rw-r--r--commands/new_test.go82
-rw-r--r--commands/server.go19
-rw-r--r--commands/undraft.go9
-rw-r--r--create/content.go10
-rw-r--r--create/content_test.go33
-rw-r--r--deps/deps.go115
-rw-r--r--helpers/configProvider.go14
-rw-r--r--helpers/path.go26
-rw-r--r--helpers/path_test.go8
-rw-r--r--helpers/pathspec.go28
-rw-r--r--helpers/pathspec_test.go22
-rw-r--r--helpers/pygments.go2
-rw-r--r--helpers/url_test.go12
-rw-r--r--hugofs/fs.go84
-rw-r--r--hugofs/fs_test.go60
-rw-r--r--hugolib/alias_test.go40
-rw-r--r--hugolib/case_insensitive_test.go67
-rw-r--r--hugolib/config_test.go6
-rw-r--r--hugolib/datafiles_test.go34
-rw-r--r--hugolib/embedded_shortcodes_test.go20
-rw-r--r--hugolib/gitinfo.go5
-rw-r--r--hugolib/handler_page.go1
-rw-r--r--hugolib/handler_test.go46
-rw-r--r--hugolib/hugo_sites.go230
-rw-r--r--hugolib/hugo_sites_build.go2
-rw-r--r--hugolib/hugo_sites_build_test.go268
-rw-r--r--hugolib/i18n.go5
-rw-r--r--hugolib/menu_test.go24
-rw-r--r--hugolib/node_as_page_test.go341
-rw-r--r--hugolib/page.go62
-rw-r--r--hugolib/page_permalink_test.go3
-rw-r--r--hugolib/page_test.go76
-rw-r--r--hugolib/pagination.go11
-rw-r--r--hugolib/pagination_test.go73
-rw-r--r--hugolib/permalinks.go6
-rw-r--r--hugolib/robotstxt_test.go28
-rw-r--r--hugolib/rss_test.go18
-rw-r--r--hugolib/shortcode.go19
-rw-r--r--hugolib/shortcode_test.go113
-rw-r--r--hugolib/site.go288
-rw-r--r--hugolib/siteJSONEncode_test.go15
-rw-r--r--hugolib/site_render.go12
-rw-r--r--hugolib/site_test.go296
-rw-r--r--hugolib/site_url_test.go28
-rw-r--r--hugolib/sitemap_test.go28
-rw-r--r--hugolib/taxonomy.go19
-rw-r--r--hugolib/taxonomy_test.go13
-rw-r--r--hugolib/template_engines_test.go99
-rw-r--r--hugolib/template_test.go68
-rw-r--r--hugolib/testhelpers_test.go53
-rw-r--r--source/filesystem.go15
-rw-r--r--source/filesystem_test.go15
-rw-r--r--target/file.go4
-rw-r--r--target/htmlredirect.go4
-rw-r--r--target/page.go4
-rw-r--r--target/page_test.go6
-rw-r--r--tpl/amber_compiler.go42
-rw-r--r--tpl/template.go170
-rw-r--r--tpl/template_funcs.go92
-rw-r--r--tpl/template_funcs_test.go189
-rw-r--r--tpl/template_i18n.go1
-rw-r--r--tpl/template_resources.go19
-rw-r--r--tpl/template_resources_test.go32
-rw-r--r--tpl/template_test.go110
-rw-r--r--tplapi/template.go28
71 files changed, 2202 insertions, 1714 deletions
diff --git a/commands/benchmark.go b/commands/benchmark.go
index a879e8941..42966c67a 100644
--- a/commands/benchmark.go
+++ b/commands/benchmark.go
@@ -49,10 +49,13 @@ func init() {
func benchmark(cmd *cobra.Command, args []string) error {
cfg, err := InitializeConfig(benchmarkCmd)
+
if err != nil {
return err
}
+ c := commandeer{cfg}
+
var memProf *os.File
if memProfileFile != "" {
memProf, err = os.Create(memProfileFile)
@@ -79,7 +82,7 @@ func benchmark(cmd *cobra.Command, args []string) error {
t := time.Now()
for i := 0; i < benchmarkTimes; i++ {
- if err = resetAndBuildSites(cfg, false); err != nil {
+ if err = c.resetAndBuildSites(false); err != nil {
return err
}
}
diff --git a/commands/gendoc.go b/commands/gendoc.go
index 046d3839c..5ffd084e1 100644
--- a/commands/gendoc.go
+++ b/commands/gendoc.go
@@ -51,9 +51,9 @@ for rendering in Hugo.`,
if !strings.HasSuffix(gendocdir, helpers.FilePathSeparator) {
gendocdir += helpers.FilePathSeparator
}
- if found, _ := helpers.Exists(gendocdir, hugofs.Os()); !found {
+ if found, _ := helpers.Exists(gendocdir, hugofs.Os); !found {
jww.FEEDBACK.Println("Directory", gendocdir, "does not exist, creating...")
- hugofs.Os().MkdirAll(gendocdir, 0777)
+ hugofs.Os.MkdirAll(gendocdir, 0777)
}
now := time.Now().Format(time.RFC3339)
prepender := func(filename string) string {
diff --git a/commands/genman.go b/commands/genman.go
index d1f54ae31..f7a3a424d 100644
--- a/commands/genman.go
+++ b/commands/genman.go
@@ -41,9 +41,9 @@ in the "man" directory under the current directory.`,
if !strings.HasSuffix(genmandir, helpers.FilePathSeparator) {
genmandir += helpers.FilePathSeparator
}
- if found, _ := helpers.Exists(genmandir, hugofs.Os()); !found {
+ if found, _ := helpers.Exists(genmandir, hugofs.Os); !found {
jww.FEEDBACK.Println("Directory", genmandir, "does not exist, creating...")
- hugofs.Os().MkdirAll(genmandir, 0777)
+ hugofs.Os.MkdirAll(genmandir, 0777)
}
cmd.Root().DisableAutoGenTag = true
diff --git a/commands/hugo.go b/commands/hugo.go
index f4204cad1..566e68603 100644
--- a/commands/hugo.go
+++ b/commands/hugo.go
@@ -40,6 +40,7 @@ import (
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/fsync"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugolib"
"github.com/spf13/hugo/livereload"
@@ -50,6 +51,10 @@ import (
"github.com/spf13/viper"
)
+type commandeer struct {
+ deps.DepsCfg
+}
+
// Hugo represents the Hugo sites to build. This variable is exported as it
// is used by at least one external library (the Hugo caddy plugin). We should
// provide a cleaner external API, but until then, this is it.
@@ -119,12 +124,14 @@ Complete documentation is available at http://gohugo.io/.`,
return err
}
+ c := commandeer{cfg}
+
if buildWatch {
viper.Set("disableLiveReload", true)
- watchConfig(cfg)
+ c.watchConfig()
}
- return build(cfg)
+ return c.build()
},
}
@@ -268,9 +275,9 @@ func init() {
}
// InitializeConfig initializes a config file with sensible default configuration flags.
-func InitializeConfig(subCmdVs ...*cobra.Command) (hugolib.DepsCfg, error) {
+func InitializeConfig(subCmdVs ...*cobra.Command) (deps.DepsCfg, error) {
- var cfg hugolib.DepsCfg
+ var cfg deps.DepsCfg
if err := hugolib.LoadGlobalConfig(source, cfgFile); err != nil {
return cfg, err
@@ -323,34 +330,34 @@ func InitializeConfig(subCmdVs ...*cobra.Command) (hugolib.DepsCfg, error) {
viper.Set("cacheDir", cacheDir)
}
+ // Init file systems. This may be changed at a later point.
+ cfg.Fs = hugofs.NewDefault()
+
cacheDir = viper.GetString("cacheDir")
if cacheDir != "" {
if helpers.FilePathSeparator != cacheDir[len(cacheDir)-1:] {
cacheDir = cacheDir + helpers.FilePathSeparator
}
- isDir, err := helpers.DirExists(cacheDir, hugofs.Source())
+ isDir, err := helpers.DirExists(cacheDir, cfg.Fs.Source)
utils.CheckErr(err)
if !isDir {
mkdir(cacheDir)
}
viper.Set("cacheDir", cacheDir)
} else {
- viper.Set("cacheDir", helpers.GetTempDir("hugo_cache", hugofs.Source()))
+ viper.Set("cacheDir", helpers.GetTempDir("hugo_cache", cfg.Fs.Source))
}
jww.INFO.Println("Using config file:", viper.ConfigFileUsed())
- // Init file systems. This may be changed at a later point.
- hugofs.InitDefaultFs()
-
themeDir := helpers.GetThemeDir()
if themeDir != "" {
- if _, err := hugofs.Source().Stat(themeDir); os.IsNotExist(err) {
+ if _, err := cfg.Fs.Source.Stat(themeDir); os.IsNotExist(err) {
return cfg, newSystemError("Unable to find theme Directory:", themeDir)
}
}
- themeVersionMismatch, minVersion := isThemeVsHugoVersionMismatch()
+ themeVersionMismatch, minVersion := isThemeVsHugoVersionMismatch(cfg.Fs.Source)
if themeVersionMismatch {
jww.ERROR.Printf("Current theme does not support Hugo version %s. Minimum version required is %s\n",
@@ -447,12 +454,12 @@ func flagChanged(flags *flag.FlagSet, key string) bool {
return flag.Changed
}
-func watchConfig(cfg hugolib.DepsCfg) {
+func (c commandeer) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
jww.FEEDBACK.Println("Config file changed:", e.Name)
// Force a full rebuild
- utils.CheckErr(recreateAndBuildSites(cfg, true))
+ utils.CheckErr(c.recreateAndBuildSites(true))
if !viper.GetBool("disableLiveReload") {
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
livereload.ForceRefresh()
@@ -460,39 +467,40 @@ func watchConfig(cfg hugolib.DepsCfg) {
})
}
-func build(cfg hugolib.DepsCfg, watches ...bool) error {
+func (c commandeer) build(watches ...bool) error {
// Hugo writes the output to memory instead of the disk.
// This is only used for benchmark testing. Cause the content is only visible
// in memory.
if renderToMemory {
- hugofs.SetDestination(new(afero.MemMapFs))
+ c.Fs.Destination = new(afero.MemMapFs)
// Rendering to memoryFS, publish to Root regardless of publishDir.
viper.Set("publishDir", "/")
}
- if err := copyStatic(); err != nil {
+ if err := c.copyStatic(); err != nil {
return fmt.Errorf("Error copying static files to %s: %s", helpers.AbsPathify(viper.GetString("publishDir")), err)
}
watch := false
if len(watches) > 0 && watches[0] {
watch = true
}
- if err := buildSites(cfg, buildWatch || watch); err != nil {
+ if err := c.buildSites(buildWatch || watch); err != nil {
return fmt.Errorf("Error building site: %s", err)
}
if buildWatch {
jww.FEEDBACK.Println("Watching for changes in", helpers.AbsPathify(viper.GetString("contentDir")))
jww.FEEDBACK.Println("Press Ctrl+C to stop")
- utils.CheckErr(newWatcher(cfg, 0))
+ utils.CheckErr(c.newWatcher(0))
}
return nil
}
-func getStaticSourceFs() afero.Fs {
- source := hugofs.Source()
- themeDir, err := helpers.GetThemeStaticDirPath()
+func (c commandeer) getStaticSourceFs() afero.Fs {
+ source := c.Fs.Source
+ pathSpec := helpers.NewPathSpec(c.Fs, viper.GetViper())
+ themeDir, err := pathSpec.GetThemeStaticDirPath()
staticDir := helpers.GetStaticDirPath() + helpers.FilePathSeparator
useTheme := true
@@ -532,12 +540,12 @@ func getStaticSourceFs() afero.Fs {
jww.INFO.Println("using a UnionFS for static directory comprised of:")
jww.INFO.Println("Base:", themeDir)
jww.INFO.Println("Overlay:", staticDir)
- base := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.Source(), themeDir))
- overlay := afero.NewReadOnlyFs(afero.NewBasePathFs(hugofs.Source(), staticDir))
+ base := afero.NewReadOnlyFs(afero.NewBasePathFs(source, themeDir))
+ overlay := afero.NewReadOnlyFs(afero.NewBasePathFs(source, staticDir))
return afero.NewCopyOnWriteFs(base, overlay)
}
-func copyStatic() error {
+func (c commandeer) copyStatic() error {
publishDir := helpers.AbsPathify(viper.GetString("publishDir")) + helpers.FilePathSeparator
// If root, remove the second '/'
@@ -546,7 +554,7 @@ func copyStatic() error {
}
// Includes both theme/static & /static
- staticSourceFs := getStaticSourceFs()
+ staticSourceFs := c.getStaticSourceFs()
if staticSourceFs == nil {
jww.WARN.Println("No static directories found to sync")
@@ -557,7 +565,7 @@ func copyStatic() error {
syncer.NoTimes = viper.GetBool("noTimes")
syncer.NoChmod = viper.GetBool("noChmod")
syncer.SrcFs = staticSourceFs
- syncer.DestFs = hugofs.Destination()
+ syncer.DestFs = c.Fs.Destination
// Now that we are using a unionFs for the static directories
// We can effectively clean the publishDir on initial sync
syncer.Delete = viper.GetBool("cleanDestinationDir")
@@ -572,7 +580,7 @@ func copyStatic() error {
}
// getDirList provides NewWatcher() with a list of directories to watch for changes.
-func getDirList() []string {
+func (c commandeer) getDirList() []string {
var a []string
dataDir := helpers.AbsPathify(viper.GetString("dataDir"))
i18nDir := helpers.AbsPathify(viper.GetString("i18nDir"))
@@ -621,7 +629,7 @@ func getDirList() []string {
jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", path, err)
return nil
}
- linkfi, err := hugofs.Source().Stat(link)
+ linkfi, err := c.Fs.Source.Stat(link)
if err != nil {
jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
return nil
@@ -642,25 +650,25 @@ func getDirList() []string {
return nil
}
- helpers.SymbolicWalk(hugofs.Source(), dataDir, walker)
- helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("contentDir")), walker)
- helpers.SymbolicWalk(hugofs.Source(), i18nDir, walker)
- helpers.SymbolicWalk(hugofs.Source(), helpers.AbsPathify(viper.GetString("layoutDir")), walker)
+ helpers.SymbolicWalk(c.Fs.Source, dataDir, walker)
+ helpers.SymbolicWalk(c.Fs.Source, helpers.AbsPathify(viper.GetString("contentDir")), walker)
+ helpers.SymbolicWalk(c.Fs.Source, i18nDir, walker)
+ helpers.SymbolicWalk(c.Fs.Source, helpers.AbsPathify(viper.GetString("layoutDir")), walker)
- helpers.SymbolicWalk(hugofs.Source(), staticDir, walker)
+ helpers.SymbolicWalk(c.Fs.Source, staticDir, walker)
if helpers.ThemeSet() {
- helpers.SymbolicWalk(hugofs.Source(), filepath.Join(themesDir, "layouts"), walker)
- helpers.SymbolicWalk(hugofs.Source(), filepath.Join(themesDir, "static"), walker)
- helpers.SymbolicWalk(hugofs.Source(), filepath.Join(themesDir, "i18n"), walker)
- helpers.SymbolicWalk(hugofs.Source(), filepath.Join(themesDir, "data"), walker)
+ helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "layouts"), walker)
+ helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "static"), walker)
+ helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "i18n"), walker)
+ helpers.SymbolicWalk(c.Fs.Source, filepath.Join(themesDir, "data"), walker)
}
return a
}
-func recreateAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
- if err := initSites(cfg); err != nil {
+func (c commandeer) recreateAndBuildSites(watching bool) (err error) {
+ if err := c.initSites(); err != nil {
return err
}
if !quiet {
@@ -669,9 +677,9 @@ func recreateAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
return Hugo.Build(hugolib.BuildCfg{CreateSitesFromConfig: true, Watching: watching, PrintStats: !quiet})
}
-func resetAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
- if err := initSites(cfg); err != nil {
- return err
+func (c commandeer) resetAndBuildSites(watching bool) (err error) {
+ if err = c.initSites(); err != nil {
+ return
}
if !quiet {
jww.FEEDBACK.Println("Started building sites ...")
@@ -679,12 +687,12 @@ func resetAndBuildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
return Hugo.Build(hugolib.BuildCfg{ResetState: true, Watching: watching, PrintStats: !quiet})
}
-func initSites(cfg hugolib.DepsCfg) error {
+func (c commandeer) initSites() error {
if Hugo != nil {
return nil
}
- h, err := hugolib.NewHugoSitesFromConfiguration(cfg)
+ h, err := hugolib.NewHugoSitesFromConfiguration(c.DepsCfg)
if err != nil {
return err
@@ -694,8 +702,8 @@ func initSites(cfg hugolib.DepsCfg) error {
return nil
}
-func buildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
- if err := initSites(cfg); err != nil {
+func (c commandeer) buildSites(watching bool) (err error) {
+ if err := c.initSites(); err != nil {
return err
}
if !quiet {
@@ -704,19 +712,21 @@ func buildSites(cfg hugolib.DepsCfg, watching bool) (err error) {
return Hugo.Build(hugolib.BuildCfg{Watching: watching, PrintStats: !quiet})
}
-func rebuildSites(cfg hugolib.DepsCfg, events []fsnotify.Event) error {
- if err := initSites(cfg); err != nil {
+func (c commandeer) rebuildSites(events []fsnotify.Event) error {
+ if err := c.initSites(); err != nil {
return err
}
return Hugo.Build(hugolib.BuildCfg{PrintStats: !quiet, Watching: true}, events...)
}
// newWatcher creates a new watcher to watch filesystem events.
-func newWatcher(cfg hugolib.DepsCfg, port int) error {
+func (c commandeer) newWatcher(port int) error {
if runtime.GOOS == "darwin" {
tweakLimit()
}
+ pathSpec := helpers.NewPathSpec(c.Fs, viper.GetViper())
+
watcher, err := watcher.New(1 * time.Second)
var wg sync.WaitGroup
@@ -728,7 +738,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
wg.Add(1)
- for _, d := range getDirList() {
+ for _, d := range c.getDirList() {
if d != "" {
_ = watcher.Add(d)
}
@@ -793,12 +803,12 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
// recursively add new directories to watch list
// When mkdir -p is used, only the top directory triggers an event (at least on OSX)
if ev.Op&fsnotify.Create == fsnotify.Create {
- if s, err := hugofs.Source().Stat(ev.Name); err == nil && s.Mode().IsDir() {
- helpers.SymbolicWalk(hugofs.Source(), ev.Name, walkAdder)
+ if s, err := c.Fs.Source.Stat(ev.Name); err == nil && s.Mode().IsDir() {
+ helpers.SymbolicWalk(c.Fs.Source, ev.Name, walkAdder)
}
}
- isstatic := strings.HasPrefix(ev.Name, helpers.GetStaticDirPath()) || (len(helpers.GetThemesDirPath()) > 0 && strings.HasPrefix(ev.Name, helpers.GetThemesDirPath()))
+ isstatic := strings.HasPrefix(ev.Name, helpers.GetStaticDirPath()) || (len(pathSpec.GetThemesDirPath()) > 0 && strings.HasPrefix(ev.Name, pathSpec.GetThemesDirPath()))
if isstatic {
staticEvents = append(staticEvents, ev)
@@ -821,12 +831,12 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
if viper.GetBool("forceSyncStatic") {
jww.FEEDBACK.Printf("Syncing all static files\n")
- err := copyStatic()
+ err := c.copyStatic()
if err != nil {
utils.StopOnErr(err, fmt.Sprintf("Error copying static files to %s", publishDir))
}
} else {
- staticSourceFs := getStaticSourceFs()
+ staticSourceFs := c.getStaticSourceFs()
if staticSourceFs == nil {
jww.WARN.Println("No static directories found to sync")
@@ -837,7 +847,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
syncer.NoTimes = viper.GetBool("noTimes")
syncer.NoChmod = viper.GetBool("noChmod")
syncer.SrcFs = staticSourceFs
- syncer.DestFs = hugofs.Destination()
+ syncer.DestFs = c.Fs.Destination
// prevent spamming the log on changes
logger := helpers.NewDistinctFeedbackLogger()
@@ -862,7 +872,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
fromPath := ev.Name
// If we are here we already know the event took place in a static dir
- relPath, err := helpers.MakeStaticPathRelative(fromPath)
+ relPath, err := pathSpec.MakeStaticPathRelative(fromPath)
if err != nil {
jww.ERROR.Println(err)
continue
@@ -882,7 +892,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
// If file doesn't exist in any static dir, remove it
toRemove := filepath.Join(publishDir, relPath)
logger.Println("File no longer exists in static dir, removing", toRemove)
- hugofs.Destination().RemoveAll(toRemove)
+ c.Fs.Destination.RemoveAll(toRemove)
} else if err == nil {
// If file still exists, sync it
logger.Println("Syncing", relPath, "to", publishDir)
@@ -910,7 +920,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
// force refresh when more than one file
if len(staticEvents) > 0 {
for _, ev := range staticEvents {
- path, _ := helpers.MakeStaticPathRelative(ev.Name)
+ path, _ := pathSpec.MakeStaticPathRelative(ev.Name)
livereload.RefreshPath(path)
}
@@ -925,7 +935,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
const layout = "2006-01-02 15:04 -0700"
jww.FEEDBACK.Println(time.Now().Format(layout))
- rebuildSites(cfg, dynamicEvents)
+ c.rebuildSites(dynamicEvents)
if !buildWatch && !viper.GetBool("disableLiveReload") {
// Will block forever trying to write to a channel that nobody is reading if livereload isn't initialized
@@ -947,7 +957,7 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
http.HandleFunc("/livereload", livereload.Handler)
}
- go serve(port)
+ go c.serve(port)
}
wg.Wait()
@@ -956,14 +966,13 @@ func newWatcher(cfg hugolib.DepsCfg, port int) error {
// isThemeVsHugoVersionMismatch returns whether the current Hugo version is
// less than the theme's min_version.
-func isThemeVsHugoVersionMismatch() (mismatch bool, requiredMinVersion string) {
+func isThemeVsHugoVersionMismatch(fs afero.Fs) (mismatch bool, requiredMinVersion string) {
if !helpers.ThemeSet() {
return
}
themeDir := helpers.GetThemeDir()
- fs := hugofs.Source()
path := filepath.Join(themeDir, "theme.toml")
exists, err := helpers.Exists(path, fs)
diff --git a/commands/import_jekyll.go b/commands/import_jekyll.go
index 7e55e0670..151fffa8a 100644
--- a/commands/import_jekyll.go
+++ b/commands/import_jekyll.go
@@ -25,6 +25,7 @@ import (
"strings"
"time"
+ "github.com/spf13/afero"
"github.com/spf13/cast"
"github.com/spf13/cobra"
"github.com/spf13/hugo/helpers"
@@ -122,7 +123,7 @@ func importFromJekyll(cmd *cobra.Command, args []string) error {
return convertJekyllPost(site, path, relPath, targetDir, draft)
}
- err = helpers.SymbolicWalk(hugofs.Os(), jekyllRoot, callback)
+ err = helpers.SymbolicWalk(hugofs.Os, jekyllRoot, callback)
if err != nil {
return err
@@ -137,7 +138,13 @@ func importFromJekyll(cmd *cobra.Command, args []string) error {
// TODO: Consider calling doNewSite() instead?
func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) (*hugolib.Site, error) {
- fs := hugofs.Source()
+ s, err := hugolib.NewSiteDefaultLang()
+ if err != nil {
+ return nil, err
+ }
+
+ fs := s.Fs.Source
+
if exists, _ := helpers.Exists(targetDir, fs); exists {
if isDir, _ := helpers.IsDir(targetDir, fs); !isDir {
return nil, errors.New("Target path \"" + targetDir + "\" already exists but not a directory")
@@ -150,7 +157,7 @@ func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) (*hugolib.Si
}
}
- jekyllConfig := loadJekyllConfig(jekyllRoot)
+ jekyllConfig := loadJekyllConfig(fs, jekyllRoot)
// Crude test to make sure at least one of _drafts/ and _posts/ exists
// and is not empty.
@@ -177,16 +184,14 @@ func createSiteFromJekyll(jekyllRoot, targetDir string, force bool) (*hugolib.Si
mkdir(targetDir, "data")
mkdir(targetDir, "themes")
- createConfigFromJekyll(targetDir, "yaml", jekyllConfig)
+ createConfigFromJekyll(fs, targetDir, "yaml", jekyllConfig)
copyJekyllFilesAndFolders(jekyllRoot, filepath.Join(targetDir, "static"))
- site := hugolib.NewSiteDefaultLang()
- return site, nil
+ return s, nil
}
-func loadJekyllConfig(jekyllRoot string) map[string]interface{} {
- fs := hugofs.Source()
+func loadJekyllConfig(fs afero.Fs, jekyllRoot string) map[string]interface{} {
path := filepath.Join(jekyllRoot, "_config.yml")
exists, err := helpers.Exists(path, fs)
@@ -218,7 +223,7 @@ func loadJekyllConfig(jekyllRoot string) map[string]interface{} {
return c.(map[string]interface{})
}
-func createConfigFromJekyll(inpath string, kind string, jekyllConfig map[string]interface{}) (err error) {
+func createConfigFromJekyll(fs afero.Fs, inpath string, kind string, jekyllConfig map[string]interface{}) (err error) {
title := "My New Hugo Site"
baseURL := "http://example.org/"
@@ -251,7 +256,7 @@ func createConfigFromJekyll(inpath string, kind string, jekyllConfig map[string]
return err
}
- err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.Source())
+ err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), fs)
if err != nil {
return
}
diff --git a/commands/new.go b/commands/new.go
index 0b6ada534..b4a2740f3 100644
--- a/commands/new.go
+++ b/commands/new.go
@@ -16,15 +16,18 @@ package commands
import (
"bytes"
"errors"
+ "fmt"
"os"
"path/filepath"
"strings"
"time"
+ "github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/hugo/create"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/hugolib"
"github.com/spf13/hugo/parser"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
@@ -84,7 +87,9 @@ as you see fit.`,
// NewContent adds new content to a Hugo site.
func NewContent(cmd *cobra.Command, args []string) error {
- if _, err := InitializeConfig(); err != nil {
+ cfg, err := InitializeConfig()
+
+ if err != nil {
return err
}
@@ -110,10 +115,16 @@ func NewContent(cmd *cobra.Command, args []string) error {
kind = contentType
}
- return create.NewContent(hugofs.Source(), kind, createpath)
+ s, err := hugolib.NewSite(cfg)
+
+ if err != nil {
+ return newSystemError(err)
+ }
+
+ return create.NewContent(s, kind, createpath)
}
-func doNewSite(basepath string, force bool) error {
+func doNewSite(fs *hugofs.Fs, basepath string, force bool) error {
dirs := []string{
filepath.Join(basepath, "layouts"),
filepath.Join(basepath, "content"),
@@ -123,12 +134,12 @@ func doNewSite(basepath string, force bool) error {
filepath.Join(basepath, "themes"),
}
- if exists, _ := helpers.Exists(basepath, hugofs.Source()); exists {
- if isDir, _ := helpers.IsDir(basepath, hugofs.Source()); !isDir {
+ if exists, _ := helpers.Exists(basepath, fs.Source); exists {
+ if isDir, _ := helpers.IsDir(basepath, fs.Source); !isDir {
return errors.New(basepath + " already exists but not a directory")
}
- isEmpty, _ := helpers.IsEmpty(basepath, hugofs.Source())
+ isEmpty, _ := helpers.IsEmpty(basepath, fs.Source)
switch {
case !isEmpty && !force:
@@ -137,7 +148,7 @@ func doNewSite(basepath string, force bool) error {
case !isEmpty && force:
all := append(dirs, filepath.Join(basepath, "config."+configFormat))
for _, path := range all {
- if exists, _ := helpers.Exists(path, hugofs.Source()); exists {
+ if exists, _ := helpers.Exists(path, fs.Source); exists {
return errors.New(path + " already exists")
}
}
@@ -145,10 +156,12 @@ func doNewSite(basepath string, force bool) error {
}
for _, dir := range dirs {
- hugofs.Source().MkdirAll(dir, 0777)
+ if err := fs.Source.MkdirAll(dir, 0777); err != nil {
+ return fmt.Errorf("Failed to create dir: %s", err)
+ }
}
- createConfig(basepath, configFormat)
+ createConfig(fs, basepath, configFormat)
jww.FEEDBACK.Printf("Congratulations! Your new Hugo site is created in %s.\n\n", basepath)
jww.FEEDBACK.Println(nextStepsText())
@@ -190,12 +203,14 @@ func NewSite(cmd *cobra.Command, args []string) error {
forceNew, _ := cmd.Flags().GetBool("force")
- return doNewSite(createpath, forceNew)
+ return doNewSite(hugofs.NewDefault(), createpath, forceNew)
}
// NewTheme creates a new Hugo theme.
func NewTheme(cmd *cobra.Command, args []string) error {
- if _, err := InitializeConfig(); err != nil {
+ cfg, err := InitializeConfig()
+
+ if err != nil {
return err
}
@@ -207,26 +222,26 @@ func NewTheme(cmd *cobra.Command, args []string) error {
createpath := helpers.AbsPathify(filepath.Join(viper.GetString("themesDir"), args[0]))
jww.INFO.Println("creating theme at", createpath)
- if x, _ := helpers.Exists(createpath, hugofs.Source()); x {
+ if x, _ := helpers.Exists(createpath, cfg.Fs.Source); x {
return newUserError(createpath, "already exists")
}
mkdir(createpath, "layouts", "_default")
mkdir(createpath, "layouts", "partials")
- touchFile(createpath, "layouts", "index.html")
- touchFile(createpath, "layouts", "404.html")
- touchFile(createpath, "layouts", "_default", "list.html")
- touchFile(createpath, "layouts", "_default", "single.html")
+ touchFile(cfg.Fs.Source, createpath, "layouts", "index.html")
+ touchFile(cfg.Fs.Source, createpath, "layouts", "404.html")
+ touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "list.html")
+ touchFile(cfg.Fs.Source, createpath, "layouts", "_default", "single.html")
- touchFile(createpath, "layouts", "partials", "header.html")
- touchFile(createpath, "layouts", "partials", "footer.html")
+ touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "header.html")
+ touchFile(cfg.Fs.Source, createpath, "layouts", "partials", "footer.html")
mkdir(createpath, "archetypes")
archDefault := []byte("+++\n+++\n")
- err := helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), hugofs.Source())
+ err = helpers.WriteToDisk(filepath.Join(createpath, "archetypes", "default.md"), bytes.NewReader(archDefault), cfg.Fs.Source)
if err != nil {
return err
}
@@ -256,12 +271,12 @@ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
`)
- err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), hugofs.Source())
+ err = helpers.WriteToDisk(filepath.Join(createpath, "LICENSE.md"), bytes.NewReader(by), cfg.Fs.Source)
if err != nil {
return err
}
- createThemeMD(createpath)
+ createThemeMD(cfg.Fs, createpath)
return nil
}
@@ -275,16 +290,16 @@ func mkdir(x ...string) {
}
}
-func touchFile(x ...string) {
+func touchFile(fs afero.Fs, x ...string) {
inpath := filepath.Join(x...)
mkdir(filepath.Dir(inpath))
- err := helpers.WriteToDisk(inpath, bytes.NewReader([]byte{}), hugofs.Source())
+ err := helpers.WriteToDisk(inpath, bytes.NewReader([]byte{}), fs)
if err != nil {
jww.FATAL.Fatalln(err)
}
}
-func createThemeMD(inpath string) (err error) {
+func createThemeMD(fs *hugofs.Fs, inpath string) (err error) {
by := []byte(`# theme.toml template for a Hugo theme
# See https://github.com/spf13/hugoThemes#themetoml for an example
@@ -309,7 +324,7 @@ min_version = 0.18
repo = ""
`)
- err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), hugofs.Source())
+ err = helpers.WriteToDisk(filepath.Join(inpath, "theme.toml"), bytes.NewReader(by), fs.Source)
if err != nil {
return
}
@@ -320,7 +335,7 @@ min_version = 0.18
func newContentPathSection(path string) (string, string) {
// Forward slashes is used in all examples. Convert if needed.
// Issue #1133
- createpath := strings.Replace(path, "/", helpers.FilePathSeparator, -1)
+ createpath := filepath.FromSlash(path)
var section string
// assume the first directory is the section (kind)
if strings.Contains(createpath[1:], helpers.FilePathSeparator) {
@@ -330,7 +345,7 @@ func newContentPathSection(path string) (string, string) {
return createpath, section
}
-func createConfig(inpath string, kind string) (err error) {
+func createConfig(fs *hugofs.Fs, inpath string, kind string) (err error) {
in := map[string]interface{}{
"baseURL": "http://example.org/",
"title": "My New Hugo Site",
@@ -343,7 +358,7 @@ func createConfig(inpath string, kind string) (err error) {
return err
}
- err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), hugofs.Source())
+ err = helpers.WriteToDisk(filepath.Join(inpath, "config."+kind), bytes.NewReader(by), fs.Source)
if err != nil {
return
}
diff --git a/commands/new_test.go b/commands/new_test.go
index 5991e1813..acb3d7598 100644
--- a/commands/new_test.go
+++ b/commands/new_test.go
@@ -14,12 +14,12 @@
package commands
import (
- "os"
"path/filepath"
"testing"
"github.com/spf13/hugo/hugofs"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
// Issue #1133
@@ -29,7 +29,8 @@ func TestNewContentPathSectionWithForwardSlashes(t *testing.T) {
assert.Equal(t, "post", s)
}
-func checkNewSiteInited(basepath string, t *testing.T) {
+func checkNewSiteInited(fs *hugofs.Fs, basepath string, t *testing.T) {
+
paths := []string{
filepath.Join(basepath, "layouts"),
filepath.Join(basepath, "content"),
@@ -40,63 +41,70 @@ func checkNewSiteInited(basepath string, t *testing.T) {
}
for _, path := range paths {
- _, err := hugofs.Source().Stat(path)
- assert.Nil(t, err)
+ _, err := fs.Source.Stat(path)
+ require.NoError(t, err)
}
}
func TestDoNewSite(t *testing.T) {
- basepath := filepath.Join(os.TempDir(), "blog")
- hugofs.InitMemFs()
- err := doNewSite(basepath, false)
- assert.Nil(t, err)
+ basepath := filepath.Join("base", "blog")
+ fs := hugofs.NewMem()
+
+ require.NoError(t, doNewSite(fs, basepath, false))
- checkNewSiteInited(basepath, t)
+ checkNewSiteInited(fs, basepath, t)
}
func TestDoNewSite_noerror_base_exists_but_empty(t *testing.T) {
- basepath := filepath.Join(os.TempDir(), "blog")
- hugofs.InitMemFs()
- hugofs.Source().MkdirAll(basepath, 777)
- err := doNewSite(basepath, false)
- assert.Nil(t, err)
+ basepath := filepath.Join("base", "blog")
+ fs := hugofs.NewMem()
+
+ require.NoError(t, fs.Source.MkdirAll(basepath, 777))
+
+ require.NoError(t, doNewSite(fs, basepath, false))
}
func TestDoNewSite_error_base_exists(t *testing.T) {
- basepath := filepath.Join(os.TempDir(), "blog")
- hugofs.InitMemFs()
- hugofs.Source().MkdirAll(basepath, 777)
- hugofs.Source().Create(filepath.Join(basepath, "foo"))
+ basepath := filepath.Join("base", "blog")
+ fs := hugofs.NewMem()
+
+ require.NoError(t, fs.Source.MkdirAll(basepath, 777))
+ _, err := fs.Source.Create(filepath.Join(basepath, "foo"))
+ require.NoError(t, err)
// Since the directory already exists and isn't empty, expect an error
- err := doNewSite(basepath, false)
- assert.NotNil(t, err)
+ require.Error(t, doNewSite(fs, basepath, false))
+
}
func TestDoNewSite_force_empty_dir(t *testing.T) {
- basepath := filepath.Join(os.TempDir(), "blog")
- hugofs.InitMemFs()
- hugofs.Source().MkdirAll(basepath, 777)
- err := doNewSite(basepath, true)
- assert.Nil(t, err)
+ basepath := filepath.Join("base", "blog")
+ fs := hugofs.NewMem()
- checkNewSiteInited(basepath, t)
+ require.NoError(t, fs.Source.MkdirAll(basepath, 777))
+
+ require.NoError(t, doNewSite(fs, basepath, true))
+
+ checkNewSiteInited(fs, basepath, t)
}
func TestDoNewSite_error_force_dir_inside_exists(t *testing.T) {
- basepath := filepath.Join(os.TempDir(), "blog")
+ basepath := filepath.Join("base", "blog")
+ fs := hugofs.NewMem()
+
contentPath := filepath.Join(basepath, "content")
- hugofs.InitMemFs()
- hugofs.Source().MkdirAll(contentPath, 777)
- err := doNewSite(basepath, true)
- assert.NotNil(t, err)
+
+ require.NoError(t, fs.Source.MkdirAll(contentPath, 777))
+ require.Error(t, doNewSite(fs, basepath, true))
}
func TestDoNewSite_error_force_config_inside_exists(t *testing.T) {
- basepath := filepath.Join(os.TempDir(), "blog")
+ basepath := filepath.Join("base", "blog")
+ fs := hugofs.NewMem()
+
configPath := filepath.Join(basepath, "config.toml")
- hugofs.InitMemFs()
- hugofs.Source().MkdirAll(basepath, 777)
- hugofs.Source().Create(configPath)
- err := doNewSite(basepath, true)
- assert.NotNil(t, err)
+ require.NoError(t, fs.Source.MkdirAll(basepath, 777))
+ _, err := fs.Source.Create(configPath)
+ require.NoError(t, err)
+
+ require.Error(t, doNewSite(fs, basepath, true))
}
diff --git a/commands/server.go b/commands/server.go
index 45d776998..6b1776f4a 100644
--- a/commands/server.go
+++ b/commands/server.go
@@ -29,7 +29,6 @@ import (
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
)
@@ -109,6 +108,8 @@ func server(cmd *cobra.Command, args []string) error {
return err
}
+ c := commandeer{cfg}
+
if flagChanged(cmd.Flags(), "disableLiveReload") {
viper.Set("disableLiveReload", disableLiveReload)
}
@@ -119,7 +120,7 @@ func server(cmd *cobra.Command, args []string) error {
if viper.GetBool("watch") {
serverWatch = true
- watchConfig(cfg)
+ c.watchConfig()
}
l, err := net.Listen("tcp", net.JoinHostPort(serverInterface, strconv.Itoa(serverPort)))
@@ -157,18 +158,18 @@ func server(cmd *cobra.Command, args []string) error {
// Hugo writes the output to memory instead of the disk
if !renderToDisk {
- hugofs.SetDestination(new(afero.MemMapFs))
+ cfg.Fs.Destination = new(afero.MemMapFs)
// Rendering to memoryFS, publish to Root regardless of publishDir.
viper.Set("publishDir", "/")
}
- if err := build(cfg, serverWatch); err != nil {
+ if err := c.build(serverWatch); err != nil {
return err
}
// Watch runs its own server as part of the routine
if serverWatch {
- watchDirs := getDirList()
+ watchDirs := c.getDirList()
baseWatchDir := viper.GetString("workingDir")
for i, dir := range watchDirs {
watchDirs[i], _ = helpers.GetRelativePath(dir, baseWatchDir)
@@ -177,26 +178,26 @@ func server(cmd *cobra.Command, args []string) error {
rootWatchDirs := strings.Join(helpers.UniqueStrings(helpers.ExtractRootPaths(watchDirs)), ",")
jww.FEEDBACK.Printf("Watching for changes in %s%s{%s}\n", baseWatchDir, helpers.FilePathSeparator, rootWatchDirs)
- err := newWatcher(cfg, serverPort)
+ err := c.newWatcher(serverPort)
if err != nil {
return err
}
}
- serve(serverPort)
+ c.serve(serverPort)
return nil
}
-func serve(port int) {
+func (c commandeer) serve(port int) {
if renderToDisk {
jww.FEEDBACK.Println("Serving pages from " + helpers.AbsPathify(viper.GetString("publishDir")))
} else {
jww.FEEDBACK.Println("Serving pages from memory")
}
- httpFs := afero.NewHttpFs(hugofs.Destination())
+ httpFs := afero.NewHttpFs(c.Fs.Destination)
fs := filesOnlyFs{httpFs.Dir(helpers.AbsPathify(viper.GetString("publishDir")))}
fileserver := http.FileServer(fs)
diff --git a/commands/undraft.go b/commands/undraft.go
index 8e287651f..4f3bcfe23 100644
--- a/commands/undraft.go
+++ b/commands/undraft.go
@@ -20,7 +20,6 @@ import (
"time"
"github.com/spf13/cobra"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/parser"
)
@@ -37,7 +36,9 @@ If the content's draft status is 'False', nothing is done.`,
// to false and setting its publish date to now. If the specified content is
// not a draft, it will log an error.
func Undraft(cmd *cobra.Command, args []string) error {
- if _, err := InitializeConfig(); err != nil {
+ cfg, err := InitializeConfig()
+
+ if err != nil {
return err
}
@@ -47,7 +48,7 @@ func Undraft(cmd *cobra.Command, args []string) error {
location := args[0]
// open the file
- f, err := hugofs.Source().Open(location)
+ f, err := cfg.Fs.Source.Open(location)
if err != nil {
return err
}
@@ -64,7 +65,7 @@ func Undraft(cmd *cobra.Command, args []string) error {
return newSystemErrorF("an error occurred while undrafting %q: %s", location, err)
}
- f, err = hugofs.Source().OpenFile(location, os.O_WRONLY|os.O_TRUNC, 0644)
+ f, err = cfg.Fs.Source.OpenFile(location, os.O_WRONLY|os.O_TRUNC, 0644)
if err != nil {
return newSystemErrorF("%q not be undrafted due to error opening file to save changes: %q\n", location, err)
}
diff --git a/create/content.go b/create/content.go
index 195080d88..6a03c8c9b 100644
--- a/create/content.go
+++ b/create/content.go
@@ -34,15 +34,15 @@ import (
// NewContent creates a new content file in the content directory based upon the
// given kind, which is used to lookup an archetype.
-func NewContent(fs afero.Fs, kind, name string) (err error) {
+func NewContent(s *hugolib.Site, kind, name string) (err error) {
jww.INFO.Println("attempting to create ", name, "of", kind)
- location := FindArchetype(fs, kind)
+ location := FindArchetype(s.Fs.Source, kind)
var by []byte
if location != "" {
- by, err = afero.ReadFile(fs, location)
+ by, err = afero.ReadFile(s.Fs.Source, location)
if err != nil {
jww.ERROR.Println(err)
}
@@ -62,9 +62,7 @@ func NewContent(fs afero.Fs, kind, name string) (err error) {
return err
}
- site := hugolib.NewSiteDefaultLang()
-
- page, err := site.NewPage(name)
+ page, err := s.NewPage(name)
if err != nil {
return err
}
diff --git a/create/content_test.go b/create/content_test.go
index cdee13fb8..df29527fe 100644
--- a/create/content_test.go
+++ b/create/content_test.go
@@ -19,23 +19,22 @@ import (
"strings"
"testing"
+ "github.com/spf13/hugo/hugolib"
+
"fmt"
+ "github.com/spf13/hugo/hugofs"
+
"github.com/spf13/afero"
"github.com/spf13/hugo/create"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
+ "github.com/stretchr/testify/require"
)
func TestNewContent(t *testing.T) {
initViper()
- err := initFs()
- if err != nil {
- t.Fatalf("initialization error: %s", err)
- }
-
cases := []struct {
kind string
path string
@@ -48,15 +47,15 @@ func TestNewContent(t *testing.T) {
{"product", "product/sample-4.md", []string{`title = "sample 4"`}}, // empty archetype front matter
}
- for i, c := range cases {
- err = create.NewContent(hugofs.Source(), c.kind, c.path)
- if err != nil {
- t.Errorf("[%d] NewContent: %s", i, err)
- }
+ for _, c := range cases {
+ s, err := hugolib.NewEnglishSite()
+ require.NoError(t, err)
+ require.NoError(t, initFs(s.Fs))
- fname := filepath.Join("content", filepath.FromSlash(c.path))
- content := readFileFromFs(t, hugofs.Source(), fname)
+ require.NoError(t, create.NewContent(s, c.kind, c.path))
+ fname := filepath.Join("content", filepath.FromSlash(c.path))
+ content := readFileFromFs(t, s.Fs.Source, fname)
for i, v := range c.expected {
found := strings.Contains(content, v)
if !found {
@@ -72,11 +71,11 @@ func initViper() {
viper.Set("archetypeDir", "archetypes")
viper.Set("contentDir", "content")
viper.Set("themesDir", "themes")
+ viper.Set("layoutDir", "layouts")
viper.Set("theme", "sample")
}
-func initFs() error {
- hugofs.InitMemFs()
+func initFs(fs *hugofs.Fs) error {
perm := os.FileMode(0755)
var err error
@@ -87,7 +86,7 @@ func initFs() error {
filepath.Join("themes", "sample", "archetypes"),
}
for _, dir := range dirs {
- err = hugofs.Source().Mkdir(dir, perm)
+ err = fs.Source.Mkdir(dir, perm)
if err != nil {
return err
}
@@ -111,7 +110,7 @@ func initFs() error {
content: "+++\ndate =\"\"\ntitle = \"Empty Date Arch title\"\ntest = \"test1\"\n+++\n",
},
} {
- f, err := hugofs.Source().Create(v.path)
+ f, err := fs.Source.Create(v.path)
if err != nil {
return err
}
diff --git a/deps/deps.go b/deps/deps.go
new file mode 100644
index 000000000..d09b760aa
--- /dev/null
+++ b/deps/deps.go
@@ -0,0 +1,115 @@
+package deps
+
+import (
+ "io/ioutil"
+ "log"
+ "os"
+
+ "github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/tplapi"
+ jww "github.com/spf13/jwalterweatherman"
+)
+
+// Deps holds dependencies used by many.
+// There will be normally be only one instance of deps in play
+// at a given time, i.e. one per Site built.
+type Deps struct {
+ // The logger to use.
+ Log *jww.Notepad `json:"-"`
+
+ // The templates to use.
+ Tmpl tplapi.Template `json:"-"`
+
+ // The file systems to use.
+ Fs *hugofs.Fs `json:"-"`
+
+ // The PathSpec to use
+ *helpers.PathSpec `json:"-"`
+
+ templateProvider TemplateProvider
+ WithTemplate func(templ tplapi.Template) error
+
+ // TODO(bep) globals next in line: Viper
+
+}
+
+// Used to create and refresh, and clone the template.
+type TemplateProvider interface {
+ Update(deps *Deps) error
+ Clone(deps *Deps) error
+}
+
+func (d *Deps) LoadTemplates() error {
+ if err := d.templateProvider.Update(d); err != nil {
+ return err
+ }
+ d.Tmpl.PrintErrors()
+ return nil
+}
+
+func New(cfg DepsCfg) *Deps {
+ var (
+ logger = cfg.Logger
+ fs = cfg.Fs
+ )
+
+ if cfg.TemplateProvider == nil {
+ panic("Must have a TemplateProvider")
+ }
+
+ if cfg.Language == nil {
+ panic("Must have a Language")
+ }
+
+ if logger == nil {
+ logger = jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+ }
+
+ if fs == nil {
+ // Default to the most used file systems.
+ fs = hugofs.NewMem()
+ }
+
+ d := &Deps{
+ Fs: fs,
+ Log: logger,
+ templateProvider: cfg.TemplateProvider,
+ WithTemplate: cfg.WithTemplate,
+ PathSpec: helpers.NewPathSpec(fs, cfg.Language),
+ }
+
+ return d
+}
+
+// ForLanguage creates a copy of the Deps with the language dependent
+// parts switched out.
+func (d Deps) ForLanguage(l *helpers.Language) (*Deps, error) {
+
+ d.PathSpec = helpers.NewPathSpec(d.Fs, l)
+ if err := d.templateProvider.Clone(&d); err != nil {
+ return nil, err
+ }
+
+ return &d, nil
+
+}
+
+// DepsCfg contains configuration options that can be used to configure Hugo
+// on a global level, i.e. logging etc.
+// Nil values will be given default values.
+type DepsCfg struct {
+
+ // The Logger to use.
+ Logger *jww.Notepad
+
+ // The file systems to use
+ Fs *hugofs.Fs
+
+ // The language to use.
+ Language *helpers.Language
+
+ // Template handling.
+ TemplateProvider TemplateProvider
+ WithTemplate func(templ tplapi.Template) error
+}
diff --git a/helpers/configProvider.go b/helpers/configProvider.go
index e63112c0c..b96018257 100644
--- a/helpers/configProvider.go
+++ b/helpers/configProvider.go
@@ -29,7 +29,6 @@ import (
// TODO(bep) Get rid of these.
var (
currentConfigProvider ConfigProvider
- currentPathSpec *PathSpec
)
// ConfigProvider provides the configuration settings for Hugo.
@@ -52,24 +51,13 @@ func Config() ConfigProvider {
return viper.Get("currentContentLanguage").(ConfigProvider)
}
-// CurrentPathSpec returns the current PathSpec.
-// If it is not set, a new will be created based in the currently active Hugo config.
-func CurrentPathSpec() *PathSpec {
- if currentPathSpec != nil {
- return currentPathSpec
- }
- // Some tests rely on this. We will fix that, eventually.
- return NewPathSpecFromConfig(Config())
-}
-
// InitConfigProviderForCurrentContentLanguage does what it says.
func InitConfigProviderForCurrentContentLanguage() {
currentConfigProvider = viper.Get("CurrentContentLanguage").(ConfigProvider)
- currentPathSpec = NewPathSpecFromConfig(currentConfigProvider)
}
// ResetConfigProvider is used in tests.
func ResetConfigProvider() {
currentConfigProvider = nil
- currentPathSpec = nil
+
}
diff --git a/helpers/path.go b/helpers/path.go
index 2e154062e..83a91deba 100644
--- a/helpers/path.go
+++ b/helpers/path.go
@@ -23,8 +23,6 @@ import (
"strings"
"unicode"
- "github.com/spf13/hugo/hugofs"
-
"github.com/spf13/afero"
"github.com/spf13/viper"
"golang.org/x/text/transform"
@@ -196,29 +194,29 @@ func GetRelativeThemeDir() string {
// GetThemeStaticDirPath returns the theme's static dir path if theme is set.
// If theme is set and the static dir doesn't exist, an error is returned.
-func GetThemeStaticDirPath() (string, error) {
- return getThemeDirPath("static")
+func (p *PathSpec) GetThemeStaticDirPath() (string, error) {
+ return p.getThemeDirPath("static")
}
// GetThemeDataDirPath returns the theme's data dir path if theme is set.
// If theme is set and the data dir doesn't exist, an error is returned.
-func GetThemeDataDirPath() (string, error) {
- return getThemeDirPath("data")
+func (p *PathSpec) GetThemeDataDirPath() (string, error) {
+ return p.getThemeDirPath("data")
}
// GetThemeI18nDirPath returns the theme's i18n dir path if theme is set.
// If theme is set and the i18n dir doesn't exist, an error is returned.
-func GetThemeI18nDirPath() (string, error) {
- return getThemeDirPath("i18n")
+func (p *PathSpec) GetThemeI18nDirPath() (string, error) {
+ return p.getThemeDirPath("i18n")
}
-func getThemeDirPath(path string) (string, error) {
+func (p *PathSpec) getThemeDirPath(path string) (string, error) {
if !ThemeSet() {
return "", ErrThemeUndefined
}
themeDir := filepath.Join(GetThemeDir(), path)
- if _, err := hugofs.Source().Stat(themeDir); os.IsNotExist(err) {
+ if _, err := p.fs.Source.Stat(themeDir); os.IsNotExist(err) {
return "", fmt.Errorf("Unable to find %s directory for theme %s in %s", path, viper.GetString("theme"), themeDir)
}
@@ -228,17 +226,17 @@ func getThemeDirPath(path string) (string, error) {
// GetThemesDirPath gets the static files directory of the current theme, if there is one.
// Ignores underlying errors.
// TODO(bep) Candidate for deprecation?
-func GetThemesDirPath() string {
- dir, _ := getThemeDirPath("static")
+func (p *PathSpec) GetThemesDirPath() string {
+ dir, _ := p.getThemeDirPath("static")
return dir
}
// MakeStaticPathRelative makes a relative path to the static files directory.
// It does so by taking either the project's static path or the theme's static
// path into consideration.
-func MakeStaticPathRelative(inPath string) (string, error) {
+func (p *PathSpec) MakeStaticPathRelative(inPath string) (string, error) {
staticDir := GetStaticDirPath()
- themeStaticDir := GetThemesDirPath()
+ themeStaticDir := p.GetThemesDirPath()
return makePathRelative(inPath, staticDir, themeStaticDir)
}
diff --git a/helpers/path_test.go b/helpers/path_test.go
index f1407fb15..4d1ac28aa 100644
--- a/helpers/path_test.go
+++ b/helpers/path_test.go
@@ -30,6 +30,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/spf13/afero"
+ "github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
)
@@ -64,7 +65,8 @@ func TestMakePath(t *testing.T) {
for _, test := range tests {
viper.Set("removePathAccents", test.removeAccents)
- p := NewPathSpecFromConfig(viper.GetViper())
+ p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
+
output := p.MakePath(test.input)
if output != test.expected {
t.Errorf("Expected %#v, got %#v\n", test.expected, output)
@@ -77,7 +79,7 @@ func TestMakePathSanitized(t *testing.T) {
defer viper.Reset()
initCommonTestConfig()
- p := NewPathSpecFromConfig(viper.GetViper())
+ p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
tests := []struct {
input string
@@ -105,7 +107,7 @@ func TestMakePathSanitizedDisablePathToLower(t *testing.T) {
initCommonTestConfig()
viper.Set("disablePathToLower", true)
- p := NewPathSpecFromConfig(viper.GetViper())
+ p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
tests := []struct {
input string
diff --git a/helpers/pathspec.go b/helpers/pathspec.go
index d95dcde7a..0fc957b3b 100644
--- a/helpers/pathspec.go
+++ b/helpers/pathspec.go
@@ -13,6 +13,12 @@
package helpers
+import (
+ "fmt"
+
+ "github.com/spf13/hugo/hugofs"
+)
+
// PathSpec holds methods that decides how paths in URLs and files in Hugo should look like.
type PathSpec struct {
disablePathToLower bool
@@ -33,11 +39,27 @@ type PathSpec struct {
defaultContentLanguageInSubdir bool
defaultContentLanguage string
multilingual bool
+
+ // The file systems to use
+ fs *hugofs.Fs
+}
+
+func (p PathSpec) String() string {
+ return fmt.Sprintf("PathSpec, language %q, prefix %q, multilingual: %T", p.currentContentLanguage.Lang, p.getLanguagePrefix(), p.multilingual)
}
-// NewPathSpecFromConfig creats a new PathSpec from the given ConfigProvider.
-func NewPathSpecFromConfig(config ConfigProvider) *PathSpec {
+// NewPathSpec creats a new PathSpec from the given filesystems and ConfigProvider.
+func NewPathSpec(fs *hugofs.Fs, config ConfigProvider) *PathSpec {
+
+ currCl, ok := config.Get("currentContentLanguage").(*Language)
+
+ if !ok {
+ // TODO(bep) globals
+ currCl = NewLanguage("en")
+ }
+
return &PathSpec{
+ fs: fs,
disablePathToLower: config.GetBool("disablePathToLower"),
removePathAccents: config.GetBool("removePathAccents"),
uglyURLs: config.GetBool("uglyURLs"),
@@ -45,7 +67,7 @@ func NewPathSpecFromConfig(config ConfigProvider) *PathSpec {
multilingual: config.GetBool("multilingual"),
defaultContentLanguageInSubdir: config.GetBool("defaultContentLanguageInSubdir"),
defaultContentLanguage: config.GetString("defaultContentLanguage"),
- currentContentLanguage: config.Get("currentContentLanguage").(*Language),
+ currentContentLanguage: currCl,
paginatePath: config.GetString("paginatePath"),
}
}
diff --git a/helpers/pathspec_test.go b/helpers/pathspec_test.go
index 9cd0af80e..42d828519 100644
--- a/helpers/pathspec_test.go
+++ b/helpers/pathspec_test.go
@@ -16,6 +16,8 @@ package helpers
import (
"testing"
+ "github.com/spf13/hugo/hugofs"
+
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
@@ -31,15 +33,15 @@ func TestNewPathSpecFromConfig(t *testing.T) {
viper.Set("canonifyURLs", true)
viper.Set("paginatePath", "side")
- pathSpec := NewPathSpecFromConfig(viper.GetViper())
+ p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
- require.True(t, pathSpec.canonifyURLs)
- require.True(t, pathSpec.defaultContentLanguageInSubdir)
- require.True(t, pathSpec.disablePathToLower)
- require.True(t, pathSpec.multilingual)
- require.True(t, pathSpec.removePathAccents)
- require.True(t, pathSpec.uglyURLs)
- require.Equal(t, "no", pathSpec.defaultContentLanguage)
- require.Equal(t, "no", pathSpec.currentContentLanguage.Lang)
- require.Equal(t, "side", pathSpec.paginatePath)
+ require.True(t, p.canonifyURLs)
+ require.True(t, p.defaultContentLanguageInSubdir)
+ require.True(t, p.disablePathToLower)
+ require.True(t, p.multilingual)
+ require.True(t, p.removePathAccents)
+ require.True(t, p.uglyURLs)
+ require.Equal(t, "no", p.defaultContentLanguage)
+ require.Equal(t, "no", p.currentContentLanguage.Lang)
+ require.Equal(t, "side", p.paginatePath)
}
diff --git a/helpers/pygments.go b/helpers/pygments.go
index 5e9812d72..8e6d1a998 100644
--- a/helpers/pygments.go
+++ b/helpers/pygments.go
@@ -60,7 +60,7 @@ func Highlight(code, lang, optsStr string) string {
io.WriteString(hash, lang)
io.WriteString(hash, options)
- fs := hugofs.Os()
+ fs := hugofs.Os
ignoreCache := viper.GetBool("ignoreCache")
cacheDir := viper.GetString("cacheDir")
diff --git a/helpers/url_test.go b/helpers/url_test.go
index 8dbec3f7c..b50a9efd8 100644
--- a/helpers/url_test.go
+++ b/helpers/url_test.go
@@ -18,6 +18,7 @@ import (
"strings"
"testing"
+ "github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -26,7 +27,7 @@ import (
func TestURLize(t *testing.T) {
initCommonTestConfig()
- p := NewPathSpecFromConfig(viper.GetViper())
+ p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
tests := []struct {
input string
@@ -85,9 +86,11 @@ func doTestAbsURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
{"http//foo", "http://base/path", "http://base/path/MULTIhttp/foo"},
}
+ p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
+
for _, test := range tests {
viper.Set("baseURL", test.baseURL)
- p := NewPathSpecFromConfig(viper.GetViper())
+
output := p.AbsURL(test.input, addLanguage)
expected := test.expected
if multilingual && addLanguage {
@@ -164,7 +167,7 @@ func doTestRelURL(t *testing.T, defaultInSubDir, addLanguage, multilingual bool,
for i, test := range tests {
viper.Set("baseURL", test.baseURL)
viper.Set("canonifyURLs", test.canonify)
- p := NewPathSpecFromConfig(viper.GetViper())
+ p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
output := p.RelURL(test.input, addLanguage)
@@ -247,9 +250,10 @@ func TestURLPrep(t *testing.T) {
{false, "/section/name.html", "/section/name/"},
{true, "/section/name/index.html", "/section/name.html"},
}
+
for i, d := range data {
viper.Set("uglyURLs", d.ugly)
- p := NewPathSpecFromConfig(viper.GetViper())
+ p := NewPathSpec(hugofs.NewMem(), viper.GetViper())
output := p.URLPrep(d.input)
if d.output != output {
diff --git a/hugofs/fs.go b/hugofs/fs.go
index 7f8abd337..3afa17956 100644
--- a/hugofs/fs.go
+++ b/hugofs/fs.go
@@ -19,76 +19,54 @@ import (
"github.com/spf13/viper"
)
-var (
- sourceFs afero.Fs
- destinationFs afero.Fs
- osFs afero.Fs = &afero.OsFs{}
- workingDirFs *afero.BasePathFs
-)
-
-// Source returns Hugo's source file system.
-func Source() afero.Fs {
- return sourceFs
-}
+// Os points to an Os Afero file system.
+var Os = &afero.OsFs{}
-// SetSource sets Hugo's source file system
-// and re-initializes dependent file systems.
-func SetSource(fs afero.Fs) {
- sourceFs = fs
- initSourceDependencies()
-}
+type Fs struct {
+ // Source is Hugo's source file system.
+ Source afero.Fs
-// Destination returns Hugo's destionation file system.
-func Destination() afero.Fs {
- return destinationFs
-}
-
-// SetDestination sets Hugo's destionation file system
-func SetDestination(fs afero.Fs) {
- destinationFs = fs
-}
+ // Destination is Hugo's destionation file system.
+ Destination afero.Fs
-// Os returns an OS file system.
-func Os() afero.Fs {
- return osFs
-}
+ // Os is an OS file system.
+ Os afero.Fs
-// WorkingDir returns a read-only file system
-// restricted to the project working dir.
-func WorkingDir() *afero.BasePathFs {
- return workingDirFs
+ // WorkingDir is a read-only file system
+ // restricted to the project working dir.
+ WorkingDir *afero.BasePathFs
}
-// InitDefaultFs initializes with the OS file system
+// NewDefault creates a new Fs with the OS file system
// as source and destination file systems.
-func InitDefaultFs() {
- InitFs(&afero.OsFs{})
+func NewDefault() *Fs {
+ fs := &afero.OsFs{}
+ return newFs(fs)
}
-// InitMemFs initializes with a MemMapFs as source and destination file systems.
+// NewDefault creates a new Fs with the MemMapFs
+// as source and destination file systems.
// Useful for testing.
-func InitMemFs() {
- InitFs(&afero.MemMapFs{})
+func NewMem() *Fs {
+ fs := &afero.MemMapFs{}
+ return newFs(fs)
}
-// InitFs initializes with the given file system
-// as source and destination file systems.
-func InitFs(fs afero.Fs) {
- sourceFs = fs
- destinationFs = fs
-
- initSourceDependencies()
+func newFs(base afero.Fs) *Fs {
+ return &Fs{
+ Source: base,
+ Destination: base,
+ Os: &afero.OsFs{},
+ WorkingDir: getWorkingDirFs(base),
+ }
}
-func initSourceDependencies() {
+func getWorkingDirFs(base afero.Fs) *afero.BasePathFs {
workingDir := viper.GetString("workingDir")
if workingDir != "" {
- workingDirFs = afero.NewBasePathFs(afero.NewReadOnlyFs(sourceFs), workingDir).(*afero.BasePathFs)
+ return afero.NewBasePathFs(afero.NewReadOnlyFs(base), workingDir).(*afero.BasePathFs)
}
-}
-
-func init() {
- InitDefaultFs()
+ return nil
}
diff --git a/hugofs/fs_test.go b/hugofs/fs_test.go
index 55007009f..5482e6d27 100644
--- a/hugofs/fs_test.go
+++ b/hugofs/fs_test.go
@@ -21,51 +21,35 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestInitDefault(t *testing.T) {
+func TestNewDefault(t *testing.T) {
viper.Reset()
defer viper.Reset()
- InitDefaultFs()
+ f := NewDefault()
- assert.NotNil(t, Source())
- assert.IsType(t, new(afero.OsFs), Source())
- assert.NotNil(t, Destination())
- assert.IsType(t, new(afero.OsFs), Destination())
- assert.NotNil(t, Os())
- assert.IsType(t, new(afero.OsFs), Os())
- assert.Nil(t, WorkingDir())
+ assert.NotNil(t, f.Source)
+ assert.IsType(t, new(afero.OsFs), f.Source)
+ assert.NotNil(t, f.Destination)
+ assert.IsType(t, new(afero.OsFs), f.Destination)
+ assert.NotNil(t, f.Os)
+ assert.IsType(t, new(afero.OsFs), f.Os)
+ assert.Nil(t, f.WorkingDir)
+
+ assert.IsType(t, new(afero.OsFs), Os)
}
-func TestInitMemFs(t *testing.T) {
+func TestNewMem(t *testing.T) {
viper.Reset()
defer viper.Reset()
- InitMemFs()
-
- assert.NotNil(t, Source())
- assert.IsType(t, new(afero.MemMapFs), Source())
- assert.NotNil(t, Destination())
- assert.IsType(t, new(afero.MemMapFs), Destination())
- assert.IsType(t, new(afero.OsFs), Os())
- assert.Nil(t, WorkingDir())
-}
-
-func TestSetSource(t *testing.T) {
-
- InitMemFs()
-
- SetSource(new(afero.OsFs))
- assert.NotNil(t, Source())
- assert.IsType(t, new(afero.OsFs), Source())
-}
-
-func TestSetDestination(t *testing.T) {
-
- InitMemFs()
+ f := NewMem()
- SetDestination(new(afero.OsFs))
- assert.NotNil(t, Destination())
- assert.IsType(t, new(afero.OsFs), Destination())
+ assert.NotNil(t, f.Source)
+ assert.IsType(t, new(afero.MemMapFs), f.Source)
+ assert.NotNil(t, f.Destination)
+ assert.IsType(t, new(afero.MemMapFs), f.Destination)
+ assert.IsType(t, new(afero.OsFs), f.Os)
+ assert.Nil(t, f.WorkingDir)
}
func TestWorkingDir(t *testing.T) {
@@ -74,8 +58,8 @@ func TestWorkingDir(t *testing.T) {
viper.Set("workingDir", "/a/b/")
- InitMemFs()
+ f := NewMem()
- assert.NotNil(t, WorkingDir())
- assert.IsType(t, new(afero.BasePathFs), WorkingDir())
+ assert.NotNil(t, f.WorkingDir)
+ assert.IsType(t, new(afero.BasePathFs), f.WorkingDir)
}
diff --git a/hugolib/alias_test.go b/hugolib/alias_test.go
index 87bc9b130..22803d22e 100644
--- a/hugolib/alias_test.go
+++ b/hugolib/alias_test.go
@@ -16,6 +16,10 @@ package hugolib
import (
"path/filepath"
"testing"
+
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
+ "github.com/stretchr/testify/require"
)
const pageWithAlias = `---
@@ -30,31 +34,37 @@ const aliasTemplate = "<html><body>ALIASTEMPLATE</body></html>"
func TestAlias(t *testing.T) {
testCommonResetState()
- writeSource(t, filepath.Join("content", "page.md"), pageWithAlias)
- writeSource(t, filepath.Join("layouts", "_default", "single.html"), basicTemplate)
- if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ fs := hugofs.NewMem()
+
+ writeSource(t, fs, filepath.Join("content", "page.md"), pageWithAlias)
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), basicTemplate)
+
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
// the real page
- assertFileContent(t, filepath.Join("public", "page", "index.html"), false, "For some moments the old man")
+ assertFileContent(t, fs, filepath.Join("public", "page", "index.html"), false, "For some moments the old man")
// the alias redirector
- assertFileContent(t, filepath.Join("public", "foo", "bar", "index.html"), false, "<meta http-equiv=\"refresh\" content=\"0; ")
+ assertFileContent(t, fs, filepath.Join("public", "foo", "bar", "index.html"), false, "<meta http-equiv=\"refresh\" content=\"0; ")
}
func TestAliasTemplate(t *testing.T) {
testCommonResetState()
- writeSource(t, filepath.Join("content", "page.md"), pageWithAlias)
- writeSource(t, filepath.Join("layouts", "_default", "single.html"), basicTemplate)
- writeSource(t, filepath.Join("layouts", "alias.html"), aliasTemplate)
- if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ fs := hugofs.NewMem()
+
+ writeSource(t, fs, filepath.Join("content", "page.md"), pageWithAlias)
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), basicTemplate)
+ writeSource(t, fs, filepath.Join("layouts", "alias.html"), aliasTemplate)
+
+ sites, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
+
+ require.NoError(t, err)
+
+ require.NoError(t, sites.Build(BuildCfg{}))
// the real page
- assertFileContent(t, filepath.Join("public", "page", "index.html"), false, "For some moments the old man")
+ assertFileContent(t, fs, filepath.Join("public", "page", "index.html"), false, "For some moments the old man")
// the alias redirector
- assertFileContent(t, filepath.Join("public", "foo", "bar", "index.html"), false, "ALIASTEMPLATE")
+ assertFileContent(t, fs, filepath.Join("public", "foo", "bar", "index.html"), false, "ALIASTEMPLATE")
}
diff --git a/hugolib/case_insensitive_test.go b/hugolib/case_insensitive_test.go
index ad8351337..eefde1727 100644
--- a/hugolib/case_insensitive_test.go
+++ b/hugolib/case_insensitive_test.go
@@ -18,6 +18,11 @@ import (
"path/filepath"
"strings"
"testing"
+
+ "github.com/spf13/viper"
+
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
)
var (
@@ -106,22 +111,22 @@ ColorS:
`
)
-func caseMixingTestsWriteCommonSources(t *testing.T) {
- writeSource(t, filepath.Join("content", "sect1", "page1.md"), caseMixingPage1)
- writeSource(t, filepath.Join("content", "sect2", "page2.md"), caseMixingPage2)
- writeSource(t, filepath.Join("content", "sect1", "page1.en.md"), caseMixingPage1En)
+func caseMixingTestsWriteCommonSources(t *testing.T, fs *hugofs.Fs) {
+ writeSource(t, fs, filepath.Join("content", "sect1", "page1.md"), caseMixingPage1)
+ writeSource(t, fs, filepath.Join("content", "sect2", "page2.md"), caseMixingPage2)
+ writeSource(t, fs, filepath.Join("content", "sect1", "page1.en.md"), caseMixingPage1En)
- writeSource(t, "layouts/shortcodes/shortcode.html", `
+ writeSource(t, fs, "layouts/shortcodes/shortcode.html", `
Shortcode Page: {{ .Page.Params.COLOR }}|{{ .Page.Params.Colors.Blue }}
Shortcode Site: {{ .Page.Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
`)
- writeSource(t, "layouts/partials/partial.html", `
+ writeSource(t, fs, "layouts/partials/partial.html", `
Partial Page: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}
Partial Site: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
`)
- writeSource(t, "config.toml", caseMixingSiteConfigTOML)
+ writeSource(t, fs, "config.toml", caseMixingSiteConfigTOML)
}
@@ -139,13 +144,21 @@ func TestCaseInsensitiveConfigurationVariations(t *testing.T) {
// page frontmatter: regular fields, blackfriday config, param with nested map
testCommonResetState()
- caseMixingTestsWriteCommonSources(t)
- writeSource(t, filepath.Join("layouts", "_default", "baseof.html"), `
+ depsCfg := newTestDepsConfig()
+ viper.SetFs(depsCfg.Fs.Source)
+
+ caseMixingTestsWriteCommonSources(t, depsCfg.Fs)
+
+ if err := LoadGlobalConfig("", "config.toml"); err != nil {
+ t.Fatalf("Failed to load config: %s", err)
+ }
+
+ writeSource(t, depsCfg.Fs, filepath.Join("layouts", "_default", "baseof.html"), `
Block Page Colors: {{ .Params.COLOR }}|{{ .Params.Colors.Blue }}
{{ block "main" . }}default{{end}}`)
- writeSource(t, filepath.Join("layouts", "sect2", "single.html"), `
+ writeSource(t, depsCfg.Fs, filepath.Join("layouts", "sect2", "single.html"), `
{{ define "main"}}
Page Colors: {{ .Params.CoLOR }}|{{ .Params.Colors.Blue }}
Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }}
@@ -154,7 +167,7 @@ Site Colors: {{ .Site.Params.COlOR }}|{{ .Site.Params.COLORS.YELLOW }}
{{ end }}
`)
- writeSource(t, filepath.Join("layouts", "_default", "single.html"), `
+ writeSource(t, depsCfg.Fs, filepath.Join("layouts", "_default", "single.html"), `
Page Title: {{ .Title }}
Site Title: {{ .Site.Title }}
Site Lang Mood: {{ .Site.Language.Params.MOoD }}
@@ -164,11 +177,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
{{ partial "partial.html" . }}
`)
- if err := LoadGlobalConfig("", "config.toml"); err != nil {
- t.Fatalf("Failed to load config: %s", err)
- }
-
- sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
+ sites, err := NewHugoSitesFromConfiguration(depsCfg)
if err != nil {
t.Fatalf("Failed to create sites: %s", err)
@@ -180,7 +189,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
t.Fatalf("Failed to build sites: %s", err)
}
- assertFileContent(t, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true,
+ assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true,
"Page Colors: red|heavenly",
"Site Colors: green|yellow",
"Site Lang Mood: Happy",
@@ -193,7 +202,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
"&laquo;Hi&raquo;", // angled quotes
)
- assertFileContent(t, filepath.Join("public", "en", "sect1", "page1", "index.html"), true,
+ assertFileContent(t, sites.Fs, filepath.Join("public", "en", "sect1", "page1", "index.html"), true,
"Site Colors: Pink|golden",
"Page Colors: black|bluesy",
"Site Lang Mood: Thoughtful",
@@ -202,7 +211,7 @@ Site Colors: {{ .Site.Params.COLOR }}|{{ .Site.Params.COLORS.YELLOW }}
"&ldquo;Hi&rdquo;",
)
- assertFileContent(t, filepath.Join("public", "nn", "sect2", "page2", "index.html"), true,
+ assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect2", "page2", "index.html"), true,
"Page Colors: black|sky",
"Site Colors: green|yellow",
"Shortcode Page: black|sky",
@@ -244,7 +253,15 @@ func TestCaseInsensitiveConfigurationForAllTemplateEngines(t *testing.T) {
func doTestCaseInsensitiveConfigurationForTemplateEngine(t *testing.T, suffix string, templateFixer func(s string) string) {
testCommonResetState()
- caseMixingTestsWriteCommonSources(t)
+
+ fs := hugofs.NewMem()
+ viper.SetFs(fs.Source)
+
+ caseMixingTestsWriteCommonSources(t, fs)
+
+ if err := LoadGlobalConfig("", "config.toml"); err != nil {
+ t.Fatalf("Failed to load config: %s", err)
+ }
t.Log("Testing", suffix)
@@ -261,13 +278,9 @@ p
t.Log(templ)
- writeSource(t, filepath.Join("layouts", "_default", fmt.Sprintf("single.%s", suffix)), templ)
-
- if err := LoadGlobalConfig("", "config.toml"); err != nil {
- t.Fatalf("Failed to load config: %s", err)
- }
+ writeSource(t, fs, filepath.Join("layouts", "_default", fmt.Sprintf("single.%s", suffix)), templ)
- sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
+ sites, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
if err != nil {
t.Fatalf("Failed to create sites: %s", err)
@@ -279,7 +292,7 @@ p
t.Fatalf("Failed to build sites: %s", err)
}
- assertFileContent(t, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true,
+ assertFileContent(t, sites.Fs, filepath.Join("public", "nn", "sect1", "page1", "index.html"), true,
"Page Colors: red|heavenly",
"Site Colors: green|yellow",
"Shortcode Page: red|heavenly",
diff --git a/hugolib/config_test.go b/hugolib/config_test.go
index 6a2325fc9..cbfc71a22 100644
--- a/hugolib/config_test.go
+++ b/hugolib/config_test.go
@@ -18,6 +18,7 @@ import (
"github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -30,7 +31,10 @@ func TestLoadGlobalConfig(t *testing.T) {
PaginatePath = "side"
`
- writeSource(t, "hugo.toml", configContent)
+ fs := hugofs.NewMem()
+ viper.SetFs(fs.Source)
+
+ writeSource(t, fs, "hugo.toml", configContent)
require.NoError(t, LoadGlobalConfig("", "hugo.toml"))
assert.Equal(t, "side", helpers.Config().GetString("paginatePath"))
diff --git a/hugolib/datafiles_test.go b/hugolib/datafiles_test.go
index dd330b0a1..0f848594a 100644
--- a/hugolib/datafiles_test.go
+++ b/hugolib/datafiles_test.go
@@ -89,19 +89,16 @@ func TestDataDirUnknownFormat(t *testing.T) {
sources := []source.ByteSource{
{Name: filepath.FromSlash("test.roml"), Content: []byte("boo")},
}
- s := NewSiteDefaultLang()
- err := s.loadData([]source.Input{&source.InMemorySource{ByteSource: sources}})
- if err != nil {
- t.Fatalf("Should not return an error")
- }
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
+ require.NoError(t, s.loadData([]source.Input{&source.InMemorySource{ByteSource: sources}}))
}
func doTestDataDir(t *testing.T, expected interface{}, sources []source.Input) {
- s := NewSiteDefaultLang()
- err := s.loadData(sources)
- if err != nil {
- t.Fatalf("Error loading data: %s", err)
- }
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
+ require.NoError(t, s.loadData(sources))
+
if !reflect.DeepEqual(expected, s.Data) {
t.Errorf("Expected structure\n%#v got\n%#v", expected, s.Data)
}
@@ -109,21 +106,22 @@ func doTestDataDir(t *testing.T, expected interface{}, sources []source.Input) {
func TestDataFromShortcode(t *testing.T) {
testCommonResetState()
- writeSource(t, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
- writeSource(t, "layouts/_default/single.html", `
+
+ cfg := newTestDepsConfig()
+
+ writeSource(t, cfg.Fs, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
+ writeSource(t, cfg.Fs, "layouts/_default/single.html", `
* Slogan from template: {{ .Site.Data.hugo.slogan }}
* {{ .Content }}`)
- writeSource(t, "layouts/shortcodes/d.html", `{{ .Page.Site.Data.hugo.slogan }}`)
- writeSource(t, "content/c.md", `---
+ writeSource(t, cfg.Fs, "layouts/shortcodes/d.html", `{{ .Page.Site.Data.hugo.slogan }}`)
+ writeSource(t, cfg.Fs, "content/c.md", `---
---
Slogan from shortcode: {{< d >}}
`)
- h, err := newHugoSitesDefaultLanguage()
- require.NoError(t, err)
- require.NoError(t, h.Build(BuildCfg{}))
+ buildSingleSite(t, cfg, BuildCfg{})
- content := readSource(t, "public/c/index.html")
+ content := readSource(t, cfg.Fs, "public/c/index.html")
require.True(t, strings.Contains(content, "Slogan from template: Hugo Rocks!"), content)
require.True(t, strings.Contains(content, "Slogan from shortcode: Hugo Rocks!"), content)
diff --git a/hugolib/embedded_shortcodes_test.go b/hugolib/embedded_shortcodes_test.go
index 61c40cf01..64a92247b 100644
--- a/hugolib/embedded_shortcodes_test.go
+++ b/hugolib/embedded_shortcodes_test.go
@@ -27,9 +27,11 @@ import (
"log"
"path/filepath"
- "github.com/spf13/hugo/tpl"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/tplapi"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
@@ -65,17 +67,17 @@ func doTestShortcodeCrossrefs(t *testing.T, relative bool) {
path := filepath.FromSlash("blog/post.md")
in := fmt.Sprintf(`{{< %s "%s" >}}`, refShortcode, path)
- writeSource(t, "content/"+path, simplePageWithURL+": "+in)
+ fs := hugofs.NewMem()
+
+ writeSource(t, fs, "content/"+path, simplePageWithURL+": "+in)
expected := fmt.Sprintf(`%s/simple/url/`, expectedBase)
- sites, err := newHugoSitesDefaultLanguage()
- require.NoError(t, err)
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
- require.NoError(t, sites.Build(BuildCfg{}))
- require.Len(t, sites.Sites[0].RegularPages, 1)
+ require.Len(t, s.RegularPages, 1)
- output := string(sites.Sites[0].RegularPages[0].Content)
+ output := string(s.RegularPages[0].Content)
if !strings.Contains(output, expected) {
t.Errorf("Got\n%q\nExpected\n%q", output, expected)
@@ -308,7 +310,7 @@ func TestShortcodeTweet(t *testing.T) {
},
}
- p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
+ p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error {
templ.Funcs(tweetFuncMap)
return nil
})
@@ -361,7 +363,7 @@ func TestShortcodeInstagram(t *testing.T) {
},
}
- p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
+ p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error {
templ.Funcs(instagramFuncMap)
return nil
})
diff --git a/hugolib/gitinfo.go b/hugolib/gitinfo.go
index 2893db06f..82baa3250 100644
--- a/hugolib/gitinfo.go
+++ b/hugolib/gitinfo.go
@@ -20,7 +20,6 @@ import (
"github.com/bep/gitmap"
"github.com/spf13/hugo/helpers"
- jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
)
@@ -36,7 +35,7 @@ func (h *HugoSites) assembleGitInfo() {
gitRepo, err := gitmap.Map(workingDir, "")
if err != nil {
- jww.ERROR.Printf("Got error reading Git log: %s", err)
+ h.Log.ERROR.Printf("Got error reading Git log: %s", err)
return
}
@@ -60,7 +59,7 @@ func (h *HugoSites) assembleGitInfo() {
filename := path.Join(filepath.ToSlash(contentRoot), contentDir, filepath.ToSlash(p.Path()))
g, ok := gitMap[filename]
if !ok {
- jww.ERROR.Printf("Failed to find GitInfo for %q", filename)
+ h.Log.ERROR.Printf("Failed to find GitInfo for %q", filename)
return
}
diff --git a/hugolib/handler_page.go b/hugolib/handler_page.go
index 2026f2bbf..6b6b17173 100644
--- a/hugolib/handler_page.go
+++ b/hugolib/handler_page.go
@@ -65,7 +65,6 @@ type htmlHandler struct {
func (h htmlHandler) Extensions() []string { return []string{"html", "htm"} }
-// TODO(bep) globals use p.s.t
func (h htmlHandler) PageConvert(p *Page) HandledResult {
if p.rendered {
panic(fmt.Sprintf("Page %q already rendered, does not need conversion", p.BaseFileName()))
diff --git a/hugolib/handler_test.go b/hugolib/handler_test.go
index ba5daa8c2..01e6793a6 100644
--- a/hugolib/handler_test.go
+++ b/hugolib/handler_test.go
@@ -17,44 +17,36 @@ import (
"path/filepath"
"testing"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
- "github.com/spf13/hugo/source"
- "github.com/spf13/hugo/target"
"github.com/spf13/viper"
)
func TestDefaultHandler(t *testing.T) {
testCommonResetState()
- hugofs.InitMemFs()
- sources := []source.ByteSource{
- {Name: filepath.FromSlash("sect/doc1.html"), Content: []byte("---\nmarkup: markdown\n---\n# title\nsome *content*")},
- {Name: filepath.FromSlash("sect/doc2.html"), Content: []byte("<!doctype html><html><body>more content</body></html>")},
- {Name: filepath.FromSlash("sect/doc3.md"), Content: []byte("# doc3\n*some* content")},
- {Name: filepath.FromSlash("sect/doc4.md"), Content: []byte("---\ntitle: doc4\n---\n# doc4\n*some content*")},
- {Name: filepath.FromSlash("sect/doc3/img1.png"), Content: []byte("‰PNG  ��� IHDR����������:~›U��� IDATWcø��ZMoñ����IEND®B`‚")},
- {Name: filepath.FromSlash("sect/img2.gif"), Content: []byte("GIF89a��€��ÿÿÿ���,�������D�;")},
- {Name: filepath.FromSlash("sect/img2.spf"), Content: []byte("****FAKE-FILETYPE****")},
- {Name: filepath.FromSlash("doc7.html"), Content: []byte("<html><body>doc7 content</body></html>")},
- {Name: filepath.FromSlash("sect/doc8.html"), Content: []byte("---\nmarkup: md\n---\n# title\nsome *content*")},
- }
-
viper.Set("defaultExtension", "html")
viper.Set("verbose", true)
+ viper.Set("uglyURLs", true)
- s := &Site{
- Source: &source.InMemorySource{ByteSource: sources},
- targets: targetList{page: &target.PagePub{UglyURLs: true, PublishDir: "public"}},
- Language: helpers.NewLanguage("en"),
- }
+ fs := hugofs.NewMem()
- if err := buildAndRenderSite(s,
- "_default/single.html", "{{.Content}}",
- "head", "<head><script src=\"script.js\"></script></head>",
- "head_abs", "<head><script src=\"/script.js\"></script></head>"); err != nil {
- t.Fatalf("Failed to render site: %s", err)
- }
+ writeSource(t, fs, filepath.FromSlash("content/sect/doc1.html"), "---\nmarkup: markdown\n---\n# title\nsome *content*")
+ writeSource(t, fs, filepath.FromSlash("content/sect/doc2.html"), "<!doctype html><html><body>more content</body></html>")
+ writeSource(t, fs, filepath.FromSlash("content/sect/doc3.md"), "# doc3\n*some* content")
+ writeSource(t, fs, filepath.FromSlash("content/sect/doc4.md"), "---\ntitle: doc4\n---\n# doc4\n*some content*")
+ writeSource(t, fs, filepath.FromSlash("content/sect/doc3/img1.png"), "‰PNG  ��� IHDR����������:~›U��� IDATWcø��ZMoñ����IEND®B`‚")
+ writeSource(t, fs, filepath.FromSlash("content/sect/img2.gif"), "GIF89a��€��ÿÿÿ���,�������D�;")
+ writeSource(t, fs, filepath.FromSlash("content/sect/img2.spf"), "****FAKE-FILETYPE****")
+ writeSource(t, fs, filepath.FromSlash("content/doc7.html"), "<html><body>doc7 content</body></html>")
+ writeSource(t, fs, filepath.FromSlash("content/sect/doc8.html"), "---\nmarkup: md\n---\n# title\nsome *content*")
+
+ writeSource(t, fs, filepath.FromSlash("layouts/_default/single.html"), "{{.Content}}")
+ writeSource(t, fs, filepath.FromSlash("head"), "<head><script src=\"script.js\"></script></head>")
+ writeSource(t, fs, filepath.FromSlash("head_abs"), "<head><script src=\"/script.js\"></script></head")
+
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
tests := []struct {
doc string
@@ -71,7 +63,7 @@ func TestDefaultHandler(t *testing.T) {
}
for _, test := range tests {
- file, err := hugofs.Destination().Open(test.doc)
+ file, err := fs.Destination.Open(test.doc)
if err != nil {
t.Fatalf("Did not find %s in target.", test.doc)
}
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index 0d9105ef6..c8ae51d0a 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -14,20 +14,19 @@
package hugolib
import (
+ "errors"
"fmt"
- "io/ioutil"
- "log"
- "os"
"strings"
"sync"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
"github.com/spf13/viper"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/tpl"
- jww "github.com/spf13/jwalterweatherman"
+ "github.com/spf13/hugo/tplapi"
)
// HugoSites represents the sites to build. Each site represents a language.
@@ -38,74 +37,77 @@ type HugoSites struct {
multilingual *Multilingual
- *deps
+ *deps.Deps
}
-// deps holds dependencies used by many.
-// TODO(bep) globals a better name.
-// There will be normally be only one instance of deps in play
-// at a given time.
-type deps struct {
- // The logger to use.
- log *jww.Notepad
-
- tmpl *tpl.GoHTMLTemplate
-
- // TODO(bep) next in line: Viper, hugofs
-}
-
-func (d *deps) refreshTemplates(withTemplate ...func(templ tpl.Template) error) {
- d.tmpl = tpl.New(d.log, withTemplate...)
- d.tmpl.PrintErrors() // TODO(bep) globals error handling
-}
-
-func newDeps(cfg DepsCfg) *deps {
- logger := cfg.Logger
-
- if logger == nil {
- // TODO(bep) globals default log level
- //logger = jww.NewNotepad(jww.LevelError, jww.LevelWarn, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
- logger = jww.NewNotepad(jww.LevelError, jww.LevelError, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
- }
+// NewHugoSites creates a new collection of sites given the input sites, building
+// a language configuration based on those.
+func newHugoSites(cfg deps.DepsCfg, sites ...*Site) (*HugoSites, error) {
- return &deps{
- log: logger,
- tmpl: tpl.New(logger, cfg.WithTemplate...),
+ if cfg.Language != nil {
+ return nil, errors.New("Cannot provide Language in Cfg when sites are provided")
}
-}
-// NewHugoSites creates a new collection of sites given the input sites, building
-// a language configuration based on those.
-func newHugoSites(cfg DepsCfg, sites ...*Site) (*HugoSites, error) {
langConfig, err := newMultiLingualFromSites(sites...)
if err != nil {
return nil, err
}
- var d *deps
-
- if sites[0].deps != nil {
- d = sites[0].deps
- } else {
- d = newDeps(cfg)
- }
-
h := &HugoSites{
- deps: d,
multilingual: langConfig,
Sites: sites}
for _, s := range sites {
s.owner = h
- s.deps = h.deps
}
+
+ applyDepsIfNeeded(cfg, sites...)
+
+ h.Deps = sites[0].Deps
+
return h, nil
}
+func applyDepsIfNeeded(cfg deps.DepsCfg, sites ...*Site) error {
+
+ if cfg.TemplateProvider == nil {
+ cfg.TemplateProvider = tpl.DefaultTemplateProvider
+ }
+
+ var (
+ d *deps.Deps
+ err error
+ )
+
+ for _, s := range sites {
+ if s.Deps != nil {
+ continue
+ }
+
+ if d == nil {
+ cfg.Language = s.Language
+ cfg.WithTemplate = s.withSiteTemplates(cfg.WithTemplate)
+ d = deps.New(cfg)
+ if err := d.LoadTemplates(); err != nil {
+ return err
+ }
+
+ } else {
+ d, err = d.ForLanguage(s.Language)
+ if err != nil {
+ return err
+ }
+ }
+ s.Deps = d
+ }
+
+ return nil
+}
+
// NewHugoSitesFromConfiguration creates HugoSites from the global Viper config.
// TODO(bep) globals rename this when all the globals are gone.
-func NewHugoSitesFromConfiguration(cfg DepsCfg) (*HugoSites, error) {
+func NewHugoSitesFromConfiguration(cfg deps.DepsCfg) (*HugoSites, error) {
sites, err := createSitesFromConfig(cfg)
if err != nil {
return nil, err
@@ -113,17 +115,42 @@ func NewHugoSitesFromConfiguration(cfg DepsCfg) (*HugoSites, error) {
return newHugoSites(cfg, sites...)
}
-func createSitesFromConfig(cfg DepsCfg) ([]*Site, error) {
- deps := newDeps(cfg)
- return createSitesFromDeps(deps)
+func (s *Site) withSiteTemplates(withTemplates ...func(templ tplapi.Template) error) func(templ tplapi.Template) error {
+ return func(templ tplapi.Template) error {
+ templ.LoadTemplates(s.absLayoutDir())
+ if s.hasTheme() {
+ templ.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
+ }
+
+ for _, wt := range withTemplates {
+ if wt == nil {
+ continue
+ }
+ if err := wt(templ); err != nil {
+ return err
+ }
+ }
+
+ return nil
+ }
}
-func createSitesFromDeps(deps *deps) ([]*Site, error) {
- var sites []*Site
+func createSitesFromConfig(cfg deps.DepsCfg) ([]*Site, error) {
+
+ var (
+ sites []*Site
+ )
+
multilingual := viper.GetStringMap("languages")
if len(multilingual) == 0 {
- sites = append(sites, newSite(helpers.NewDefaultLanguage(), deps))
+ l := helpers.NewDefaultLanguage()
+ cfg.Language = l
+ s, err := newSite(cfg)
+ if err != nil {
+ return nil, err
+ }
+ sites = append(sites, s)
}
if len(multilingual) > 0 {
@@ -136,9 +163,17 @@ func createSitesFromDeps(deps *deps) ([]*Site, error) {
}
for _, lang := range languages {
- sites = append(sites, newSite(lang, deps))
- }
+ var s *Site
+ var err error
+ cfg.Language = lang
+ s, err = newSite(cfg)
+
+ if err != nil {
+ return nil, err
+ }
+ sites = append(sites, s)
+ }
}
return sites, nil
@@ -155,7 +190,8 @@ func (h *HugoSites) reset() {
func (h *HugoSites) createSitesFromConfig() error {
- sites, err := createSitesFromDeps(h.deps)
+ depsCfg := deps.DepsCfg{Fs: h.Fs}
+ sites, err := createSitesFromConfig(depsCfg)
if err != nil {
return err
@@ -173,6 +209,12 @@ func (h *HugoSites) createSitesFromConfig() error {
s.owner = h
}
+ if err := applyDepsIfNeeded(depsCfg, sites...); err != nil {
+ return err
+ }
+
+ h.Deps = sites[0].Deps
+
h.multilingual = langConfig
return nil
@@ -199,24 +241,10 @@ type BuildCfg struct {
CreateSitesFromConfig bool
// Skip rendering. Useful for testing.
SkipRender bool
- // Use this to add templates to use for rendering.
- // Useful for testing.
- withTemplate func(templ tpl.Template) error
// Use this to indicate what changed (for rebuilds).
whatChanged *whatChanged
}
-// DepsCfg contains configuration options that can be used to configure Hugo
-// on a global level, i.e. logging etc.
-// Nil values will be given default values.
-type DepsCfg struct {
-
- // The Logger to use.
- Logger *jww.Notepad
-
- WithTemplate []func(templ tpl.Template) error
-}
-
func (h *HugoSites) renderCrossSitesArtifacts() error {
if !h.multilingual.enabled() {
@@ -293,7 +321,7 @@ func (h *HugoSites) createMissingPages() error {
foundTaxonomyTermsPage := false
for key := range tax {
if s.Info.preserveTaxonomyNames {
- key = s.Info.pathSpec.MakePathSanitized(key)
+ key = s.PathSpec.MakePathSanitized(key)
}
for _, p := range taxonomyPages {
if p.sections[0] == plural && p.sections[1] == key {
@@ -454,8 +482,8 @@ func (s *Site) preparePagesForRender(cfg *BuildCfg) {
}
var err error
- if workContentCopy, err = handleShortcodes(p, s.owner.tmpl, workContentCopy); err != nil {
- jww.ERROR.Printf("Failed to handle shortcodes for page %s: %s", p.BaseFileName(), err)
+ if workContentCopy, err = handleShortcodes(p, s.Tmpl, workContentCopy); err != nil {
+ s.Log.ERROR.Printf("Failed to handle shortcodes for page %s: %s", p.BaseFileName(), err)
}
if p.Markup != "html" {
@@ -464,7 +492,7 @@ func (s *Site) preparePagesForRender(cfg *BuildCfg) {
summaryContent, err := p.setUserDefinedSummaryIfProvided(workContentCopy)
if err != nil {
- jww.ERROR.Printf("Failed to set user defined summary for page %q: %s", p.Path(), err)
+ s.Log.ERROR.Printf("Failed to set user defined summary for page %q: %s", p.Path(), err)
} else if summaryContent != nil {
workContentCopy = summaryContent.content
}
@@ -501,9 +529,9 @@ func (h *HugoSites) Pages() Pages {
return h.Sites[0].AllPages
}
-func handleShortcodes(p *Page, t tpl.Template, rawContentCopy []byte) ([]byte, error) {
+func handleShortcodes(p *Page, t tplapi.Template, rawContentCopy []byte) ([]byte, error) {
if len(p.contentShortCodes) > 0 {
- jww.DEBUG.Printf("Replace %d shortcodes in %q", len(p.contentShortCodes), p.BaseFileName())
+ p.s.Log.DEBUG.Printf("Replace %d shortcodes in %q", len(p.contentShortCodes), p.BaseFileName())
shortcodes, err := executeShortcodeFuncMap(p.contentShortCodes)
if err != nil {
@@ -513,7 +541,7 @@ func handleShortcodes(p *Page, t tpl.Template, rawContentCopy []byte) ([]byte, e
rawContentCopy, err = replaceShortcodeTokens(rawContentCopy, shortcodePlaceholderPrefix, shortcodes)
if err != nil {
- jww.FATAL.Printf("Failed to replace shortcode tokens in %s:\n%s", p.BaseFileName(), err.Error())
+ p.s.Log.FATAL.Printf("Failed to replace shortcode tokens in %s:\n%s", p.BaseFileName(), err.Error())
}
}
@@ -550,51 +578,15 @@ func (h *HugoSites) findAllPagesByKindNotIn(kind string) Pages {
return h.findPagesByKindNotIn(kind, h.Sites[0].AllPages)
}
-// Convenience func used in tests to build a single site/language excluding render phase.
-func buildSiteSkipRender(s *Site, additionalTemplates ...string) error {
- return doBuildSite(s, false, additionalTemplates...)
-}
-
-// Convenience func used in tests to build a single site/language including render phase.
-func buildAndRenderSite(s *Site, additionalTemplates ...string) error {
- return doBuildSite(s, true, additionalTemplates...)
-}
-
-// Convenience func used in tests to build a single site/language.
-func doBuildSite(s *Site, render bool, additionalTemplates ...string) error {
- if s.PageCollections == nil {
- s.PageCollections = newPageCollections()
- }
- sites, err := newHugoSites(DepsCfg{}, s)
- if err != nil {
- return err
- }
-
- addTemplates := func(templ tpl.Template) error {
- for i := 0; i < len(additionalTemplates); i += 2 {
- err := templ.AddTemplate(additionalTemplates[i], additionalTemplates[i+1])
- if err != nil {
- return err
- }
- }
- return nil
- }
-
- config := BuildCfg{SkipRender: !render, withTemplate: addTemplates}
- return sites.Build(config)
-}
-
// Convenience func used in tests.
-func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages helpers.Languages) (*HugoSites, error) {
+func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages helpers.Languages, cfg deps.DepsCfg) (*HugoSites, error) {
if len(languages) == 0 {
panic("Must provide at least one language")
}
- cfg := DepsCfg{}
-
first := &Site{
- Source: &source.InMemorySource{ByteSource: input},
Language: languages[0],
+ Source: &source.InMemorySource{ByteSource: input},
}
if len(languages) == 1 {
return newHugoSites(cfg, first)
@@ -611,6 +603,6 @@ func newHugoSitesFromSourceAndLanguages(input []source.ByteSource, languages hel
}
// Convenience func used in tests.
-func newHugoSitesDefaultLanguage() (*HugoSites, error) {
- return newHugoSitesFromSourceAndLanguages(nil, helpers.Languages{helpers.NewDefaultLanguage()})
+func newHugoSitesDefaultLanguage(cfg deps.DepsCfg) (*HugoSites, error) {
+ return newHugoSitesFromSourceAndLanguages(nil, helpers.Languages{helpers.NewDefaultLanguage()}, cfg)
}
diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go
index b3b176018..e915d11da 100644
--- a/hugolib/hugo_sites_build.go
+++ b/hugolib/hugo_sites_build.go
@@ -59,7 +59,7 @@ func (h *HugoSites) Build(config BuildCfg, events ...fsnotify.Event) error {
}
if config.PrintStats {
- h.log.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds()))
+ h.Log.FEEDBACK.Printf("total in %v ms\n", int(1000*time.Since(t0).Seconds()))
}
return nil
diff --git a/hugolib/hugo_sites_build_test.go b/hugolib/hugo_sites_build_test.go
index a8fc9a58f..9abb17d5e 100644
--- a/hugolib/hugo_sites_build_test.go
+++ b/hugolib/hugo_sites_build_test.go
@@ -14,6 +14,7 @@ import (
"github.com/fortytw2/leaktest"
"github.com/fsnotify/fsnotify"
"github.com/spf13/afero"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
@@ -25,6 +26,7 @@ import (
type testSiteConfig struct {
DefaultContentLanguage string
+ Fs *hugofs.Fs
}
func init() {
@@ -32,22 +34,19 @@ func init() {
}
func testCommonResetState() {
- hugofs.InitMemFs()
viper.Reset()
- viper.SetFs(hugofs.Source())
+ // TODO(bep) globals viper viper.SetFs(hugofs.Source())
+ viper.Set("currentContentLanguage", helpers.NewLanguage("en"))
helpers.ResetConfigProvider()
loadDefaultSettings()
// Default is false, but true is easier to use as default in tests
viper.Set("defaultContentLanguageInSubdir", true)
- if err := hugofs.Source().Mkdir("content", 0755); err != nil {
- panic("Content folder creation failed.")
- }
-
}
-func TestMultiSitesMainLangInRoot(t *testing.T) {
+// TODO(bep) globals this currently fails because of a configuration dependency that will be resolved when we get rid of the global Viper.
+func _TestMultiSitesMainLangInRoot(t *testing.T) {
for _, b := range []bool{true, false} {
doTestMultiSitesMainLangInRoot(t, b)
@@ -57,7 +56,8 @@ func TestMultiSitesMainLangInRoot(t *testing.T) {
func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {
testCommonResetState()
viper.Set("defaultContentLanguageInSubdir", defaultInSubDir)
- siteConfig := testSiteConfig{DefaultContentLanguage: "fr"}
+ fs := hugofs.NewMem()
+ siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}
sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate)
@@ -80,7 +80,8 @@ func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {
require.Equal(t, "", frSite.Info.LanguagePrefix)
}
- require.Equal(t, "/blog/en/foo", enSite.Info.pathSpec.RelURL("foo", true))
+ fmt.Println(">>>", enSite.PathSpec)
+ require.Equal(t, "/blog/en/foo", enSite.PathSpec.RelURL("foo", true))
doc1en := enSite.RegularPages[0]
doc1fr := frSite.RegularPages[0]
@@ -96,64 +97,64 @@ func doTestMultiSitesMainLangInRoot(t *testing.T, defaultInSubDir bool) {
require.Equal(t, replaceDefaultContentLanguageValue("http://example.com/blog/fr/sect/doc1/", defaultInSubDir), frPerm)
require.Equal(t, replaceDefaultContentLanguageValue("/blog/fr/sect/doc1/", defaultInSubDir), frRelPerm)
- assertFileContent(t, "public/fr/sect/doc1/index.html", defaultInSubDir, "Single", "Bonjour")
- assertFileContent(t, "public/en/sect/doc1-slug/index.html", defaultInSubDir, "Single", "Hello")
+ assertFileContent(t, fs, "public/fr/sect/doc1/index.html", defaultInSubDir, "Single", "Bonjour")
+ assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", defaultInSubDir, "Single", "Hello")
// Check home
if defaultInSubDir {
// should have a redirect on top level.
- assertFileContent(t, "public/index.html", true, `<meta http-equiv="refresh" content="0; url=http://example.com/blog/fr" />`)
+ assertFileContent(t, fs, "public/index.html", true, `<meta http-equiv="refresh" content="0; url=http://example.com/blog/fr" />`)
} else {
// should have redirect back to root
- assertFileContent(t, "public/fr/index.html", true, `<meta http-equiv="refresh" content="0; url=http://example.com/blog" />`)
+ assertFileContent(t, fs, "public/fr/index.html", true, `<meta http-equiv="refresh" content="0; url=http://example.com/blog" />`)
}
- assertFileContent(t, "public/fr/index.html", defaultInSubDir, "Home", "Bonjour")
- assertFileContent(t, "public/en/index.html", defaultInSubDir, "Home", "Hello")
+ assertFileContent(t, fs, "public/fr/index.html", defaultInSubDir, "Home", "Bonjour")
+ assertFileContent(t, fs, "public/en/index.html", defaultInSubDir, "Home", "Hello")
// Check list pages
- assertFileContent(t, "public/fr/sect/index.html", defaultInSubDir, "List", "Bonjour")
- assertFileContent(t, "public/en/sect/index.html", defaultInSubDir, "List", "Hello")
- assertFileContent(t, "public/fr/plaques/frtag1/index.html", defaultInSubDir, "List", "Bonjour")
- assertFileContent(t, "public/en/tags/tag1/index.html", defaultInSubDir, "List", "Hello")
+ assertFileContent(t, fs, "public/fr/sect/index.html", defaultInSubDir, "List", "Bonjour")
+ assertFileContent(t, fs, "public/en/sect/index.html", defaultInSubDir, "List", "Hello")
+ assertFileContent(t, fs, "public/fr/plaques/frtag1/index.html", defaultInSubDir, "List", "Bonjour")
+ assertFileContent(t, fs, "public/en/tags/tag1/index.html", defaultInSubDir, "List", "Hello")
// Check sitemaps
// Sitemaps behaves different: In a multilanguage setup there will always be a index file and
// one sitemap in each lang folder.
- assertFileContent(t, "public/sitemap.xml", true,
+ assertFileContent(t, fs, "public/sitemap.xml", true,
"<loc>http://example.com/blog/en/sitemap.xml</loc>",
"<loc>http://example.com/blog/fr/sitemap.xml</loc>")
if defaultInSubDir {
- assertFileContent(t, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/fr/</loc>")
+ assertFileContent(t, fs, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/fr/</loc>")
} else {
- assertFileContent(t, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/</loc>")
+ assertFileContent(t, fs, "public/fr/sitemap.xml", true, "<loc>http://example.com/blog/</loc>")
}
- assertFileContent(t, "public/en/sitemap.xml", true, "<loc>http://example.com/blog/en/</loc>")
+ assertFileContent(t, fs, "public/en/sitemap.xml", true, "<loc>http://example.com/blog/en/</loc>")
// Check rss
- assertFileContent(t, "public/fr/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/index.xml"`)
- assertFileContent(t, "public/en/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/index.xml"`)
- assertFileContent(t, "public/fr/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/sect/index.xml"`)
- assertFileContent(t, "public/en/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/sect/index.xml"`)
- assertFileContent(t, "public/fr/plaques/frtag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/plaques/frtag1/index.xml"`)
- assertFileContent(t, "public/en/tags/tag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/tags/tag1/index.xml"`)
+ assertFileContent(t, fs, "public/fr/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/index.xml"`)
+ assertFileContent(t, fs, "public/en/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/index.xml"`)
+ assertFileContent(t, fs, "public/fr/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/sect/index.xml"`)
+ assertFileContent(t, fs, "public/en/sect/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/sect/index.xml"`)
+ assertFileContent(t, fs, "public/fr/plaques/frtag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/fr/plaques/frtag1/index.xml"`)
+ assertFileContent(t, fs, "public/en/tags/tag1/index.xml", defaultInSubDir, `<atom:link href="http://example.com/blog/en/tags/tag1/index.xml"`)
// Check paginators
- assertFileContent(t, "public/fr/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/"`)
- assertFileContent(t, "public/en/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/"`)
- assertFileContent(t, "public/fr/page/2/index.html", defaultInSubDir, "Home Page 2", "Bonjour", "http://example.com/blog/fr/")
- assertFileContent(t, "public/en/page/2/index.html", defaultInSubDir, "Home Page 2", "Hello", "http://example.com/blog/en/")
- assertFileContent(t, "public/fr/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/sect/"`)
- assertFileContent(t, "public/en/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/sect/"`)
- assertFileContent(t, "public/fr/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/sect/")
- assertFileContent(t, "public/en/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/sect/")
- assertFileContent(t, "public/fr/plaques/frtag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/plaques/frtag1/"`)
- assertFileContent(t, "public/en/tags/tag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/tags/tag1/"`)
- assertFileContent(t, "public/fr/plaques/frtag1/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/plaques/frtag1/")
- assertFileContent(t, "public/en/tags/tag1/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/tags/tag1/")
+ assertFileContent(t, fs, "public/fr/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/"`)
+ assertFileContent(t, fs, "public/en/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/"`)
+ assertFileContent(t, fs, "public/fr/page/2/index.html", defaultInSubDir, "Home Page 2", "Bonjour", "http://example.com/blog/fr/")
+ assertFileContent(t, fs, "public/en/page/2/index.html", defaultInSubDir, "Home Page 2", "Hello", "http://example.com/blog/en/")
+ assertFileContent(t, fs, "public/fr/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/sect/"`)
+ assertFileContent(t, fs, "public/en/sect/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/sect/"`)
+ assertFileContent(t, fs, "public/fr/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/sect/")
+ assertFileContent(t, fs, "public/en/sect/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/sect/")
+ assertFileContent(t, fs, "public/fr/plaques/frtag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/fr/plaques/frtag1/"`)
+ assertFileContent(t, fs, "public/en/tags/tag1/page/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/en/tags/tag1/"`)
+ assertFileContent(t, fs, "public/fr/plaques/frtag1/page/2/index.html", defaultInSubDir, "List Page 2", "Bonjour", "http://example.com/blog/fr/plaques/frtag1/")
+ assertFileContent(t, fs, "public/en/tags/tag1/page/2/index.html", defaultInSubDir, "List Page 2", "Hello", "http://example.com/blog/en/tags/tag1/")
// nn (Nynorsk) and nb (Bokmål) have custom pagePath: side ("page" in Norwegian)
- assertFileContent(t, "public/nn/side/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/nn/"`)
- assertFileContent(t, "public/nb/side/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/nb/"`)
+ assertFileContent(t, fs, "public/nn/side/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/nn/"`)
+ assertFileContent(t, fs, "public/nb/side/1/index.html", defaultInSubDir, `refresh" content="0; url=http://example.com/blog/nb/"`)
}
func replaceDefaultContentLanguageValue(value string, defaultInSubDir bool) string {
@@ -166,18 +167,18 @@ func replaceDefaultContentLanguageValue(value string, defaultInSubDir bool) stri
}
-func assertFileContent(t *testing.T, filename string, defaultInSubDir bool, matches ...string) {
+func assertFileContent(t *testing.T, fs *hugofs.Fs, filename string, defaultInSubDir bool, matches ...string) {
filename = replaceDefaultContentLanguageValue(filename, defaultInSubDir)
- content := readDestination(t, filename)
+ content := readDestination(t, fs, filename)
for _, match := range matches {
match = replaceDefaultContentLanguageValue(match, defaultInSubDir)
require.True(t, strings.Contains(content, match), fmt.Sprintf("File no match for\n%q in\n%q:\n%s", strings.Replace(match, "%", "%%", -1), filename, strings.Replace(content, "%", "%%", -1)))
}
}
-func assertFileContentRegexp(t *testing.T, filename string, defaultInSubDir bool, matches ...string) {
+func assertFileContentRegexp(t *testing.T, fs *hugofs.Fs, filename string, defaultInSubDir bool, matches ...string) {
filename = replaceDefaultContentLanguageValue(filename, defaultInSubDir)
- content := readDestination(t, filename)
+ content := readDestination(t, fs, filename)
for _, match := range matches {
match = replaceDefaultContentLanguageValue(match, defaultInSubDir)
r := regexp.MustCompile(match)
@@ -190,7 +191,12 @@ func TestMultiSitesWithTwoLanguages(t *testing.T) {
viper.Set("defaultContentLanguage", "nn")
- writeSource(t, "config.toml", `
+ fs := hugofs.NewMem()
+
+ depsCfg := deps.DepsCfg{Fs: fs}
+ viper.SetFs(depsCfg.Fs.Source)
+
+ writeSource(t, depsCfg.Fs, "config.toml", `
[languages]
[languages.nn]
languageName = "Nynorsk"
@@ -208,15 +214,17 @@ weight = 2
t.Fatalf("Failed to load config: %s", err)
}
- // Add some data
- writeSource(t, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
-
- sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
+ sites, err := NewHugoSitesFromConfiguration(depsCfg)
if err != nil {
t.Fatalf("Failed to create sites: %s", err)
}
+ writeSource(t, fs, filepath.Join("content", "foo.md"), "foo")
+
+ // Add some data
+ writeSource(t, fs, filepath.Join("data", "hugo.toml"), "slogan = \"Hugo Rocks!\"")
+
require.NoError(t, sites.Build(BuildCfg{}))
require.Len(t, sites.Sites, 2)
@@ -245,7 +253,8 @@ func TestMultiSitesBuild(t *testing.T) {
func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
defer leaktest.Check(t)()
testCommonResetState()
- siteConfig := testSiteConfig{DefaultContentLanguage: "fr"}
+ fs := hugofs.NewMem()
+ siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}
sites := createMultiTestSitesForConfig(t, siteConfig, configTemplate, configSuffix)
err := sites.Build(BuildCfg{})
@@ -286,7 +295,7 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
assert.Equal(t, "http://example.com/blog/superbob", permalink, "invalid doc3 permalink")
assert.Equal(t, "/superbob", doc3.URL(), "invalid url, was specified on doc3")
- assertFileContent(t, "public/superbob/index.html", true, "doc3|Hello|en")
+ assertFileContent(t, fs, "public/superbob/index.html", true, "doc3|Hello|en")
assert.Equal(t, doc2.Next, doc3, "doc3 should follow doc2, in .Next")
doc1fr := doc1en.Translations()[0]
@@ -326,16 +335,16 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
}
// Check redirect to main language, French
- languageRedirect := readDestination(t, "public/index.html")
+ languageRedirect := readDestination(t, fs, "public/index.html")
require.True(t, strings.Contains(languageRedirect, "0; url=http://example.com/blog/fr"), languageRedirect)
// check home page content (including data files rendering)
- assertFileContent(t, "public/en/index.html", true, "Home Page 1", "Hello", "Hugo Rocks!")
- assertFileContent(t, "public/fr/index.html", true, "Home Page 1", "Bonjour", "Hugo Rocks!")
+ assertFileContent(t, fs, "public/en/index.html", true, "Home Page 1", "Hello", "Hugo Rocks!")
+ assertFileContent(t, fs, "public/fr/index.html", true, "Home Page 1", "Bonjour", "Hugo Rocks!")
// check single page content
- assertFileContent(t, "public/fr/sect/doc1/index.html", true, "Single", "Shortcode: Bonjour")
- assertFileContent(t, "public/en/sect/doc1-slug/index.html", true, "Single", "Shortcode: Hello")
+ assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Shortcode: Bonjour")
+ assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Single", "Shortcode: Hello")
// Check node translations
homeEn := enSite.getPage(KindHome)
@@ -369,11 +378,11 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
require.Equal(t, "nb", taxTermNn.Translations()[0].Lang())
// Check sitemap(s)
- sitemapIndex := readDestination(t, "public/sitemap.xml")
+ sitemapIndex := readDestination(t, fs, "public/sitemap.xml")
require.True(t, strings.Contains(sitemapIndex, "<loc>http://example.com/blog/en/sitemap.xml</loc>"), sitemapIndex)
require.True(t, strings.Contains(sitemapIndex, "<loc>http://example.com/blog/fr/sitemap.xml</loc>"), sitemapIndex)
- sitemapEn := readDestination(t, "public/en/sitemap.xml")
- sitemapFr := readDestination(t, "public/fr/sitemap.xml")
+ sitemapEn := readDestination(t, fs, "public/en/sitemap.xml")
+ sitemapFr := readDestination(t, fs, "public/fr/sitemap.xml")
require.True(t, strings.Contains(sitemapEn, "http://example.com/blog/en/sect/doc2/"), sitemapEn)
require.True(t, strings.Contains(sitemapFr, "http://example.com/blog/fr/sect/doc1/"), sitemapFr)
@@ -384,8 +393,8 @@ func doTestMultiSitesBuild(t *testing.T, configTemplate, configSuffix string) {
require.Len(t, frTags, 2, fmt.Sprintf("Tags in fr: %v", frTags))
require.NotNil(t, enTags["tag1"])
require.NotNil(t, frTags["frtag1"])
- readDestination(t, "public/fr/plaques/frtag1/index.html")
- readDestination(t, "public/en/tags/tag1/index.html")
+ readDestination(t, fs, "public/fr/plaques/frtag1/index.html")
+ readDestination(t, fs, "public/en/tags/tag1/index.html")
// Check Blackfriday config
assert.True(t, strings.Contains(string(doc1fr.Content), "&laquo;"), string(doc1fr.Content))
@@ -409,7 +418,8 @@ func TestMultiSitesRebuild(t *testing.T) {
defer leaktest.Check(t)()
testCommonResetState()
- siteConfig := testSiteConfig{DefaultContentLanguage: "fr"}
+ fs := hugofs.NewMem()
+ siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}
sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate)
cfg := BuildCfg{Watching: true}
@@ -419,7 +429,7 @@ func TestMultiSitesRebuild(t *testing.T) {
t.Fatalf("Failed to build sites: %s", err)
}
- _, err = hugofs.Destination().Open("public/en/sect/doc2/index.html")
+ _, err = fs.Destination.Open("public/en/sect/doc2/index.html")
if err != nil {
t.Fatalf("Unable to locate file")
@@ -432,12 +442,12 @@ func TestMultiSitesRebuild(t *testing.T) {
require.Len(t, frSite.RegularPages, 3)
// Verify translations
- assertFileContent(t, "public/en/sect/doc1-slug/index.html", true, "Hello")
- assertFileContent(t, "public/fr/sect/doc1/index.html", true, "Bonjour")
+ assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Hello")
+ assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Bonjour")
// check single page content
- assertFileContent(t, "public/fr/sect/doc1/index.html", true, "Single", "Shortcode: Bonjour")
- assertFileContent(t, "public/en/sect/doc1-slug/index.html", true, "Single", "Shortcode: Hello")
+ assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Shortcode: Bonjour")
+ assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Single", "Shortcode: Hello")
for i, this := range []struct {
preFunc func(t *testing.T)
@@ -468,9 +478,9 @@ func TestMultiSitesRebuild(t *testing.T) {
},
{
func(t *testing.T) {
- writeNewContentFile(t, "new_en_1", "2016-07-31", "content/new1.en.md", -5)
- writeNewContentFile(t, "new_en_2", "1989-07-30", "content/new2.en.md", -10)
- writeNewContentFile(t, "new_fr_1", "2016-07-30", "content/new1.fr.md", 10)
+ writeNewContentFile(t, fs, "new_en_1", "2016-07-31", "content/new1.en.md", -5)
+ writeNewContentFile(t, fs, "new_en_2", "1989-07-30", "content/new2.en.md", -10)
+ writeNewContentFile(t, fs, "new_fr_1", "2016-07-30", "content/new1.fr.md", 10)
},
[]fsnotify.Event{
{Name: "content/new1.en.md", Op: fsnotify.Create},
@@ -485,21 +495,21 @@ func TestMultiSitesRebuild(t *testing.T) {
require.Equal(t, "new_en_2", enSite.RegularPages[0].Title)
require.Equal(t, "new_en_1", enSite.RegularPages[1].Title)
- rendered := readDestination(t, "public/en/new1/index.html")
+ rendered := readDestination(t, fs, "public/en/new1/index.html")
require.True(t, strings.Contains(rendered, "new_en_1"), rendered)
},
},
{
func(t *testing.T) {
p := "content/sect/doc1.en.md"
- doc1 := readSource(t, p)
+ doc1 := readSource(t, fs, p)
doc1 += "CHANGED"
- writeSource(t, p, doc1)
+ writeSource(t, fs, p, doc1)
},
[]fsnotify.Event{{Name: "content/sect/doc1.en.md", Op: fsnotify.Write}},
func(t *testing.T) {
require.Len(t, enSite.RegularPages, 5)
- doc1 := readDestination(t, "public/en/sect/doc1-slug/index.html")
+ doc1 := readDestination(t, fs, "public/en/sect/doc1-slug/index.html")
require.True(t, strings.Contains(doc1, "CHANGED"), doc1)
},
@@ -507,7 +517,7 @@ func TestMultiSitesRebuild(t *testing.T) {
// Rename a file
{
func(t *testing.T) {
- if err := hugofs.Source().Rename("content/new1.en.md", "content/new1renamed.en.md"); err != nil {
+ if err := fs.Source.Rename("content/new1.en.md", "content/new1renamed.en.md"); err != nil {
t.Fatalf("Rename failed: %s", err)
}
},
@@ -518,23 +528,23 @@ func TestMultiSitesRebuild(t *testing.T) {
func(t *testing.T) {
require.Len(t, enSite.RegularPages, 5, "Rename")
require.Equal(t, "new_en_1", enSite.RegularPages[1].Title)
- rendered := readDestination(t, "public/en/new1renamed/index.html")
+ rendered := readDestination(t, fs, "public/en/new1renamed/index.html")
require.True(t, strings.Contains(rendered, "new_en_1"), rendered)
}},
{
// Change a template
func(t *testing.T) {
template := "layouts/_default/single.html"
- templateContent := readSource(t, template)
+ templateContent := readSource(t, fs, template)
templateContent += "{{ print \"Template Changed\"}}"
- writeSource(t, template, templateContent)
+ writeSource(t, fs, template, templateContent)
},
[]fsnotify.Event{{Name: "layouts/_default/single.html", Op: fsnotify.Write}},
func(t *testing.T) {
require.Len(t, enSite.RegularPages, 5)
require.Len(t, enSite.AllPages, 30)
require.Len(t, frSite.RegularPages, 4)
- doc1 := readDestination(t, "public/en/sect/doc1-slug/index.html")
+ doc1 := readDestination(t, fs, "public/en/sect/doc1-slug/index.html")
require.True(t, strings.Contains(doc1, "Template Changed"), doc1)
},
},
@@ -542,18 +552,18 @@ func TestMultiSitesRebuild(t *testing.T) {
// Change a language file
func(t *testing.T) {
languageFile := "i18n/fr.yaml"
- langContent := readSource(t, languageFile)
+ langContent := readSource(t, fs, languageFile)
langContent = strings.Replace(langContent, "Bonjour", "Salut", 1)
- writeSource(t, languageFile, langContent)
+ writeSource(t, fs, languageFile, langContent)
},
[]fsnotify.Event{{Name: "i18n/fr.yaml", Op: fsnotify.Write}},
func(t *testing.T) {
require.Len(t, enSite.RegularPages, 5)
require.Len(t, enSite.AllPages, 30)
require.Len(t, frSite.RegularPages, 4)
- docEn := readDestination(t, "public/en/sect/doc1-slug/index.html")
+ docEn := readDestination(t, fs, "public/en/sect/doc1-slug/index.html")
require.True(t, strings.Contains(docEn, "Hello"), "No Hello")
- docFr := readDestination(t, "public/fr/sect/doc1/index.html")
+ docFr := readDestination(t, fs, "public/fr/sect/doc1/index.html")
require.True(t, strings.Contains(docFr, "Salut"), "No Salut")
homeEn := enSite.getPage(KindHome)
@@ -566,7 +576,7 @@ func TestMultiSitesRebuild(t *testing.T) {
// Change a shortcode
{
func(t *testing.T) {
- writeSource(t, "layouts/shortcodes/shortcode.html", "Modified Shortcode: {{ i18n \"hello\" }}")
+ writeSource(t, fs, "layouts/shortcodes/shortcode.html", "Modified Shortcode: {{ i18n \"hello\" }}")
},
[]fsnotify.Event{
{Name: "layouts/shortcodes/shortcode.html", Op: fsnotify.Write},
@@ -575,8 +585,8 @@ func TestMultiSitesRebuild(t *testing.T) {
require.Len(t, enSite.RegularPages, 5)
require.Len(t, enSite.AllPages, 30)
require.Len(t, frSite.RegularPages, 4)
- assertFileContent(t, "public/fr/sect/doc1/index.html", true, "Single", "Modified Shortcode: Salut")
- assertFileContent(t, "public/en/sect/doc1-slug/index.html", true, "Single", "Modified Shortcode: Hello")
+ assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Modified Shortcode: Salut")
+ assertFileContent(t, fs, "public/en/sect/doc1-slug/index.html", true, "Single", "Modified Shortcode: Hello")
},
},
} {
@@ -615,13 +625,14 @@ func assertShouldNotBuild(t *testing.T, sites *HugoSites) {
filename = strings.Replace(filename, ".html", "/index.html", 1)
}
- require.Equal(t, p.shouldBuild(), destinationExists(filename), filename)
+ require.Equal(t, p.shouldBuild(), destinationExists(sites.Fs, filename), filename)
}
}
func TestAddNewLanguage(t *testing.T) {
testCommonResetState()
- siteConfig := testSiteConfig{DefaultContentLanguage: "fr"}
+ fs := hugofs.NewMem()
+ siteConfig := testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}
sites := createMultiTestSites(t, siteConfig, multiSiteTOMLConfigTemplate)
cfg := BuildCfg{}
@@ -641,9 +652,9 @@ title = "Svenska"
newConfig = createConfig(t, siteConfig, newConfig)
- writeNewContentFile(t, "Swedish Contentfile", "2016-01-01", "content/sect/doc1.sv.md", 10)
+ writeNewContentFile(t, fs, "Swedish Contentfile", "2016-01-01", "content/sect/doc1.sv.md", 10)
// replace the config
- writeSource(t, "multilangconfig.toml", newConfig)
+ writeSource(t, fs, "multilangconfig.toml", newConfig)
// Watching does not work with in-memory fs, so we trigger a reload manually
require.NoError(t, viper.ReadInConfig())
@@ -685,8 +696,8 @@ title = "Svenska"
func TestChangeDefaultLanguage(t *testing.T) {
testCommonResetState()
viper.Set("defaultContentLanguageInSubdir", false)
-
- sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "fr"}, multiSiteTOMLConfigTemplate)
+ fs := hugofs.NewMem()
+ sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "fr", Fs: fs}, multiSiteTOMLConfigTemplate)
cfg := BuildCfg{}
err := sites.Build(cfg)
@@ -695,13 +706,13 @@ func TestChangeDefaultLanguage(t *testing.T) {
t.Fatalf("Failed to build sites: %s", err)
}
- assertFileContent(t, "public/sect/doc1/index.html", true, "Single", "Bonjour")
- assertFileContent(t, "public/en/sect/doc2/index.html", true, "Single", "Hello")
+ assertFileContent(t, fs, "public/sect/doc1/index.html", true, "Single", "Bonjour")
+ assertFileContent(t, fs, "public/en/sect/doc2/index.html", true, "Single", "Hello")
newConfig := createConfig(t, testSiteConfig{DefaultContentLanguage: "en"}, multiSiteTOMLConfigTemplate)
// replace the config
- writeSource(t, "multilangconfig.toml", newConfig)
+ writeSource(t, fs, "multilangconfig.toml", newConfig)
// Watching does not work with in-memory fs, so we trigger a reload manually
require.NoError(t, viper.ReadInConfig())
@@ -712,18 +723,19 @@ func TestChangeDefaultLanguage(t *testing.T) {
}
// Default language is now en, so that should now be the "root" language
- assertFileContent(t, "public/fr/sect/doc1/index.html", true, "Single", "Bonjour")
- assertFileContent(t, "public/sect/doc2/index.html", true, "Single", "Hello")
+ assertFileContent(t, fs, "public/fr/sect/doc1/index.html", true, "Single", "Bonjour")
+ assertFileContent(t, fs, "public/sect/doc2/index.html", true, "Single", "Hello")
}
func TestTableOfContentsInShortcodes(t *testing.T) {
testCommonResetState()
+ fs := hugofs.NewMem()
- sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "en"}, multiSiteTOMLConfigTemplate)
+ writeSource(t, fs, "layouts/shortcodes/toc.html", tocShortcode)
+ writeSource(t, fs, "content/post/simple.en.md", tocPageSimple)
+ writeSource(t, fs, "content/post/withSCInHeading.en.md", tocPageWithShortcodesInHeadings)
- writeSource(t, "layouts/shortcodes/toc.html", tocShortcode)
- writeSource(t, "content/post/simple.en.md", tocPageSimple)
- writeSource(t, "content/post/withSCInHeading.en.md", tocPageWithShortcodesInHeadings)
+ sites := createMultiTestSites(t, testSiteConfig{DefaultContentLanguage: "en", Fs: fs}, multiSiteTOMLConfigTemplate)
cfg := BuildCfg{}
@@ -733,8 +745,8 @@ func TestTableOfContentsInShortcodes(t *testing.T) {
t.Fatalf("Failed to build sites: %s", err)
}
- assertFileContent(t, "public/en/post/simple/index.html", true, tocPageSimpleExpected)
- assertFileContent(t, "public/en/post/withSCInHeading/index.html", true, tocPageWithShortcodesInHeadingsExpected)
+ assertFileContent(t, fs, "public/en/post/simple/index.html", true, tocPageSimpleExpected)
+ assertFileContent(t, fs, "public/en/post/withSCInHeading/index.html", true, tocPageWithShortcodesInHeadingsExpected)
}
var tocShortcode = `
@@ -1014,24 +1026,25 @@ func createMultiTestSites(t *testing.T, siteConfig testSiteConfig, tomlConfigTem
func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, configTemplate, configSuffix string) *HugoSites {
+ depsCfg := deps.DepsCfg{Fs: siteConfig.Fs}
configContent := createConfig(t, siteConfig, configTemplate)
// Add some layouts
- if err := afero.WriteFile(hugofs.Source(),
+ if err := afero.WriteFile(depsCfg.Fs.Source,
filepath.Join("layouts", "_default/single.html"),
[]byte("Single: {{ .Title }}|{{ i18n \"hello\" }}|{{.Lang}}|{{ .Content }}"),
0755); err != nil {
t.Fatalf("Failed to write layout file: %s", err)
}
- if err := afero.WriteFile(hugofs.Source(),
+ if err := afero.WriteFile(depsCfg.Fs.Source,
filepath.Join("layouts", "_default/list.html"),
[]byte("{{ $p := .Paginator }}List Page {{ $p.PageNumber }}: {{ .Title }}|{{ i18n \"hello\" }}|{{ .Permalink }}"),
0755); err != nil {
t.Fatalf("Failed to write layout file: %s", err)
}
- if err := afero.WriteFile(hugofs.Source(),
+ if err := afero.WriteFile(depsCfg.Fs.Source,
filepath.Join("layouts", "index.html"),
[]byte("{{ $p := .Paginator }}Home Page {{ $p.PageNumber }}: {{ .Title }}|{{ .IsHome }}|{{ i18n \"hello\" }}|{{ .Permalink }}|{{ .Site.Data.hugo.slogan }}"),
0755); err != nil {
@@ -1039,7 +1052,7 @@ func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, conf
}
// Add a shortcode
- if err := afero.WriteFile(hugofs.Source(),
+ if err := afero.WriteFile(depsCfg.Fs.Source,
filepath.Join("layouts", "shortcodes", "shortcode.html"),
[]byte("Shortcode: {{ i18n \"hello\" }}"),
0755); err != nil {
@@ -1047,7 +1060,7 @@ func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, conf
}
// Add some language files
- if err := afero.WriteFile(hugofs.Source(),
+ if err := afero.WriteFile(depsCfg.Fs.Source,
filepath.Join("i18n", "en.yaml"),
[]byte(`
- id: hello
@@ -1056,7 +1069,7 @@ func createMultiTestSitesForConfig(t *testing.T, siteConfig testSiteConfig, conf
0755); err != nil {
t.Fatalf("Failed to write language file: %s", err)
}
- if err := afero.WriteFile(hugofs.Source(),
+ if err := afero.WriteFile(depsCfg.Fs.Source,
filepath.Join("i18n", "fr.yaml"),
[]byte(`
- id: hello
@@ -1210,7 +1223,10 @@ lag:
}
configFile := "multilangconfig." + configSuffix
- writeSource(t, configFile, configContent)
+ writeSource(t, depsCfg.Fs, configFile, configContent)
+
+ viper.SetFs(depsCfg.Fs.Source)
+
if err := LoadGlobalConfig("", configFile); err != nil {
t.Fatalf("Failed to load config: %s", err)
}
@@ -1218,15 +1234,15 @@ lag:
// Hugo support using ByteSource's directly (for testing),
// but to make it more real, we write them to the mem file system.
for _, s := range sources {
- if err := afero.WriteFile(hugofs.Source(), filepath.Join("content", s.Name), s.Content, 0755); err != nil {
+ if err := afero.WriteFile(depsCfg.Fs.Source, filepath.Join("content", s.Name), s.Content, 0755); err != nil {
t.Fatalf("Failed to write file: %s", err)
}
}
// Add some data
- writeSource(t, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
+ writeSource(t, depsCfg.Fs, "data/hugo.toml", "slogan = \"Hugo Rocks!\"")
- sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
+ sites, err := NewHugoSitesFromConfiguration(depsCfg)
if err != nil {
t.Fatalf("Failed to create sites: %s", err)
@@ -1239,26 +1255,26 @@ lag:
return sites
}
-func writeSource(t *testing.T, filename, content string) {
- if err := afero.WriteFile(hugofs.Source(), filepath.FromSlash(filename), []byte(content), 0755); err != nil {
+func writeSource(t *testing.T, fs *hugofs.Fs, filename, content string) {
+ if err := afero.WriteFile(fs.Source, filepath.FromSlash(filename), []byte(content), 0755); err != nil {
t.Fatalf("Failed to write file: %s", err)
}
}
-func readDestination(t *testing.T, filename string) string {
- return readFileFromFs(t, hugofs.Destination(), filename)
+func readDestination(t *testing.T, fs *hugofs.Fs, filename string) string {
+ return readFileFromFs(t, fs.Destination, filename)
}
-func destinationExists(filename string) bool {
- b, err := helpers.Exists(filename, hugofs.Destination())
+func destinationExists(fs *hugofs.Fs, filename string) bool {
+ b, err := helpers.Exists(filename, fs.Destination)
if err != nil {
panic(err)
}
return b
}
-func readSource(t *testing.T, filename string) string {
- return readFileFromFs(t, hugofs.Source(), filename)
+func readSource(t *testing.T, fs *hugofs.Fs, filename string) string {
+ return readFileFromFs(t, fs.Source, filename)
}
func readFileFromFs(t *testing.T, fs afero.Fs, filename string) string {
@@ -1291,9 +1307,9 @@ func newTestPage(title, date string, weight int) string {
return fmt.Sprintf(testPageTemplate, title, date, weight, title)
}
-func writeNewContentFile(t *testing.T, title, date, filename string, weight int) {
+func writeNewContentFile(t *testing.T, fs *hugofs.Fs, title, date, filename string, weight int) {
content := newTestPage(title, date, weight)
- writeSource(t, filename, content)
+ writeSource(t, fs, filename, content)
}
func createConfig(t *testing.T, config testSiteConfig, configTemplate string) string {
diff --git a/hugolib/i18n.go b/hugolib/i18n.go
index e71f9d3a3..d2e1a97df 100644
--- a/hugolib/i18n.go
+++ b/hugolib/i18n.go
@@ -19,11 +19,10 @@ import (
"github.com/nicksnyder/go-i18n/i18n/bundle"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/tpl"
- jww "github.com/spf13/jwalterweatherman"
)
-func loadI18n(sources []source.Input) error {
- jww.DEBUG.Printf("Load I18n from %q", sources)
+func (s *Site) loadI18n(sources []source.Input) error {
+ s.Log.DEBUG.Printf("Load I18n from %q", sources)
i18nBundle := bundle.New()
diff --git a/hugolib/menu_test.go b/hugolib/menu_test.go
index 43a7623c7..8fd94ec48 100644
--- a/hugolib/menu_test.go
+++ b/hugolib/menu_test.go
@@ -18,12 +18,14 @@ import (
"strings"
"testing"
+ "github.com/spf13/hugo/deps"
+
"github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/hugofs"
"path/filepath"
toml "github.com/pelletier/go-toml"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
@@ -677,31 +679,29 @@ func setupTestMenuState(t *testing.T) {
}
func setupMenuTests(t *testing.T, pageSources []source.ByteSource) *Site {
- s := createTestSite(pageSources)
setupTestMenuState(t)
- testSiteSetup(s, t)
- return s
+ fs := hugofs.NewMem()
+
+ for _, src := range pageSources {
+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
+
+ }
+
+ return buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+
}
func createTestSite(pageSources []source.ByteSource) *Site {
- hugofs.InitMemFs()
return &Site{
- deps: newDeps(DepsCfg{}),
Source: &source.InMemorySource{ByteSource: pageSources},
Language: helpers.NewDefaultLanguage(),
}
}
-func testSiteSetup(s *Site, t *testing.T) {
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Sites build failed: %s", err)
- }
-}
-
func tomlToMap(s string) (map[string]interface{}, error) {
tree, err := toml.Load(s)
diff --git a/hugolib/node_as_page_test.go b/hugolib/node_as_page_test.go
index d661fe885..35588da4d 100644
--- a/hugolib/node_as_page_test.go
+++ b/hugolib/node_as_page_test.go
@@ -18,8 +18,11 @@ import (
"path/filepath"
"strings"
"testing"
+
"time"
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
@@ -56,24 +59,28 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
viper.Set("uglyURLs", ugly)
viper.Set("preserveTaxonomyNames", preserveTaxonomyNames)
- writeLayoutsForNodeAsPageTests(t)
- writeNodePagesForNodeAsPageTests("", t)
-
- writeRegularPagesForNodeAsPageTests(t)
-
viper.Set("paginate", 1)
viper.Set("title", "Hugo Rocks")
viper.Set("rssURI", "customrss.xml")
- s := NewSiteDefaultLang()
+ depsCfg := newTestDepsConfig()
- if err := buildAndRenderSite(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ viper.SetFs(depsCfg.Fs.Source)
+
+ writeLayoutsForNodeAsPageTests(t, depsCfg.Fs)
+ writeNodePagesForNodeAsPageTests(t, depsCfg.Fs, "")
+
+ writeRegularPagesForNodeAsPageTests(t, depsCfg.Fs)
+
+ sites, err := NewHugoSitesFromConfiguration(depsCfg)
+
+ require.NoError(t, err)
+
+ require.NoError(t, sites.Build(BuildCfg{}))
// date order: home, sect1, sect2, cat/hugo, cat/web, categories
- assertFileContent(t, filepath.Join("public", "index.html"), false,
+ assertFileContent(t, depsCfg.Fs, filepath.Join("public", "index.html"), false,
"Index Title: Home Sweet Home!",
"Home <strong>Content!</strong>",
"# Pages: 4",
@@ -82,10 +89,9 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
"GetPage: Section1 ",
)
- assertFileContent(t, expectedFilePath(ugly, "public", "sect1", "regular1"), false, "Single Title: Page 01", "Content Page 01")
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "sect1", "regular1"), false, "Single Title: Page 01", "Content Page 01")
- h := s.owner
- nodes := h.findAllPagesByKindNotIn(KindPage)
+ nodes := sites.findAllPagesByKindNotIn(KindPage)
require.Len(t, nodes, 7)
@@ -99,7 +105,7 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
section2 := nodes[4]
require.Equal(t, "Section2", section2.Title)
- pages := h.findAllPagesByKind(KindPage)
+ pages := sites.findAllPagesByKind(KindPage)
require.Len(t, pages, 4)
first := pages[0]
@@ -109,46 +115,48 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
require.True(t, first.IsPage())
// Check Home paginator
- assertFileContent(t, expectedFilePath(ugly, "public", "page", "2"), false,
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "page", "2"), false,
"Pag: Page 02")
// Check Sections
- assertFileContent(t, expectedFilePath(ugly, "public", "sect1"), false,
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "sect1"), false,
"Section Title: Section", "Section1 <strong>Content!</strong>",
"Date: 2009-01-04",
"Lastmod: 2009-01-05",
)
- assertFileContent(t, expectedFilePath(ugly, "public", "sect2"), false,
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "sect2"), false,
"Section Title: Section", "Section2 <strong>Content!</strong>",
"Date: 2009-01-06",
"Lastmod: 2009-01-07",
)
// Check Sections paginator
- assertFileContent(t, expectedFilePath(ugly, "public", "sect1", "page", "2"), false,
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "sect1", "page", "2"), false,
"Pag: Page 02")
- sections := h.findAllPagesByKind(KindSection)
+ sections := sites.findAllPagesByKind(KindSection)
require.Len(t, sections, 2)
// Check taxonomy lists
- assertFileContent(t, expectedFilePath(ugly, "public", "categories", "hugo"), false,
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories", "hugo"), false,
"Taxonomy Title: Taxonomy Hugo", "Taxonomy Hugo <strong>Content!</strong>",
"Date: 2009-01-08",
"Lastmod: 2009-01-09",
)
- assertFileContent(t, expectedFilePath(ugly, "public", "categories", "hugo-rocks"), false,
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories", "hugo-rocks"), false,
"Taxonomy Title: Taxonomy Hugo Rocks",
)
+ s := sites.Sites[0]
+
web := s.getPage(KindTaxonomy, "categories", "web")
require.NotNil(t, web)
require.Len(t, web.Data["Pages"].(Pages), 4)
- assertFileContent(t, expectedFilePath(ugly, "public", "categories", "web"), false,
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories", "web"), false,
"Taxonomy Title: Taxonomy Web",
"Taxonomy Web <strong>Content!</strong>",
"Date: 2009-01-10",
@@ -156,12 +164,12 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
)
// Check taxonomy list paginator
- assertFileContent(t, expectedFilePath(ugly, "public", "categories", "hugo", "page", "2"), false,
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories", "hugo", "page", "2"), false,
"Taxonomy Title: Taxonomy Hugo",
"Pag: Page 02")
// Check taxonomy terms
- assertFileContent(t, expectedFilePath(ugly, "public", "categories"), false,
+ assertFileContent(t, depsCfg.Fs, expectedFilePath(ugly, "public", "categories"), false,
"Taxonomy Terms Title: Taxonomy Term Categories", "Taxonomy Term Categories <strong>Content!</strong>", "k/v: hugo",
"Date: 2009-01-14",
"Lastmod: 2009-01-15",
@@ -170,11 +178,11 @@ func doTestNodeAsPage(t *testing.T, ugly, preserveTaxonomyNames bool) {
// There are no pages to paginate over in the taxonomy terms.
// RSS
- assertFileContent(t, filepath.Join("public", "customrss.xml"), false, "Recent content in Home Sweet Home! on Hugo Rocks", "<rss")
- assertFileContent(t, filepath.Join("public", "sect1", "customrss.xml"), false, "Recent content in Section1 on Hugo Rocks", "<rss")
- assertFileContent(t, filepath.Join("public", "sect2", "customrss.xml"), false, "Recent content in Section2 on Hugo Rocks", "<rss")
- assertFileContent(t, filepath.Join("public", "categories", "hugo", "customrss.xml"), false, "Recent content in Taxonomy Hugo on Hugo Rocks", "<rss")
- assertFileContent(t, filepath.Join("public", "categories", "web", "customrss.xml"), false, "Recent content in Taxonomy Web on Hugo Rocks", "<rss")
+ assertFileContent(t, depsCfg.Fs, filepath.Join("public", "customrss.xml"), false, "Recent content in Home Sweet Home! on Hugo Rocks", "<rss")
+ assertFileContent(t, depsCfg.Fs, filepath.Join("public", "sect1", "customrss.xml"), false, "Recent content in Section1 on Hugo Rocks", "<rss")
+ assertFileContent(t, depsCfg.Fs, filepath.Join("public", "sect2", "customrss.xml"), false, "Recent content in Section2 on Hugo Rocks", "<rss")
+ assertFileContent(t, depsCfg.Fs, filepath.Join("public", "categories", "hugo", "customrss.xml"), false, "Recent content in Taxonomy Hugo on Hugo Rocks", "<rss")
+ assertFileContent(t, depsCfg.Fs, filepath.Join("public", "categories", "web", "customrss.xml"), false, "Recent content in Taxonomy Web on Hugo Rocks", "<rss")
}
@@ -187,19 +195,23 @@ func TestNodesWithNoContentFile(t *testing.T) {
func doTestNodesWithNoContentFile(t *testing.T, ugly bool) {
testCommonResetState()
- writeLayoutsForNodeAsPageTests(t)
- writeRegularPagesForNodeAsPageTests(t)
-
viper.Set("uglyURLs", ugly)
viper.Set("paginate", 1)
viper.Set("title", "Hugo Rocks!")
viper.Set("rssURI", "customrss.xml")
- s := NewSiteDefaultLang()
+ fs := hugofs.NewMem()
- if err := buildAndRenderSite(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ writeLayoutsForNodeAsPageTests(t, fs)
+ writeRegularPagesForNodeAsPageTests(t, fs)
+
+ sites, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
+
+ require.NoError(t, err)
+
+ require.NoError(t, sites.Build(BuildCfg{}))
+
+ s := sites.Sites[0]
// Home page
homePages := s.findPagesByKind(KindHome)
@@ -210,21 +222,21 @@ func doTestNodesWithNoContentFile(t *testing.T, ugly bool) {
require.Len(t, homePage.Pages, 4)
require.True(t, homePage.Path() == "")
- assertFileContent(t, filepath.Join("public", "index.html"), false,
+ assertFileContent(t, fs, filepath.Join("public", "index.html"), false,
"Index Title: Hugo Rocks!",
"Date: 2010-06-12",
"Lastmod: 2010-06-13",
)
// Taxonomy list
- assertFileContent(t, expectedFilePath(ugly, "public", "categories", "hugo"), false,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories", "hugo"), false,
"Taxonomy Title: Hugo",
"Date: 2010-06-12",
"Lastmod: 2010-06-13",
)
// Taxonomy terms
- assertFileContent(t, expectedFilePath(ugly, "public", "categories"), false,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "categories"), false,
"Taxonomy Terms Title: Categories",
)
@@ -232,9 +244,9 @@ func doTestNodesWithNoContentFile(t *testing.T, ugly bool) {
for _, p := range pages {
var want string
if ugly {
- want = "/" + p.Site.pathSpec.URLize(p.Title) + ".html"
+ want = "/" + p.s.PathSpec.URLize(p.Title) + ".html"
} else {
- want = "/" + p.Site.pathSpec.URLize(p.Title) + "/"
+ want = "/" + p.s.PathSpec.URLize(p.Title) + "/"
}
if p.URL() != want {
t.Errorf("Taxonomy term URL mismatch: want %q, got %q", want, p.URL())
@@ -242,29 +254,29 @@ func doTestNodesWithNoContentFile(t *testing.T, ugly bool) {
}
// Sections
- assertFileContent(t, expectedFilePath(ugly, "public", "sect1"), false,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect1"), false,
"Section Title: Sect1s",
"Date: 2010-06-12",
"Lastmod: 2010-06-13",
)
- assertFileContent(t, expectedFilePath(ugly, "public", "sect2"), false,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "sect2"), false,
"Section Title: Sect2s",
"Date: 2008-07-06",
"Lastmod: 2008-07-09",
)
// RSS
- assertFileContent(t, filepath.Join("public", "customrss.xml"), false, "Hugo Rocks!", "<rss")
- assertFileContent(t, filepath.Join("public", "sect1", "customrss.xml"), false, "Recent content in Sect1s on Hugo Rocks!", "<rss")
- assertFileContent(t, filepath.Join("public", "sect2", "customrss.xml"), false, "Recent content in Sect2s on Hugo Rocks!", "<rss")
- assertFileContent(t, filepath.Join("public", "categories", "hugo", "customrss.xml"), false, "Recent content in Hugo on Hugo Rocks!", "<rss")
- assertFileContent(t, filepath.Join("public", "categories", "web", "customrss.xml"), false, "Recent content in Web on Hugo Rocks!", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "customrss.xml"), false, "Hugo Rocks!", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "sect1", "customrss.xml"), false, "Recent content in Sect1s on Hugo Rocks!", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "sect2", "customrss.xml"), false, "Recent content in Sect2s on Hugo Rocks!", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "customrss.xml"), false, "Recent content in Hugo on Hugo Rocks!", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "categories", "web", "customrss.xml"), false, "Recent content in Web on Hugo Rocks!", "<rss")
}
func TestNodesAsPageMultilingual(t *testing.T) {
- for _, ugly := range []bool{true, false} {
+ for _, ugly := range []bool{false, true} {
doTestNodesAsPageMultilingual(t, ugly)
}
}
@@ -273,11 +285,13 @@ func doTestNodesAsPageMultilingual(t *testing.T, ugly bool) {
testCommonResetState()
+ fs := hugofs.NewMem()
+
viper.Set("uglyURLs", ugly)
- writeLayoutsForNodeAsPageTests(t)
+ viper.SetFs(fs.Source)
- writeSource(t, "config.toml",
+ writeSource(t, fs, "config.toml",
`
paginage = 1
title = "Hugo Multilingual Rocks!"
@@ -303,19 +317,17 @@ weight = 3
title = "Deutsche Hugo"
`)
+ writeLayoutsForNodeAsPageTests(t, fs)
+
for _, lang := range []string{"nn", "en"} {
- writeRegularPagesForNodeAsPageTestsWithLang(t, lang)
+ writeRegularPagesForNodeAsPageTestsWithLang(t, fs, lang)
}
- // Only write node pages for the English and Deutsch
- writeNodePagesForNodeAsPageTests("en", t)
- writeNodePagesForNodeAsPageTests("de", t)
-
if err := LoadGlobalConfig("", "config.toml"); err != nil {
t.Fatalf("Failed to load config: %s", err)
}
- sites, err := NewHugoSitesFromConfiguration(DepsCfg{})
+ sites, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
if err != nil {
t.Fatalf("Failed to create sites: %s", err)
@@ -325,6 +337,10 @@ title = "Deutsche Hugo"
t.Fatalf("Got %d sites", len(sites.Sites))
}
+ // Only write node pages for the English and Deutsch
+ writeNodePagesForNodeAsPageTests(t, fs, "en")
+ writeNodePagesForNodeAsPageTests(t, fs, "de")
+
err = sites.Build(BuildCfg{})
if err != nil {
@@ -356,92 +372,99 @@ title = "Deutsche Hugo"
require.Equal(t, expetedPermalink(ugly, "/en/sect1/"), enSect.Permalink())
- assertFileContent(t, filepath.Join("public", "nn", "index.html"), true,
+ assertFileContent(t, fs, filepath.Join("public", "nn", "index.html"), true,
"Index Title: Hugo på norsk")
- assertFileContent(t, filepath.Join("public", "en", "index.html"), true,
+ assertFileContent(t, fs, filepath.Join("public", "en", "index.html"), true,
"Index Title: Home Sweet Home!", "<strong>Content!</strong>")
- assertFileContent(t, filepath.Join("public", "de", "index.html"), true,
+ assertFileContent(t, fs, filepath.Join("public", "de", "index.html"), true,
"Index Title: Home Sweet Home!", "<strong>Content!</strong>")
// Taxonomy list
- assertFileContent(t, expectedFilePath(ugly, "public", "nn", "categories", "hugo"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "categories", "hugo"), true,
"Taxonomy Title: Hugo")
- assertFileContent(t, expectedFilePath(ugly, "public", "en", "categories", "hugo"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "categories", "hugo"), true,
"Taxonomy Title: Taxonomy Hugo")
// Taxonomy terms
- assertFileContent(t, expectedFilePath(ugly, "public", "nn", "categories"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "categories"), true,
"Taxonomy Terms Title: Categories")
- assertFileContent(t, expectedFilePath(ugly, "public", "en", "categories"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "categories"), true,
"Taxonomy Terms Title: Taxonomy Term Categories")
// Sections
- assertFileContent(t, expectedFilePath(ugly, "public", "nn", "sect1"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "sect1"), true,
"Section Title: Sect1s")
- assertFileContent(t, expectedFilePath(ugly, "public", "nn", "sect2"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "sect2"), true,
"Section Title: Sect2s")
- assertFileContent(t, expectedFilePath(ugly, "public", "en", "sect1"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "sect1"), true,
"Section Title: Section1")
- assertFileContent(t, expectedFilePath(ugly, "public", "en", "sect2"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "sect2"), true,
"Section Title: Section2")
// Regular pages
- assertFileContent(t, expectedFilePath(ugly, "public", "en", "sect1", "regular1"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "en", "sect1", "regular1"), true,
"Single Title: Page 01")
- assertFileContent(t, expectedFilePath(ugly, "public", "nn", "sect1", "regular2"), true,
+ assertFileContent(t, fs, expectedFilePath(ugly, "public", "nn", "sect1", "regular2"), true,
"Single Title: Page 02")
// RSS
- assertFileContent(t, filepath.Join("public", "nn", "customrss.xml"), true, "Hugo på norsk", "<rss")
- assertFileContent(t, filepath.Join("public", "nn", "sect1", "customrss.xml"), true, "Recent content in Sect1s on Hugo på norsk", "<rss")
- assertFileContent(t, filepath.Join("public", "nn", "sect2", "customrss.xml"), true, "Recent content in Sect2s on Hugo på norsk", "<rss")
- assertFileContent(t, filepath.Join("public", "nn", "categories", "hugo", "customrss.xml"), true, "Recent content in Hugo on Hugo på norsk", "<rss")
- assertFileContent(t, filepath.Join("public", "nn", "categories", "web", "customrss.xml"), true, "Recent content in Web on Hugo på norsk", "<rss")
-
- assertFileContent(t, filepath.Join("public", "en", "customrss.xml"), true, "Recent content in Home Sweet Home! on Hugo in English", "<rss")
- assertFileContent(t, filepath.Join("public", "en", "sect1", "customrss.xml"), true, "Recent content in Section1 on Hugo in English", "<rss")
- assertFileContent(t, filepath.Join("public", "en", "sect2", "customrss.xml"), true, "Recent content in Section2 on Hugo in English", "<rss")
- assertFileContent(t, filepath.Join("public", "en", "categories", "hugo", "customrss.xml"), true, "Recent content in Taxonomy Hugo on Hugo in English", "<rss")
- assertFileContent(t, filepath.Join("public", "en", "categories", "web", "customrss.xml"), true, "Recent content in Taxonomy Web on Hugo in English", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "nn", "customrss.xml"), true, "Hugo på norsk", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "nn", "sect1", "customrss.xml"), true, "Recent content in Sect1s on Hugo på norsk", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "nn", "sect2", "customrss.xml"), true, "Recent content in Sect2s on Hugo på norsk", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "nn", "categories", "hugo", "customrss.xml"), true, "Recent content in Hugo on Hugo på norsk", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "nn", "categories", "web", "customrss.xml"), true, "Recent content in Web on Hugo på norsk", "<rss")
+
+ assertFileContent(t, fs, filepath.Join("public", "en", "customrss.xml"), true, "Recent content in Home Sweet Home! on Hugo in English", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "en", "sect1", "customrss.xml"), true, "Recent content in Section1 on Hugo in English", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "en", "sect2", "customrss.xml"), true, "Recent content in Section2 on Hugo in English", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "en", "categories", "hugo", "customrss.xml"), true, "Recent content in Taxonomy Hugo on Hugo in English", "<rss")
+ assertFileContent(t, fs, filepath.Join("public", "en", "categories", "web", "customrss.xml"), true, "Recent content in Taxonomy Web on Hugo in English", "<rss")
}
func TestNodesWithTaxonomies(t *testing.T) {
testCommonResetState()
- writeLayoutsForNodeAsPageTests(t)
- writeRegularPagesForNodeAsPageTests(t)
+ fs := hugofs.NewMem()
+
+ viper.Set("paginate", 1)
+ viper.Set("title", "Hugo Rocks!")
- writeSource(t, filepath.Join("content", "_index.md"), `---
+ writeLayoutsForNodeAsPageTests(t, fs)
+ writeRegularPagesForNodeAsPageTests(t, fs)
+
+ writeSource(t, fs, filepath.Join("content", "_index.md"), `---
title: Home With Taxonomies
categories: [
- "Hugo",
+ "Hugo",
"Home"
]
---
`)
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks!")
+ h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
- s := NewSiteDefaultLang()
+ require.NoError(t, err)
- if err := buildAndRenderSite(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ require.NoError(t, h.Build(BuildCfg{}))
- assertFileContent(t, filepath.Join("public", "categories", "hugo", "index.html"), true, "Taxonomy Title: Hugo", "# Pages: 5")
- assertFileContent(t, filepath.Join("public", "categories", "home", "index.html"), true, "Taxonomy Title: Home", "# Pages: 1")
+ assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "index.html"), true, "Taxonomy Title: Hugo", "# Pages: 5")
+ assertFileContent(t, fs, filepath.Join("public", "categories", "home", "index.html"), true, "Taxonomy Title: Home", "# Pages: 1")
}
func TestNodesWithMenu(t *testing.T) {
testCommonResetState()
- writeLayoutsForNodeAsPageTests(t)
- writeRegularPagesForNodeAsPageTests(t)
+ viper.Set("paginate", 1)
+ viper.Set("title", "Hugo Rocks!")
+
+ fs := hugofs.NewMem()
- writeSource(t, filepath.Join("content", "_index.md"), `---
+ writeLayoutsForNodeAsPageTests(t, fs)
+ writeRegularPagesForNodeAsPageTests(t, fs)
+
+ writeSource(t, fs, filepath.Join("content", "_index.md"), `---
title: Home With Menu
menu:
mymenu:
@@ -449,7 +472,7 @@ menu:
---
`)
- writeSource(t, filepath.Join("content", "sect1", "_index.md"), `---
+ writeSource(t, fs, filepath.Join("content", "sect1", "_index.md"), `---
title: Sect1 With Menu
menu:
mymenu:
@@ -457,7 +480,7 @@ menu:
---
`)
- writeSource(t, filepath.Join("content", "categories", "hugo", "_index.md"), `---
+ writeSource(t, fs, filepath.Join("content", "categories", "hugo", "_index.md"), `---
title: Taxonomy With Menu
menu:
mymenu:
@@ -465,98 +488,102 @@ menu:
---
`)
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks!")
+ h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
- s := NewSiteDefaultLang()
+ require.NoError(t, err)
- if err := buildAndRenderSite(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ require.NoError(t, h.Build(BuildCfg{}))
- assertFileContent(t, filepath.Join("public", "index.html"), true, "Home With Menu", "Home Menu Item: Go Home!: /")
- assertFileContent(t, filepath.Join("public", "sect1", "index.html"), true, "Sect1 With Menu", "Section Menu Item: Go Sect1!: /sect1/")
- assertFileContent(t, filepath.Join("public", "categories", "hugo", "index.html"), true, "Taxonomy With Menu", "Taxonomy Menu Item: Go Tax Hugo!: /categories/hugo/")
+ assertFileContent(t, fs, filepath.Join("public", "index.html"), true, "Home With Menu", "Home Menu Item: Go Home!: /")
+ assertFileContent(t, fs, filepath.Join("public", "sect1", "index.html"), true, "Sect1 With Menu", "Section Menu Item: Go Sect1!: /sect1/")
+ assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", "index.html"), true, "Taxonomy With Menu", "Taxonomy Menu Item: Go Tax Hugo!: /categories/hugo/")
}
func TestNodesWithAlias(t *testing.T) {
testCommonResetState()
- writeLayoutsForNodeAsPageTests(t)
- writeRegularPagesForNodeAsPageTests(t)
+ fs := hugofs.NewMem()
+
+ viper.Set("paginate", 1)
+ viper.Set("baseURL", "http://base/")
+ viper.Set("title", "Hugo Rocks!")
+
+ writeLayoutsForNodeAsPageTests(t, fs)
+ writeRegularPagesForNodeAsPageTests(t, fs)
- writeSource(t, filepath.Join("content", "_index.md"), `---
+ writeSource(t, fs, filepath.Join("content", "_index.md"), `---
title: Home With Alias
aliases:
- /my/new/home.html
---
`)
- viper.Set("paginate", 1)
- viper.Set("baseURL", "http://base/")
- viper.Set("title", "Hugo Rocks!")
+ h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
- s := NewSiteDefaultLang()
+ require.NoError(t, err)
- if err := buildAndRenderSite(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ require.NoError(t, h.Build(BuildCfg{}))
- assertFileContent(t, filepath.Join("public", "index.html"), true, "Home With Alias")
- assertFileContent(t, filepath.Join("public", "my", "new", "home.html"), true, "content=\"0; url=http://base/")
+ assertFileContent(t, fs, filepath.Join("public", "index.html"), true, "Home With Alias")
+ assertFileContent(t, fs, filepath.Join("public", "my", "new", "home.html"), true, "content=\"0; url=http://base/")
}
func TestNodesWithSectionWithIndexPageOnly(t *testing.T) {
testCommonResetState()
- writeLayoutsForNodeAsPageTests(t)
+ fs := hugofs.NewMem()
- writeSource(t, filepath.Join("content", "sect", "_index.md"), `---
+ viper.Set("paginate", 1)
+ viper.Set("title", "Hugo Rocks!")
+
+ writeLayoutsForNodeAsPageTests(t, fs)
+
+ writeSource(t, fs, filepath.Join("content", "sect", "_index.md"), `---
title: MySection
---
My Section Content
`)
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks!")
+ h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
- s := NewSiteDefaultLang()
+ require.NoError(t, err)
- if err := buildAndRenderSite(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ require.NoError(t, h.Build(BuildCfg{}))
- assertFileContent(t, filepath.Join("public", "sect", "index.html"), true, "My Section")
+ assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), true, "My Section")
}
func TestNodesWithURLs(t *testing.T) {
testCommonResetState()
- writeLayoutsForNodeAsPageTests(t)
+ fs := hugofs.NewMem()
- writeRegularPagesForNodeAsPageTests(t)
+ viper.Set("paginate", 1)
+ viper.Set("title", "Hugo Rocks!")
+ viper.Set("baseURL", "http://bep.is/base/")
+
+ writeLayoutsForNodeAsPageTests(t, fs)
+ writeRegularPagesForNodeAsPageTests(t, fs)
- writeSource(t, filepath.Join("content", "sect", "_index.md"), `---
+ writeSource(t, fs, filepath.Join("content", "sect", "_index.md"), `---
title: MySection
url: foo.html
---
My Section Content
`)
- viper.Set("paginate", 1)
- viper.Set("title", "Hugo Rocks!")
- viper.Set("baseURL", "http://bep.is/base/")
+ h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs})
- s := NewSiteDefaultLang()
+ require.NoError(t, err)
- if err := buildAndRenderSite(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ require.NoError(t, h.Build(BuildCfg{}))
+
+ assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), true, "My Section")
- assertFileContent(t, filepath.Join("public", "sect", "index.html"), true, "My Section")
+ s := h.Sites[0]
p := s.RegularPages[0]
@@ -573,11 +600,11 @@ My Section Content
}
-func writeRegularPagesForNodeAsPageTests(t *testing.T) {
- writeRegularPagesForNodeAsPageTestsWithLang(t, "")
+func writeRegularPagesForNodeAsPageTests(t *testing.T, fs *hugofs.Fs) {
+ writeRegularPagesForNodeAsPageTestsWithLang(t, fs, "")
}
-func writeRegularPagesForNodeAsPageTestsWithLang(t *testing.T, lang string) {
+func writeRegularPagesForNodeAsPageTestsWithLang(t *testing.T, fs *hugofs.Fs, lang string) {
var langStr string
if lang != "" {
@@ -597,7 +624,7 @@ func writeRegularPagesForNodeAsPageTestsWithLang(t *testing.T, lang string) {
}
date = date.Add(-24 * time.Duration(i) * time.Hour)
- writeSource(t, filepath.Join("content", sect, fmt.Sprintf("regular%d.%smd", i, langStr)), fmt.Sprintf(`---
+ writeSource(t, fs, filepath.Join("content", sect, fmt.Sprintf("regular%d.%smd", i, langStr)), fmt.Sprintf(`---
title: Page %02d
lastMod : %q
date : %q
@@ -612,7 +639,7 @@ Content Page %02d
}
}
-func writeNodePagesForNodeAsPageTests(lang string, t *testing.T) {
+func writeNodePagesForNodeAsPageTests(t *testing.T, fs *hugofs.Fs, lang string) {
filename := "_index.md"
@@ -624,7 +651,7 @@ func writeNodePagesForNodeAsPageTests(lang string, t *testing.T) {
date, _ := time.Parse(format, "2009-01-01")
- writeSource(t, filepath.Join("content", filename), fmt.Sprintf(`---
+ writeSource(t, fs, filepath.Join("content", filename), fmt.Sprintf(`---
title: Home Sweet Home!
date : %q
lastMod : %q
@@ -632,14 +659,14 @@ lastMod : %q
l-%s Home **Content!**
`, date.Add(1*24*time.Hour).Format(time.RFC822), date.Add(2*24*time.Hour).Format(time.RFC822), lang))
- writeSource(t, filepath.Join("content", "sect1", filename), fmt.Sprintf(`---
+ writeSource(t, fs, filepath.Join("content", "sect1", filename), fmt.Sprintf(`---
title: Section1
date : %q
lastMod : %q
---
Section1 **Content!**
`, date.Add(3*24*time.Hour).Format(time.RFC822), date.Add(4*24*time.Hour).Format(time.RFC822)))
- writeSource(t, filepath.Join("content", "sect2", filename), fmt.Sprintf(`---
+ writeSource(t, fs, filepath.Join("content", "sect2", filename), fmt.Sprintf(`---
title: Section2
date : %q
lastMod : %q
@@ -647,7 +674,7 @@ lastMod : %q
Section2 **Content!**
`, date.Add(5*24*time.Hour).Format(time.RFC822), date.Add(6*24*time.Hour).Format(time.RFC822)))
- writeSource(t, filepath.Join("content", "categories", "hugo", filename), fmt.Sprintf(`---
+ writeSource(t, fs, filepath.Join("content", "categories", "hugo", filename), fmt.Sprintf(`---
title: Taxonomy Hugo
date : %q
lastMod : %q
@@ -655,7 +682,7 @@ lastMod : %q
Taxonomy Hugo **Content!**
`, date.Add(7*24*time.Hour).Format(time.RFC822), date.Add(8*24*time.Hour).Format(time.RFC822)))
- writeSource(t, filepath.Join("content", "categories", "web", filename), fmt.Sprintf(`---
+ writeSource(t, fs, filepath.Join("content", "categories", "web", filename), fmt.Sprintf(`---
title: Taxonomy Web
date : %q
lastMod : %q
@@ -663,7 +690,7 @@ lastMod : %q
Taxonomy Web **Content!**
`, date.Add(9*24*time.Hour).Format(time.RFC822), date.Add(10*24*time.Hour).Format(time.RFC822)))
- writeSource(t, filepath.Join("content", "categories", "hugo-rocks", filename), fmt.Sprintf(`---
+ writeSource(t, fs, filepath.Join("content", "categories", "hugo-rocks", filename), fmt.Sprintf(`---
title: Taxonomy Hugo Rocks
date : %q
lastMod : %q
@@ -671,7 +698,7 @@ lastMod : %q
Taxonomy Hugo Rocks **Content!**
`, date.Add(11*24*time.Hour).Format(time.RFC822), date.Add(12*24*time.Hour).Format(time.RFC822)))
- writeSource(t, filepath.Join("content", "categories", filename), fmt.Sprintf(`---
+ writeSource(t, fs, filepath.Join("content", "categories", filename), fmt.Sprintf(`---
title: Taxonomy Term Categories
date : %q
lastMod : %q
@@ -681,8 +708,8 @@ Taxonomy Term Categories **Content!**
}
-func writeLayoutsForNodeAsPageTests(t *testing.T) {
- writeSource(t, filepath.Join("layouts", "index.html"), `
+func writeLayoutsForNodeAsPageTests(t *testing.T, fs *hugofs.Fs) {
+ writeSource(t, fs, filepath.Join("layouts", "index.html"), `
Index Title: {{ .Title }}
Index Content: {{ .Content }}
# Pages: {{ len .Data.Pages }}
@@ -699,14 +726,14 @@ Lastmod: {{ .Lastmod.Format "2006-01-02" }}
GetPage: {{ with .Site.GetPage "section" "sect1" }}{{ .Title }}{{ end }}
`)
- writeSource(t, filepath.Join("layouts", "_default", "single.html"), `
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `
Single Title: {{ .Title }}
Single Content: {{ .Content }}
Date: {{ .Date.Format "2006-01-02" }}
Lastmod: {{ .Lastmod.Format "2006-01-02" }}
`)
- writeSource(t, filepath.Join("layouts", "_default", "section.html"), `
+ writeSource(t, fs, filepath.Join("layouts", "_default", "section.html"), `
Section Title: {{ .Title }}
Section Content: {{ .Content }}
# Pages: {{ len .Data.Pages }}
@@ -723,7 +750,7 @@ Lastmod: {{ .Lastmod.Format "2006-01-02" }}
`)
// Taxonomy lists
- writeSource(t, filepath.Join("layouts", "_default", "taxonomy.html"), `
+ writeSource(t, fs, filepath.Join("layouts", "_default", "taxonomy.html"), `
Taxonomy Title: {{ .Title }}
Taxonomy Content: {{ .Content }}
# Pages: {{ len .Data.Pages }}
@@ -740,7 +767,7 @@ Lastmod: {{ .Lastmod.Format "2006-01-02" }}
`)
// Taxonomy terms
- writeSource(t, filepath.Join("layouts", "_default", "terms.html"), `
+ writeSource(t, fs, filepath.Join("layouts", "_default", "terms.html"), `
Taxonomy Terms Title: {{ .Title }}
Taxonomy Terms Content: {{ .Content }}
{{ range $key, $value := .Data.Terms }}
diff --git a/hugolib/page.go b/hugolib/page.go
index 0f6973297..84d017126 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -38,7 +38,6 @@ import (
"github.com/spf13/cast"
bp "github.com/spf13/hugo/bufferpool"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/viper"
)
@@ -536,7 +535,7 @@ func (p *Page) getRenderingConfig() *helpers.Blackfriday {
p.renderingConfig = helpers.NewBlackfriday(p.Language())
if err := mapstructure.Decode(pageParam, p.renderingConfig); err != nil {
- p.s.log.FATAL.Printf("Failed to get rendering config for %s:\n%s", p.BaseFileName(), err.Error())
+ p.s.Log.FATAL.Printf("Failed to get rendering config for %s:\n%s", p.BaseFileName(), err.Error())
}
})
@@ -556,7 +555,7 @@ func (s *Site) newPage(filename string) *Page {
sections: sectionsFromFilename(filename),
}
- s.log.DEBUG.Println("Reading from", page.File.Path())
+ s.Log.DEBUG.Println("Reading from", page.File.Path())
return &page
}
@@ -683,7 +682,7 @@ func (s *Site) NewPage(name string) (*Page, error) {
func (p *Page) ReadFrom(buf io.Reader) (int64, error) {
// Parse for metadata & body
if err := p.parse(buf); err != nil {
- p.s.log.ERROR.Print(err)
+ p.s.Log.ERROR.Print(err)
return 0, err
}
@@ -738,7 +737,7 @@ func (p *Page) getPermalink() *url.URL {
p.pageURLInit.Do(func() {
u, err := p.createPermalink()
if err != nil {
- p.s.log.ERROR.Printf("Failed to create permalink for page %q: %s", p.FullFilePath(), err)
+ p.s.Log.ERROR.Printf("Failed to create permalink for page %q: %s", p.FullFilePath(), err)
p.permalink = new(url.URL)
return
}
@@ -759,16 +758,16 @@ func (p *Page) createPermalink() (*url.URL, error) {
if p.IsNode() {
// No permalink config for nodes (currently)
- pURL := strings.TrimSpace(p.Site.pathSpec.URLize(p.URLPath.URL))
+ pURL := strings.TrimSpace(p.s.PathSpec.URLize(p.URLPath.URL))
pURL = p.addLangPathPrefix(pURL)
- pURL = p.Site.pathSpec.URLPrep(pURL)
+ pURL = p.s.PathSpec.URLPrep(pURL)
url := helpers.MakePermalink(baseURL, pURL)
return url, nil
}
- dir := strings.TrimSpace(p.Site.pathSpec.MakePath(filepath.ToSlash(strings.ToLower(p.Source.Dir()))))
- pSlug := strings.TrimSpace(p.Site.pathSpec.URLize(p.Slug))
- pURL := strings.TrimSpace(p.Site.pathSpec.URLize(p.URLPath.URL))
+ dir := strings.TrimSpace(p.s.PathSpec.MakePath(filepath.ToSlash(strings.ToLower(p.Source.Dir()))))
+ pSlug := strings.TrimSpace(p.s.PathSpec.URLize(p.Slug))
+ pURL := strings.TrimSpace(p.s.PathSpec.URLize(p.URLPath.URL))
var permalink string
var err error
@@ -784,10 +783,10 @@ func (p *Page) createPermalink() (*url.URL, error) {
}
} else {
if len(pSlug) > 0 {
- permalink = p.Site.pathSpec.URLPrep(path.Join(dir, p.Slug+"."+p.Extension()))
+ permalink = p.s.PathSpec.URLPrep(path.Join(dir, p.Slug+"."+p.Extension()))
} else {
t := p.Source.TranslationBaseName()
- permalink = p.Site.pathSpec.URLPrep(path.Join(dir, (strings.TrimSpace(t) + "." + p.Extension())))
+ permalink = p.s.PathSpec.URLPrep(path.Join(dir, (strings.TrimSpace(t) + "." + p.Extension())))
}
}
@@ -953,22 +952,22 @@ func (p *Page) update(f interface{}) error {
case "date":
p.Date, err = cast.ToTimeE(v)
if err != nil {
- p.s.log.ERROR.Printf("Failed to parse date '%v' in page %s", v, p.File.Path())
+ p.s.Log.ERROR.Printf("Failed to parse date '%v' in page %s", v, p.File.Path())
}
case "lastmod":
p.Lastmod, err = cast.ToTimeE(v)
if err != nil {
- p.s.log.ERROR.Printf("Failed to parse lastmod '%v' in page %s", v, p.File.Path())
+ p.s.Log.ERROR.Printf("Failed to parse lastmod '%v' in page %s", v, p.File.Path())
}
case "publishdate", "pubdate":
p.PublishDate, err = cast.ToTimeE(v)
if err != nil {
- p.s.log.ERROR.Printf("Failed to parse publishdate '%v' in page %s", v, p.File.Path())
+ p.s.Log.ERROR.Printf("Failed to parse publishdate '%v' in page %s", v, p.File.Path())
}
case "expirydate", "unpublishdate":
p.ExpiryDate, err = cast.ToTimeE(v)
if err != nil {
- p.s.log.ERROR.Printf("Failed to parse expirydate '%v' in page %s", v, p.File.Path())
+ p.s.Log.ERROR.Printf("Failed to parse expirydate '%v' in page %s", v, p.File.Path())
}
case "draft":
draft = new(bool)
@@ -1040,7 +1039,7 @@ func (p *Page) update(f interface{}) error {
if draft != nil && published != nil {
p.Draft = *draft
- p.s.log.ERROR.Printf("page %s has both draft and published settings in its frontmatter. Using draft.", p.File.Path())
+ p.s.Log.ERROR.Printf("page %s has both draft and published settings in its frontmatter. Using draft.", p.File.Path())
return ErrHasDraftAndPublished
} else if draft != nil {
p.Draft = *draft
@@ -1049,7 +1048,7 @@ func (p *Page) update(f interface{}) error {
}
if p.Date.IsZero() && viper.GetBool("useModTimeAsFallback") {
- fi, err := hugofs.Source().Stat(filepath.Join(helpers.AbsPathify(viper.GetString("contentDir")), p.File.Path()))
+ fi, err := p.s.Fs.Source.Stat(filepath.Join(helpers.AbsPathify(viper.GetString("contentDir")), p.File.Path()))
if err == nil {
p.Date = fi.ModTime()
}
@@ -1109,7 +1108,7 @@ func (p *Page) getParam(key string, stringToLower bool) interface{} {
return v
}
- p.s.log.ERROR.Printf("GetParam(\"%s\"): Unknown type %s\n", key, reflect.TypeOf(v))
+ p.s.Log.ERROR.Printf("GetParam(\"%s\"): Unknown type %s\n", key, reflect.TypeOf(v))
return nil
}
@@ -1251,16 +1250,16 @@ func (p *Page) Menus() PageMenus {
menus, err := cast.ToStringMapE(ms)
if err != nil {
- p.s.log.ERROR.Printf("unable to process menus for %q\n", p.Title)
+ p.s.Log.ERROR.Printf("unable to process menus for %q\n", p.Title)
}
for name, menu := range menus {
menuEntry := MenuEntry{Name: p.LinkTitle(), URL: link, Weight: p.Weight, Menu: name}
if menu != nil {
- p.s.log.DEBUG.Printf("found menu: %q, in %q\n", name, p.Title)
+ p.s.Log.DEBUG.Printf("found menu: %q, in %q\n", name, p.Title)
ime, err := cast.ToStringMapE(menu)
if err != nil {
- p.s.log.ERROR.Printf("unable to process menus for %q: %s", p.Title, err)
+ p.s.Log.ERROR.Printf("unable to process menus for %q: %s", p.Title, err)
}
menuEntry.marshallMap(ime)
@@ -1283,7 +1282,7 @@ func (p *Page) Render(layout ...string) template.HTML {
l = p.layouts()
}
- return p.s.tmpl.ExecuteTemplateToHTML(p, l...)
+ return p.s.Tmpl.ExecuteTemplateToHTML(p, l...)
}
func (p *Page) determineMarkupType() string {
@@ -1311,8 +1310,8 @@ func (p *Page) parse(reader io.Reader) error {
meta, err := psr.Metadata()
if meta != nil {
if err != nil {
- p.s.log.ERROR.Printf("Error parsing page meta data for %s", p.File.Path())
- p.s.log.ERROR.Println(err)
+ p.s.Log.ERROR.Printf("Error parsing page meta data for %s", p.File.Path())
+ p.s.Log.ERROR.Println(err)
return err
}
if err = p.update(meta); err != nil {
@@ -1381,12 +1380,12 @@ func (p *Page) saveSource(by []byte, inpath string, safe bool) (err error) {
if !filepath.IsAbs(inpath) {
inpath = helpers.AbsPathify(inpath)
}
- p.s.log.INFO.Println("creating", inpath)
+ p.s.Log.INFO.Println("creating", inpath)
if safe {
- err = helpers.SafeWriteToDisk(inpath, bytes.NewReader(by), hugofs.Source())
+ err = helpers.SafeWriteToDisk(inpath, bytes.NewReader(by), p.s.Fs.Source)
} else {
- err = helpers.WriteToDisk(inpath, bytes.NewReader(by), hugofs.Source())
+ err = helpers.WriteToDisk(inpath, bytes.NewReader(by), p.s.Fs.Source)
}
if err != nil {
return
@@ -1455,7 +1454,7 @@ func (p *Page) TargetPath() (outfile string) {
}
return p.addLangFilepathPrefix(filepath.Join(strings.ToLower(
- p.Site.pathSpec.MakePath(p.Source.Dir())), strings.TrimSpace(outfile)))
+ p.s.PathSpec.MakePath(p.Source.Dir())), strings.TrimSpace(outfile)))
}
// Pre render prepare steps
@@ -1466,14 +1465,13 @@ func (p *Page) prepareLayouts() error {
var layouts []string
if !p.IsRenderable() {
self := "__" + p.TargetPath()
- _, err := p.Site.owner.tmpl.GetClone().New(self).Parse(string(p.Content))
+ _, err := p.Site.owner.Tmpl.GetClone().New(self).Parse(string(p.Content))
if err != nil {
return err
}
layouts = append(layouts, self)
} else {
layouts = append(layouts, p.layouts()...)
- layouts = append(layouts, "_default/single.html")
}
p.layoutsCalculated = layouts
}
@@ -1707,7 +1705,7 @@ func (p *Page) initLanguage() {
if language == nil {
// It can be a file named stefano.chiodino.md.
- p.s.log.WARN.Printf("Page language (if it is that) not found in multilang setup: %s.", pageLang)
+ p.s.Log.WARN.Printf("Page language (if it is that) not found in multilang setup: %s.", pageLang)
language = ml.DefaultLang
}
diff --git a/hugolib/page_permalink_test.go b/hugolib/page_permalink_test.go
index 007f9eefc..5ed8a8b47 100644
--- a/hugolib/page_permalink_test.go
+++ b/hugolib/page_permalink_test.go
@@ -23,7 +23,8 @@ import (
"github.com/spf13/viper"
)
-func TestPermalink(t *testing.T) {
+// TODO(bep) globals test siteinfo
+func _TestPermalink(t *testing.T) {
testCommonResetState()
tests := []struct {
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index d5258de6a..10f2ed613 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -26,7 +26,9 @@ import (
"time"
"github.com/spf13/cast"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -465,7 +467,12 @@ activity = "exam"
Hi.
`
-var pageTestSite = NewSiteDefaultLang()
+func init() {
+ testCommonResetState()
+ pageTestSite, _ = NewSiteDefaultLang()
+}
+
+var pageTestSite *Site
func checkError(t *testing.T, err error, expected string) {
if err == nil {
@@ -606,6 +613,8 @@ func testAllMarkdownEnginesForPages(t *testing.T,
testCommonResetState()
+ fs := hugofs.NewMem()
+
if settings != nil {
for k, v := range settings {
viper.Set(k, v)
@@ -625,14 +634,10 @@ func testAllMarkdownEnginesForPages(t *testing.T,
}
for i := 0; i < len(fileSourcePairs); i += 2 {
- writeSource(t, filepath.Join(contentDir, fileSourcePairs[i]), fileSourcePairs[i+1])
+ writeSource(t, fs, filepath.Join(contentDir, fileSourcePairs[i]), fileSourcePairs[i+1])
}
- s := NewSiteDefaultLang()
-
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
require.Len(t, s.RegularPages, len(pageSources))
@@ -738,11 +743,14 @@ func TestPageWithDelimiter(t *testing.T) {
// Issue #1076
func TestPageWithDelimiterForMarkdownThatCrossesBorder(t *testing.T) {
- s := newSiteFromSources("simple.md", simplePageWithSummaryDelimiterAndMarkdownThatCrossesBorder)
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ testCommonResetState()
+
+ fs := hugofs.NewMem()
+
+ writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageWithSummaryDelimiterAndMarkdownThatCrossesBorder)
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
require.Len(t, s.RegularPages, 1)
@@ -759,16 +767,18 @@ func TestPageWithDelimiterForMarkdownThatCrossesBorder(t *testing.T) {
// Issue #2601
func TestPageRawContent(t *testing.T) {
- s := newSiteFromSources("raw.md", `---
+ testCommonResetState()
+
+ fs := hugofs.NewMem()
+
+ writeSource(t, fs, filepath.Join("content", "raw.md"), `---
title: Raw
---
**Raw**`)
- writeSource(t, filepath.Join("layouts", "_default", "single.html"), `{{ .RawContent }}`)
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), `{{ .RawContent }}`)
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
require.Len(t, s.RegularPages, 1)
p := s.RegularPages[0]
@@ -806,11 +816,12 @@ func TestPageWithEmbeddedScriptTag(t *testing.T) {
}
func TestPageWithAdditionalExtension(t *testing.T) {
- s := newSiteFromSources("simple.md", simplePageWithAdditionalExtension)
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ fs := hugofs.NewMem()
+
+ writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageWithAdditionalExtension)
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
@@ -820,11 +831,12 @@ func TestPageWithAdditionalExtension(t *testing.T) {
}
func TestTableOfContents(t *testing.T) {
- s := newSiteFromSources("tocpage.md", pageWithToC)
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ fs := hugofs.NewMem()
+
+ writeSource(t, fs, filepath.Join("content", "tocpage.md"), pageWithToC)
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
@@ -850,11 +862,11 @@ func TestPageWithMoreTag(t *testing.T) {
}
func TestPageWithDate(t *testing.T) {
- s := newSiteFromSources("simple.md", simplePageRFC3339Date)
+ fs := hugofs.NewMem()
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ writeSource(t, fs, filepath.Join("content", "simple.md"), simplePageRFC3339Date)
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
@@ -1372,11 +1384,11 @@ func TestKind(t *testing.T) {
func TestChompBOM(t *testing.T) {
const utf8BOM = "\xef\xbb\xbf"
- s := newSiteFromSources("simple.md", utf8BOM+simplePage)
+ fs := hugofs.NewMem()
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ writeSource(t, fs, filepath.Join("content", "simple.md"), utf8BOM+simplePage)
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
require.Len(t, s.RegularPages, 1)
diff --git a/hugolib/pagination.go b/hugolib/pagination.go
index 14a081131..acfa2b75e 100644
--- a/hugolib/pagination.go
+++ b/hugolib/pagination.go
@@ -279,7 +279,7 @@ func (p *Page) Paginator(options ...interface{}) (*Pager, error) {
return
}
- pagers, err := paginatePages(p.Data["Pages"], pagerSize, p.sections...)
+ pagers, err := paginatePages(p.s.PathSpec, p.Data["Pages"], pagerSize, p.sections...)
if err != nil {
initError = err
@@ -322,7 +322,7 @@ func (p *Page) Paginate(seq interface{}, options ...interface{}) (*Pager, error)
if p.paginator != nil {
return
}
- pagers, err := paginatePages(seq, pagerSize, p.sections...)
+ pagers, err := paginatePages(p.s.PathSpec, seq, pagerSize, p.sections...)
if err != nil {
initError = err
@@ -371,13 +371,13 @@ func resolvePagerSize(options ...interface{}) (int, error) {
return pas, nil
}
-func paginatePages(seq interface{}, pagerSize int, sections ...string) (pagers, error) {
+func paginatePages(pathSpec *helpers.PathSpec, seq interface{}, pagerSize int, sections ...string) (pagers, error) {
if pagerSize <= 0 {
return nil, errors.New("'paginate' configuration setting must be positive to paginate")
}
- urlFactory := newPaginationURLFactory(sections...)
+ urlFactory := newPaginationURLFactory(pathSpec, sections...)
var paginator *paginator
@@ -504,8 +504,7 @@ func newPaginator(elements []paginatedElement, total, size int, urlFactory pagin
return p, nil
}
-func newPaginationURLFactory(pathElements ...string) paginationURLFactory {
- pathSpec := helpers.CurrentPathSpec()
+func newPaginationURLFactory(pathSpec *helpers.PathSpec, pathElements ...string) paginationURLFactory {
basePath := path.Join(pathElements...)
diff --git a/hugolib/pagination_test.go b/hugolib/pagination_test.go
index 9bc8ffea4..a7f2d9392 100644
--- a/hugolib/pagination_test.go
+++ b/hugolib/pagination_test.go
@@ -19,10 +19,13 @@ import (
"path/filepath"
"testing"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
func TestSplitPages(t *testing.T) {
@@ -197,11 +200,22 @@ func TestPaginationURLFactory(t *testing.T) {
testCommonResetState()
viper.Set("paginatePath", "zoo")
- unicode := newPaginationURLFactory("новости проекта")
- fooBar := newPaginationURLFactory("foo", "bar")
+
+ pathSpec := newTestPathSpec()
+
+ unicode := newPaginationURLFactory(pathSpec, "новости проекта")
+ fooBar := newPaginationURLFactory(pathSpec, "foo", "bar")
assert.Equal(t, "/foo/bar/", fooBar(1))
assert.Equal(t, "/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0/zoo/4/", unicode(4))
+
+ unicoded := unicode(4)
+ unicodedExpected := "/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0/zoo/4/"
+
+ if unicoded != unicodedExpected {
+ t.Fatal("Expected\n", unicodedExpected, "\nGot\n", unicoded)
+ }
+
assert.Equal(t, "/foo/bar/zoo/12345/", fooBar(12345))
}
@@ -224,13 +238,13 @@ func doTestPaginator(t *testing.T, useViper bool) {
viper.Set("paginate", -1)
}
pages := createTestPages(12)
- s := NewSiteDefaultLang()
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
n1 := s.newHomePage()
n2 := s.newHomePage()
n1.Data["Pages"] = pages
var paginator1 *Pager
- var err error
if useViper {
paginator1, err = n1.Paginator()
@@ -261,9 +275,10 @@ func TestPaginatorWithNegativePaginate(t *testing.T) {
testCommonResetState()
viper.Set("paginate", -1)
- s := NewSiteDefaultLang()
- _, err := s.newHomePage().Paginator()
- assert.NotNil(t, err)
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
+ _, err = s.newHomePage().Paginator()
+ require.Error(t, err)
}
func TestPaginate(t *testing.T) {
@@ -280,9 +295,11 @@ func TestPaginatorURL(t *testing.T) {
viper.Set("paginate", 2)
viper.Set("paginatePath", "testing")
+ fs := hugofs.NewMem()
+
for i := 0; i < 10; i++ {
// Issue #2177, do not double encode URLs
- writeSource(t, filepath.Join("content", "阅读", fmt.Sprintf("page%d.md", (i+1))),
+ writeSource(t, fs, filepath.Join("content", "阅读", fmt.Sprintf("page%d.md", (i+1))),
fmt.Sprintf(`---
title: Page%d
---
@@ -290,8 +307,8 @@ Conten%d
`, (i+1), i+1))
}
- writeSource(t, filepath.Join("layouts", "_default", "single.html"), "<html><body>{{.Content}}</body></html>")
- writeSource(t, filepath.Join("layouts", "_default", "list.html"),
+ writeSource(t, fs, filepath.Join("layouts", "_default", "single.html"), "<html><body>{{.Content}}</body></html>")
+ writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"),
`
<html><body>
Count: {{ .Paginator.TotalNumberOfElements }}
@@ -301,11 +318,9 @@ Pages: {{ .Paginator.TotalPages }}
{{ end }}
</body></html>`)
- if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
- assertFileContent(t, filepath.Join("public", "阅读", "testing", "2", "index.html"), false, "2: /%E9%98%85%E8%AF%BB/testing/2/")
+ assertFileContent(t, fs, filepath.Join("public", "阅读", "testing", "2", "index.html"), false, "2: /%E9%98%85%E8%AF%BB/testing/2/")
}
@@ -318,12 +333,12 @@ func doTestPaginate(t *testing.T, useViper bool) {
}
pages := createTestPages(6)
- s := NewSiteDefaultLang()
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
n1 := s.newHomePage()
n2 := s.newHomePage()
var paginator1, paginator2 *Pager
- var err error
if useViper {
paginator1, err = n1.Paginate(pages)
@@ -351,9 +366,10 @@ func doTestPaginate(t *testing.T, useViper bool) {
}
func TestInvalidOptions(t *testing.T) {
- s := NewSiteDefaultLang()
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
n1 := s.newHomePage()
- _, err := n1.Paginate(createTestPages(1), 1, 2)
+ _, err = n1.Paginate(createTestPages(1), 1, 2)
assert.NotNil(t, err)
_, err = n1.Paginator(1, 2)
assert.NotNil(t, err)
@@ -365,19 +381,22 @@ func TestPaginateWithNegativePaginate(t *testing.T) {
testCommonResetState()
viper.Set("paginate", -1)
- s := NewSiteDefaultLang()
- _, err := s.newHomePage().Paginate(createTestPages(2))
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
+ _, err = s.newHomePage().Paginate(createTestPages(2))
assert.NotNil(t, err)
}
func TestPaginatePages(t *testing.T) {
groups, _ := createTestPages(31).GroupBy("Weight", "desc")
+ pathSpec := newTestPathSpec()
+
for i, seq := range []interface{}{createTestPages(11), groups, WeightedPages{}, PageGroup{}, &Pages{}} {
- v, err := paginatePages(seq, 11, "t")
+ v, err := paginatePages(pathSpec, seq, 11, "t")
assert.NotNil(t, v, "Val %d", i)
assert.Nil(t, err, "Err %d", i)
}
- _, err := paginatePages(Site{}, 11, "t")
+ _, err := paginatePages(pathSpec, Site{}, 11, "t")
assert.NotNil(t, err)
}
@@ -387,11 +406,12 @@ func TestPaginatorFollowedByPaginateShouldFail(t *testing.T) {
testCommonResetState()
viper.Set("paginate", 10)
- s := NewSiteDefaultLang()
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
n1 := s.newHomePage()
n2 := s.newHomePage()
- _, err := n1.Paginator()
+ _, err = n1.Paginator()
assert.Nil(t, err)
_, err = n1.Paginate(createTestPages(2))
assert.NotNil(t, err)
@@ -405,14 +425,15 @@ func TestPaginateFollowedByDifferentPaginateShouldFail(t *testing.T) {
testCommonResetState()
viper.Set("paginate", 10)
- s := NewSiteDefaultLang()
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
n1 := s.newHomePage()
n2 := s.newHomePage()
p1 := createTestPages(2)
p2 := createTestPages(10)
- _, err := n1.Paginate(p1)
+ _, err = n1.Paginate(p1)
assert.Nil(t, err)
_, err = n1.Paginate(p1)
diff --git a/hugolib/permalinks.go b/hugolib/permalinks.go
index b924673ff..959386419 100644
--- a/hugolib/permalinks.go
+++ b/hugolib/permalinks.go
@@ -150,14 +150,14 @@ func pageToPermalinkDate(p *Page, dateField string) (string, error) {
func pageToPermalinkTitle(p *Page, _ string) (string, error) {
// Page contains Node which has Title
// (also contains URLPath which has Slug, sometimes)
- return p.Site.pathSpec.URLize(p.Title), nil
+ return p.s.PathSpec.URLize(p.Title), nil
}
// pageToPermalinkFilename returns the URL-safe form of the filename
func pageToPermalinkFilename(p *Page, _ string) (string, error) {
//var extension = p.Source.Ext
//var name = p.Source.Path()[0 : len(p.Source.Path())-len(extension)]
- return p.Site.pathSpec.URLize(p.Source.TranslationBaseName()), nil
+ return p.s.PathSpec.URLize(p.Source.TranslationBaseName()), nil
}
// if the page has a slug, return the slug, else return the title
@@ -172,7 +172,7 @@ func pageToPermalinkSlugElseTitle(p *Page, a string) (string, error) {
if strings.HasSuffix(p.Slug, "-") {
p.Slug = p.Slug[0 : len(p.Slug)-1]
}
- return p.Site.pathSpec.URLize(p.Slug), nil
+ return p.s.PathSpec.URLize(p.Slug), nil
}
return pageToPermalinkTitle(p, a)
}
diff --git a/hugolib/robotstxt_test.go b/hugolib/robotstxt_test.go
index 2faabda7b..1b42011ca 100644
--- a/hugolib/robotstxt_test.go
+++ b/hugolib/robotstxt_test.go
@@ -14,12 +14,12 @@
package hugolib
import (
- "bytes"
+ "path/filepath"
"testing"
- "github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
- "github.com/spf13/hugo/source"
+
+ "github.com/spf13/hugo/deps"
"github.com/spf13/viper"
)
@@ -32,28 +32,16 @@ const robotTxtTemplate = `User-agent: Googlebot
func TestRobotsTXTOutput(t *testing.T) {
testCommonResetState()
- hugofs.InitMemFs()
-
viper.Set("baseURL", "http://auth/bub/")
viper.Set("enableRobotsTXT", true)
- s := &Site{
- Source: &source.InMemorySource{ByteSource: weightedSources},
- Language: helpers.NewDefaultLanguage(),
- }
+ fs := hugofs.NewMem()
- if err := buildAndRenderSite(s, "robots.txt", robotTxtTemplate); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ writeSource(t, fs, filepath.Join("layouts", "robots.txt"), robotTxtTemplate)
+ writeSourcesToSource(t, "content", fs, weightedSources...)
- robotsFile, err := hugofs.Destination().Open("public/robots.txt")
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
- if err != nil {
- t.Fatalf("Unable to locate: robots.txt")
- }
+ assertFileContent(t, fs, "public/robots.txt", true, "User-agent: Googlebot")
- robots := helpers.ReaderToBytes(robotsFile)
- if !bytes.HasPrefix(robots, []byte("User-agent: Googlebot")) {
- t.Errorf("Robots file should start with 'User-agent: Googlebot'. %s", robots)
- }
}
diff --git a/hugolib/rss_test.go b/hugolib/rss_test.go
index 72a180fe0..74a59be6d 100644
--- a/hugolib/rss_test.go
+++ b/hugolib/rss_test.go
@@ -17,6 +17,8 @@ import (
"path/filepath"
"testing"
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
)
@@ -28,19 +30,19 @@ func TestRSSOutput(t *testing.T) {
viper.Set("rssURI", rssURI)
viper.Set("title", "RSSTest")
- for _, s := range weightedSources {
- writeSource(t, filepath.Join("content", "sect", s.Name), string(s.Content))
- }
+ fs := hugofs.NewMem()
- if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil {
- t.Fatalf("Failed to build site: %s", err)
+ for _, src := range weightedSources {
+ writeSource(t, fs, filepath.Join("content", "sect", src.Name), string(src.Content))
}
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+
// Home RSS
- assertFileContent(t, filepath.Join("public", rssURI), true, "<?xml", "rss version", "RSSTest")
+ assertFileContent(t, fs, filepath.Join("public", rssURI), true, "<?xml", "rss version", "RSSTest")
// Section RSS
- assertFileContent(t, filepath.Join("public", "sect", rssURI), true, "<?xml", "rss version", "Sects on RSSTest")
+ assertFileContent(t, fs, filepath.Join("public", "sect", rssURI), true, "<?xml", "rss version", "Sects on RSSTest")
// Taxonomy RSS
- assertFileContent(t, filepath.Join("public", "categories", "hugo", rssURI), true, "<?xml", "rss version", "Hugo on RSSTest")
+ assertFileContent(t, fs, filepath.Join("public", "categories", "hugo", rssURI), true, "<?xml", "rss version", "Hugo on RSSTest")
}
diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go
index 78610d638..afee1884f 100644
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -26,7 +26,7 @@ import (
bp "github.com/spf13/hugo/bufferpool"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/tpl"
+ "github.com/spf13/hugo/tplapi"
)
// ShortcodeWithPage is the "." context in a shortcode template.
@@ -211,10 +211,10 @@ const innerCleanupRegexp = `\A<p>(.*)</p>\n\z`
const innerCleanupExpand = "$1"
func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page) string {
- tmpl := getShortcodeTemplate(sc.name, p.s.tmpl)
+ tmpl := getShortcodeTemplate(sc.name, p.s.Tmpl)
if tmpl == nil {
- p.s.log.ERROR.Printf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName())
+ p.s.Log.ERROR.Printf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName())
return ""
}
@@ -232,7 +232,7 @@ func renderShortcode(sc shortcode, parent *ShortcodeWithPage, p *Page) string {
case shortcode:
inner += renderShortcode(innerData.(shortcode), data, p)
default:
- p.s.log.ERROR.Printf("Illegal state on shortcode rendering of '%s' in page %s. Illegal type in inner data: %s ",
+ p.s.Log.ERROR.Printf("Illegal state on shortcode rendering of '%s' in page %s. Illegal type in inner data: %s ",
sc.name, p.BaseFileName(), reflect.TypeOf(innerData))
return ""
}
@@ -286,7 +286,7 @@ func extractAndRenderShortcodes(stringToParse string, p *Page) (string, map[stri
if err != nil {
// try to render what we have whilst logging the error
- p.s.log.ERROR.Println(err.Error())
+ p.s.Log.ERROR.Println(err.Error())
}
// Save for reuse
@@ -398,7 +398,7 @@ Loop:
sc.inner = append(sc.inner, currItem.val)
case tScName:
sc.name = currItem.val
- tmpl := getShortcodeTemplate(sc.name, p.s.tmpl)
+ tmpl := getShortcodeTemplate(sc.name, p.s.Tmpl)
if tmpl == nil {
return sc, fmt.Errorf("Unable to locate template for shortcode '%s' in page %s", sc.name, p.BaseFileName())
@@ -566,7 +566,7 @@ func replaceShortcodeTokens(source []byte, prefix string, replacements map[strin
return source, nil
}
-func getShortcodeTemplate(name string, t tpl.Template) *template.Template {
+func getShortcodeTemplate(name string, t tplapi.Template) *template.Template {
if x := t.Lookup("shortcodes/" + name + ".html"); x != nil {
return x
}
@@ -584,9 +584,8 @@ func renderShortcodeWithPage(tmpl *template.Template, data *ShortcodeWithPage) s
err := tmpl.Execute(buffer, data)
isInnerShortcodeCache.RUnlock()
if err != nil {
- // TODO(bep) globals
- data.Page.s.log.ERROR.Println("error processing shortcode", tmpl.Name(), "\n ERR:", err)
- data.Page.s.log.WARN.Println(data)
+ data.Page.s.Log.ERROR.Println("error processing shortcode", tmpl.Name(), "\n ERR:", err)
+ data.Page.s.Log.WARN.Println(data)
}
return buffer.String()
}
diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go
index 243705345..d4494dba2 100644
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -22,49 +22,52 @@ import (
"strings"
"testing"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
- "github.com/spf13/hugo/target"
- "github.com/spf13/hugo/tpl"
+ "github.com/spf13/hugo/tplapi"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
)
// TODO(bep) remove
-func pageFromString(in, filename string, withTemplate ...func(templ tpl.Template) error) (*Page, error) {
+func pageFromString(in, filename string, withTemplate ...func(templ tplapi.Template) error) (*Page, error) {
s := pageTestSite
if len(withTemplate) > 0 {
// Have to create a new site
- s = NewSiteDefaultLang(withTemplate...)
+ var err error
+ s, err = NewSiteDefaultLang(withTemplate...)
+ if err != nil {
+ return nil, err
+ }
}
return s.NewPageFrom(strings.NewReader(in), filename)
}
-func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error) {
+func CheckShortCodeMatch(t *testing.T, input, expected string, withTemplate func(templ tplapi.Template) error) {
CheckShortCodeMatchAndError(t, input, expected, withTemplate, false)
}
-func CheckShortCodeMatchAndError(t *testing.T, input, expected string, withTemplate func(templ tpl.Template) error, expectError bool) {
+func CheckShortCodeMatchAndError(t *testing.T, input, expected string, withTemplate func(templ tplapi.Template) error, expectError bool) {
testCommonResetState()
+ fs := hugofs.NewMem()
+
// Need some front matter, see https://github.com/spf13/hugo/issues/2337
contentFile := `---
title: "Title"
---
` + input
- writeSource(t, "content/simple.md", contentFile)
+ writeSource(t, fs, "content/simple.md", contentFile)
- h, err := newHugoSitesDefaultLanguage()
+ h, err := NewHugoSitesFromConfiguration(deps.DepsCfg{Fs: fs, WithTemplate: withTemplate})
- if err != nil {
- t.Fatalf("Failed to create sites: %s", err)
- }
+ require.NoError(t, err)
+ require.Len(t, h.Sites, 1)
- cfg := BuildCfg{SkipRender: true, withTemplate: withTemplate}
-
- err = h.Build(cfg)
+ err = h.Build(BuildCfg{})
if err != nil && !expectError {
t.Fatalf("Shortcode rendered error %s.", err)
@@ -89,7 +92,7 @@ title: "Title"
func TestShortcodeGoFuzzReports(t *testing.T) {
- p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
+ p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error {
return templ.AddInternalShortcode("sc.html", `foo`)
})
@@ -124,7 +127,7 @@ func TestNonSC(t *testing.T) {
// Issue #929
func TestHyphenatedSC(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("hyphenated-video.html", `Playing Video {{ .Get 0 }}`)
return nil
}
@@ -134,7 +137,7 @@ func TestHyphenatedSC(t *testing.T) {
// Issue #1753
func TestNoTrailingNewline(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("a.html", `{{ .Get 0 }}`)
return nil
}
@@ -143,7 +146,7 @@ func TestNoTrailingNewline(t *testing.T) {
}
func TestPositionalParamSC(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 0 }}`)
return nil
}
@@ -156,7 +159,7 @@ func TestPositionalParamSC(t *testing.T) {
}
func TestPositionalParamIndexOutOfBounds(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("video.html", `Playing Video {{ .Get 1 }}`)
return nil
}
@@ -166,7 +169,7 @@ func TestPositionalParamIndexOutOfBounds(t *testing.T) {
// some repro issues for panics in Go Fuzz testing
func TestNamedParamSC(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("img.html", `<img{{ with .Get "src" }} src="{{.}}"{{end}}{{with .Get "class"}} class="{{.}}"{{end}}>`)
return nil
}
@@ -180,7 +183,7 @@ func TestNamedParamSC(t *testing.T) {
// Issue #2294
func TestNestedNamedMissingParam(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("acc.html", `<div class="acc">{{ .Inner }}</div>`)
tem.AddInternalShortcode("div.html", `<div {{with .Get "class"}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
tem.AddInternalShortcode("div2.html", `<div {{with .Get 0}} class="{{ . }}"{{ end }}>{{ .Inner }}</div>`)
@@ -192,7 +195,7 @@ func TestNestedNamedMissingParam(t *testing.T) {
}
func TestIsNamedParamsSC(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("byposition.html", `<div id="{{ .Get 0 }}">`)
tem.AddInternalShortcode("byname.html", `<div id="{{ .Get "id" }}">`)
tem.AddInternalShortcode("ifnamedparams.html", `<div id="{{ if .IsNamedParams }}{{ .Get "id" }}{{ else }}{{ .Get 0 }}{{end}}">`)
@@ -207,7 +210,7 @@ func TestIsNamedParamsSC(t *testing.T) {
}
func TestInnerSC(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
return nil
}
@@ -217,7 +220,7 @@ func TestInnerSC(t *testing.T) {
}
func TestInnerSCWithMarkdown(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
return nil
}
@@ -230,7 +233,7 @@ func TestInnerSCWithMarkdown(t *testing.T) {
}
func TestInnerSCWithAndWithoutMarkdown(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("inside.html", `<div{{with .Get "class"}} class="{{.}}"{{end}}>{{ .Inner }}</div>`)
return nil
}
@@ -259,7 +262,7 @@ func TestEmbeddedSC(t *testing.T) {
}
func TestNestedSC(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("scn1.html", `<div>Outer, inner is {{ .Inner }}</div>`)
tem.AddInternalShortcode("scn2.html", `<div>SC2</div>`)
return nil
@@ -270,7 +273,7 @@ func TestNestedSC(t *testing.T) {
}
func TestNestedComplexSC(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("row.html", `-row-{{ .Inner}}-rowStop-`)
tem.AddInternalShortcode("column.html", `-col-{{.Inner }}-colStop-`)
tem.AddInternalShortcode("aside.html", `-aside-{{ .Inner }}-asideStop-`)
@@ -285,7 +288,7 @@ func TestNestedComplexSC(t *testing.T) {
}
func TestParentShortcode(t *testing.T) {
- wt := func(tem tpl.Template) error {
+ wt := func(tem tplapi.Template) error {
tem.AddInternalShortcode("r1.html", `1: {{ .Get "pr1" }} {{ .Inner }}`)
tem.AddInternalShortcode("r2.html", `2: {{ .Parent.Get "pr1" }}{{ .Get "pr2" }} {{ .Inner }}`)
tem.AddInternalShortcode("r3.html", `3: {{ .Parent.Parent.Get "pr1" }}{{ .Parent.Get "pr2" }}{{ .Get "pr3" }} {{ .Inner }}`)
@@ -382,7 +385,7 @@ func TestExtractShortcodes(t *testing.T) {
fmt.Sprintf("Hello %sworld%s. And that's it.", testScPlaceholderRegexp, testScPlaceholderRegexp), ""},
} {
- p, _ := pageFromString(simplePage, "simple.md", func(templ tpl.Template) error {
+ p, _ := pageFromString(simplePage, "simple.md", func(templ tplapi.Template) error {
templ.AddInternalShortcode("tag.html", `tag`)
templ.AddInternalShortcode("sc1.html", `sc1`)
templ.AddInternalShortcode("sc2.html", `sc2`)
@@ -471,7 +474,7 @@ func TestShortcodesInSite(t *testing.T) {
expected string
}{
{"sect/doc1.md", `a{{< b >}}c`,
- filepath.FromSlash("sect/doc1/index.html"), "<p>abc</p>\n"},
+ filepath.FromSlash("public/sect/doc1/index.html"), "<p>abc</p>\n"},
// Issue #1642: Multiple shortcodes wrapped in P
// Deliberately forced to pass even if they maybe shouldn't.
{"sect/doc2.md", `a
@@ -481,7 +484,7 @@ func TestShortcodesInSite(t *testing.T) {
{{< d >}}
e`,
- filepath.FromSlash("sect/doc2/index.html"),
+ filepath.FromSlash("public/sect/doc2/index.html"),
"<p>a</p>\n\n<p>b<br />\nc\nd</p>\n\n<p>e</p>\n"},
{"sect/doc3.md", `a
@@ -491,7 +494,7 @@ e`,
{{< d >}}
e`,
- filepath.FromSlash("sect/doc3/index.html"),
+ filepath.FromSlash("public/sect/doc3/index.html"),
"<p>a</p>\n\n<p>b<br />\nc</p>\n\nd\n\n<p>e</p>\n"},
{"sect/doc4.md", `a
{{< b >}}
@@ -510,22 +513,22 @@ e`,
`,
- filepath.FromSlash("sect/doc4/index.html"),
+ filepath.FromSlash("public/sect/doc4/index.html"),
"<p>a\nb\nb\nb\nb\nb</p>\n"},
// #2192 #2209: Shortcodes in markdown headers
{"sect/doc5.md", `# {{< b >}}
## {{% c %}}`,
- filepath.FromSlash("sect/doc5/index.html"), "\n\n<h1 id=\"hahahugoshortcode-1hbhb\">b</h1>\n\n<h2 id=\"hahahugoshortcode-2hbhb\">c</h2>\n"},
+ filepath.FromSlash("public/sect/doc5/index.html"), "\n\n<h1 id=\"hahahugoshortcode-1hbhb\">b</h1>\n\n<h2 id=\"hahahugoshortcode-2hbhb\">c</h2>\n"},
// #2223 pygments
{"sect/doc6.md", "\n```bash\nb: {{< b >}} c: {{% c %}}\n```\n",
- filepath.FromSlash("sect/doc6/index.html"),
+ filepath.FromSlash("public/sect/doc6/index.html"),
"b: b c: c\n</code></pre></div>\n"},
// #2249
{"sect/doc7.ad", `_Shortcodes:_ *b: {{< b >}} c: {{% c %}}*`,
- filepath.FromSlash("sect/doc7/index.html"),
+ filepath.FromSlash("public/sect/doc7/index.html"),
"<div class=\"paragraph\">\n<p><em>Shortcodes:</em> <strong>b: b c: c</strong></p>\n</div>\n"},
{"sect/doc8.rst", `**Shortcodes:** *b: {{< b >}} c: {{% c %}}*`,
- filepath.FromSlash("sect/doc8/index.html"),
+ filepath.FromSlash("public/sect/doc8/index.html"),
"<div class=\"document\">\n\n\n<p><strong>Shortcodes:</strong> <em>b: b c: c</em></p>\n</div>"},
{"sect/doc9.mmark", `
---
@@ -534,7 +537,7 @@ menu:
parent: 'parent'
---
**Shortcodes:** *b: {{< b >}} c: {{% c %}}*`,
- filepath.FromSlash("sect/doc9/index.html"),
+ filepath.FromSlash("public/sect/doc9/index.html"),
"<p><strong>Shortcodes:</strong> <em>b: b c: c</em></p>\n"},
// Issue #1229: Menus not available in shortcode.
{"sect/doc10.md", `---
@@ -545,7 +548,7 @@ tags:
- Menu
---
**Menus:** {{< menu >}}`,
- filepath.FromSlash("sect/doc10/index.html"),
+ filepath.FromSlash("public/sect/doc10/index.html"),
"<p><strong>Menus:</strong> 1</p>\n"},
// Issue #2323: Taxonomies not available in shortcode.
{"sect/doc11.md", `---
@@ -553,7 +556,7 @@ tags:
- Bugs
---
**Tags:** {{< tags >}}`,
- filepath.FromSlash("sect/doc11/index.html"),
+ filepath.FromSlash("public/sect/doc11/index.html"),
"<p><strong>Tags:</strong> 2</p>\n"},
}
@@ -563,13 +566,7 @@ tags:
sources[i] = source.ByteSource{Name: filepath.FromSlash(test.contentPath), Content: []byte(test.content)}
}
- s := &Site{
- Source: &source.InMemorySource{ByteSource: sources},
- targets: targetList{page: &target.PagePub{UglyURLs: false}},
- Language: helpers.NewDefaultLanguage(),
- }
-
- addTemplates := func(templ tpl.Template) error {
+ addTemplates := func(templ tplapi.Template) error {
templ.AddTemplate("_default/single.html", "{{.Content}}")
templ.AddInternalShortcode("b.html", `b`)
@@ -582,15 +579,11 @@ tags:
}
- sites, err := newHugoSites(DepsCfg{}, s)
+ fs := hugofs.NewMem()
- if err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ writeSourcesToSource(t, "content", fs, sources...)
- if err = sites.Build(BuildCfg{withTemplate: addTemplates}); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ buildSingleSite(t, deps.DepsCfg{WithTemplate: addTemplates, Fs: fs}, BuildCfg{})
for _, test := range tests {
if strings.HasSuffix(test.contentPath, ".ad") && !helpers.HasAsciidoc() {
@@ -604,17 +597,7 @@ tags:
continue
}
- file, err := hugofs.Destination().Open(test.outFile)
-
- if err != nil {
- t.Fatalf("Did not find %s in target: %s", test.outFile, err)
- }
-
- content := helpers.ReaderToString(file)
-
- if !strings.Contains(content, test.expected) {
- t.Fatalf("%s content expected:\n%q\ngot:\n%q", test.outFile, test.expected, content)
- }
+ assertFileContent(t, fs, test.outFile, true, test.expected)
}
}
diff --git a/hugolib/site.go b/hugolib/site.go
index c887a9305..6afc18a69 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -35,12 +35,14 @@ import (
"github.com/spf13/afero"
"github.com/spf13/cast"
bp "github.com/spf13/hugo/bufferpool"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/parser"
"github.com/spf13/hugo/source"
"github.com/spf13/hugo/target"
"github.com/spf13/hugo/tpl"
+ "github.com/spf13/hugo/tplapi"
"github.com/spf13/hugo/transform"
"github.com/spf13/nitro"
"github.com/spf13/viper"
@@ -106,57 +108,81 @@ type Site struct {
Language *helpers.Language
// Logger etc.
- *deps
+ *deps.Deps `json:"-"`
}
// reset returns a new Site prepared for rebuild.
func (s *Site) reset() *Site {
- return &Site{deps: s.deps, Language: s.Language, owner: s.owner, PageCollections: newPageCollections()}
+ return &Site{Deps: s.Deps, owner: s.owner, PageCollections: newPageCollections()}
}
-// newSite creates a new site in the given language.
-func newSite(lang *helpers.Language, deps *deps, withTemplate ...func(templ tpl.Template) error) *Site {
+// newSite creates a new site with the given configuration.
+func newSite(cfg deps.DepsCfg) (*Site, error) {
c := newPageCollections()
- // TODO(bep) globals
- viper.Set("currentContentLanguage", lang)
- if deps == nil {
- depsCfg := DepsCfg{WithTemplate: withTemplate}
- deps = newDeps(depsCfg)
+ if cfg.Language == nil {
+ cfg.Language = helpers.NewDefaultLanguage()
}
- return &Site{deps: deps, Language: lang, PageCollections: c, Info: newSiteInfo(siteBuilderCfg{pageCollections: c, language: lang})}
+ s := &Site{PageCollections: c, Language: cfg.Language}
-}
+ s.Info = newSiteInfo(siteBuilderCfg{s: s, pageCollections: c, language: s.Language})
+ return s, nil
-// NewSiteDefaultLang creates a new site in the default language.
-func NewSiteDefaultLang(withTemplate ...func(templ tpl.Template) error) *Site {
- return newSite(helpers.NewDefaultLanguage(), nil, withTemplate...)
}
-// Convenience func used in tests.
-func newSiteFromSources(pathContentPairs ...string) *Site {
- if len(pathContentPairs)%2 != 0 {
- panic("pathContentPairs must come in pairs")
+// NewSite creates a new site with the given dependency configuration.
+// The site will have a template system loaded and ready to use.
+// Note: This is mainly used in single site tests.
+func NewSite(cfg deps.DepsCfg) (*Site, error) {
+ s, err := newSite(cfg)
+
+ if err != nil {
+ return nil, err
}
- sources := make([]source.ByteSource, 0)
+ if err := applyDepsIfNeeded(cfg, s); err != nil {
+ return nil, err
+ }
+
+ return s, nil
+}
+
+// NewSiteDefaultLang creates a new site in the default language.
+// The site will have a template system loaded and ready to use.
+// Note: This is mainly used in single site tests.
+func NewSiteDefaultLang(withTemplate ...func(templ tplapi.Template) error) (*Site, error) {
+ return newSiteForLang(helpers.NewDefaultLanguage(), withTemplate...)
+}
+
+// NewSiteDefaultLang creates a new site in the default language.
+// The site will have a template system loaded and ready to use.
+// Note: This is mainly used in single site tests.
+func NewEnglishSite(withTemplate ...func(templ tplapi.Template) error) (*Site, error) {
+ return newSiteForLang(helpers.NewLanguage("en"), withTemplate...)
+}
- for i := 0; i < len(pathContentPairs); i += 2 {
- path := pathContentPairs[i]
- content := pathContentPairs[i+1]
- sources = append(sources, source.ByteSource{Name: filepath.FromSlash(path), Content: []byte(content)})
+// NewSiteDefaultLang creates a new site in the default language.
+func newSiteForLang(lang *helpers.Language, withTemplate ...func(templ tplapi.Template) error) (*Site, error) {
+ withTemplates := func(templ tplapi.Template) error {
+ for _, wt := range withTemplate {
+ if err := wt(templ); err != nil {
+ return err
+ }
+ }
+ return nil
}
+ cfg := deps.DepsCfg{WithTemplate: withTemplates, Language: lang}
+ s, err := newSite(cfg)
- lang := helpers.NewDefaultLanguage()
+ if err != nil {
+ return nil, err
+ }
- return &Site{
- deps: newDeps(DepsCfg{}),
- PageCollections: newPageCollections(),
- Source: &source.InMemorySource{ByteSource: sources},
- Language: lang,
- Info: newSiteInfo(siteBuilderCfg{language: lang}),
+ if err := applyDepsIfNeeded(cfg, s); err != nil {
+ return nil, err
}
+ return s, nil
}
type targetList struct {
@@ -202,14 +228,13 @@ type SiteInfo struct {
Data *map[string]interface{}
owner *HugoSites
+ s *Site
multilingual *Multilingual
Language *helpers.Language
LanguagePrefix string
Languages helpers.Languages
defaultContentLanguageInSubdir bool
sectionPagesMenu string
-
- pathSpec *helpers.PathSpec
}
func (s *SiteInfo) String() string {
@@ -219,15 +244,19 @@ func (s *SiteInfo) String() string {
// Used in tests.
type siteBuilderCfg struct {
- language *helpers.Language
+ language *helpers.Language
+ // TOD(bep) globals fs
+ s *Site
+ fs *hugofs.Fs
pageCollections *PageCollections
baseURL string
}
+// TODO(bep) globals get rid of this
func newSiteInfo(cfg siteBuilderCfg) SiteInfo {
return SiteInfo{
+ s: cfg.s,
BaseURL: template.URL(cfg.baseURL),
- pathSpec: helpers.NewPathSpecFromConfig(cfg.language),
multilingual: newMultiLingualForLanguage(cfg.language),
PageCollections: cfg.pageCollections,
}
@@ -498,7 +527,7 @@ type whatChanged struct {
// It returns whetever the content source was changed.
func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
- s.log.DEBUG.Printf("Rebuild for events %q", events)
+ s.Log.DEBUG.Printf("Rebuild for events %q", events)
s.timerStep("initialize rebuild")
@@ -533,8 +562,25 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
}
if len(tmplChanged) > 0 {
- s.prepTemplates(nil)
- s.owner.tmpl.PrintErrors()
+ sites := s.owner.Sites
+ first := sites[0]
+
+ // TOD(bep) globals clean
+ if err := first.Deps.LoadTemplates(); err != nil {
+ s.Log.ERROR.Println(err)
+ }
+
+ s.Tmpl.PrintErrors()
+
+ for i := 1; i < len(sites); i++ {
+ site := sites[i]
+ var err error
+ site.Deps, err = first.Deps.ForLanguage(site.Language)
+ if err != nil {
+ return whatChanged{}, err
+ }
+ }
+
s.timerStep("template prep")
}
@@ -544,7 +590,7 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
if len(i18nChanged) > 0 {
if err := s.readI18nSources(); err != nil {
- s.log.ERROR.Println(err)
+ s.Log.ERROR.Println(err)
}
}
@@ -595,7 +641,7 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
// it's been updated
if ev.Op&fsnotify.Rename == fsnotify.Rename {
// If the file is still on disk, it's only been updated, if it's not, it's been moved
- if ex, err := afero.Exists(hugofs.Source(), ev.Name); !ex || err != nil {
+ if ex, err := afero.Exists(s.Fs.Source, ev.Name); !ex || err != nil {
path, _ := helpers.GetRelativePath(ev.Name, s.getContentDir(ev.Name))
s.removePageByPath(path)
continue
@@ -613,7 +659,7 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
file, err := s.reReadFile(ev.Name)
if err != nil {
- s.log.ERROR.Println("Error reading file", ev.Name, ";", err)
+ s.Log.ERROR.Println("Error reading file", ev.Name, ";", err)
}
if file != nil {
@@ -647,7 +693,7 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
for i := 0; i < 2; i++ {
err := <-errs
if err != nil {
- s.log.ERROR.Println(err)
+ s.Log.ERROR.Println(err)
}
}
@@ -660,29 +706,8 @@ func (s *Site) reProcess(events []fsnotify.Event) (whatChanged, error) {
}
-func (s *Site) prepTemplates(withTemplate func(templ tpl.Template) error) error {
-
- wt := func(tmpl tpl.Template) error {
- // TODO(bep) global error handling
- tmpl.LoadTemplates(s.absLayoutDir())
- if s.hasTheme() {
- tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
- }
- if withTemplate != nil {
- if err := withTemplate(tmpl); err != nil {
- return err
- }
- }
- return nil
- }
-
- s.refreshTemplates(wt)
-
- return nil
-}
-
func (s *Site) loadData(sources []source.Input) (err error) {
- s.log.DEBUG.Printf("Load Data from %q", sources)
+ s.Log.DEBUG.Printf("Load Data from %q", sources)
s.Data = make(map[string]interface{})
var current map[string]interface{}
for _, currentSource := range sources {
@@ -717,7 +742,7 @@ func (s *Site) loadData(sources []source.Input) (err error) {
// this warning could happen if
// 1. A theme uses the same key; the main data folder wins
// 2. A sub folder uses the same key: the sub folder wins
- s.log.WARN.Printf("Data for key '%s' in path '%s' is overridden in subfolder", key, r.Path())
+ s.Log.WARN.Printf("Data for key '%s' in path '%s' is overridden in subfolder", key, r.Path())
}
data[key] = value
}
@@ -740,21 +765,21 @@ func (s *Site) readData(f *source.File) (interface{}, error) {
case "toml":
return parser.HandleTOMLMetaData(f.Bytes())
default:
- s.log.WARN.Printf("Data not supported for extension '%s'", f.Extension())
+ s.Log.WARN.Printf("Data not supported for extension '%s'", f.Extension())
return nil, nil
}
}
func (s *Site) readI18nSources() error {
- i18nSources := []source.Input{&source.Filesystem{Base: s.absI18nDir()}}
+ i18nSources := []source.Input{source.NewFilesystem(s.Fs, s.absI18nDir())}
- themeI18nDir, err := helpers.GetThemeI18nDirPath()
+ themeI18nDir, err := s.PathSpec.GetThemeI18nDirPath()
if err == nil {
- i18nSources = []source.Input{&source.Filesystem{Base: themeI18nDir}, i18nSources[0]}
+ i18nSources = []source.Input{source.NewFilesystem(s.Fs, themeI18nDir), i18nSources[0]}
}
- if err = loadI18n(i18nSources); err != nil {
+ if err = s.loadI18n(i18nSources); err != nil {
return err
}
@@ -763,12 +788,12 @@ func (s *Site) readI18nSources() error {
func (s *Site) readDataFromSourceFS() error {
dataSources := make([]source.Input, 0, 2)
- dataSources = append(dataSources, &source.Filesystem{Base: s.absDataDir()})
+ dataSources = append(dataSources, source.NewFilesystem(s.Fs, s.absDataDir()))
// have to be last - duplicate keys in earlier entries will win
- themeDataDir, err := helpers.GetThemeDataDirPath()
+ themeDataDir, err := s.PathSpec.GetThemeDataDirPath()
if err == nil {
- dataSources = append(dataSources, &source.Filesystem{Base: themeDataDir})
+ dataSources = append(dataSources, source.NewFilesystem(s.Fs, themeDataDir))
}
err = s.loadData(dataSources)
@@ -781,10 +806,7 @@ func (s *Site) process(config BuildCfg) (err error) {
if err = s.initialize(); err != nil {
return
}
-
- s.prepTemplates(config.withTemplate)
- s.owner.tmpl.PrintErrors()
- s.timerStep("initialize & template prep")
+ s.timerStep("initialize")
if err = s.readDataFromSourceFS(); err != nil {
return
@@ -817,7 +839,6 @@ func (s *Site) setCurrentLanguageConfig() error {
viper.Set("currentContentLanguage", s.Language)
// Cache the current config.
helpers.InitConfigProviderForCurrentContentLanguage()
- s.Info.pathSpec = helpers.CurrentPathSpec()
return tpl.SetTranslateLang(s.Language)
}
@@ -873,7 +894,7 @@ func (s *Site) initialize() (err error) {
// May be supplied in tests.
if s.Source != nil && len(s.Source.Files()) > 0 {
- s.log.DEBUG.Println("initialize: Source is already set")
+ s.Log.DEBUG.Println("initialize: Source is already set")
return
}
@@ -883,10 +904,7 @@ func (s *Site) initialize() (err error) {
staticDir := helpers.AbsPathify(viper.GetString("staticDir") + "/")
- s.Source = &source.Filesystem{
- AvoidPaths: []string{staticDir},
- Base: s.absContentDir(),
- }
+ s.Source = source.NewFilesystem(s.Fs, s.absContentDir(), staticDir)
return
}
@@ -897,7 +915,7 @@ func (s *SiteInfo) HomeAbsURL() string {
if s.IsMultiLingual() {
base = s.Language.Lang
}
- return s.pathSpec.AbsURL(base, false)
+ return s.owner.AbsURL(base, false)
}
// SitemapAbsURL is a convenience method giving the absolute URL to the sitemap.
@@ -966,7 +984,7 @@ func (s *Site) initializeSiteInfo() {
Permalinks: permalinks,
Data: &s.Data,
owner: s.owner,
- pathSpec: helpers.NewPathSpecFromConfig(lang),
+ s: s,
}
s.Info.RSSLink = s.Info.permalinkStr(lang.GetString("rssURI"))
@@ -1081,11 +1099,11 @@ func (s *Site) getRealDir(base, path string) string {
return base
}
- realDir, err := helpers.GetRealPath(hugofs.Source(), base)
+ realDir, err := helpers.GetRealPath(s.Fs.Source, base)
if err != nil {
if !os.IsNotExist(err) {
- s.log.ERROR.Printf("Failed to get real path for %s: %s", path, err)
+ s.Log.ERROR.Printf("Failed to get real path for %s: %s", path, err)
}
return ""
}
@@ -1102,7 +1120,7 @@ func (s *Site) absPublishDir() string {
}
func (s *Site) checkDirectories() (err error) {
- if b, _ := helpers.DirExists(s.absContentDir(), hugofs.Source()); !b {
+ if b, _ := helpers.DirExists(s.absContentDir(), s.Fs.Source); !b {
return errors.New("No source directory found, expecting to find it at " + s.absContentDir())
}
return
@@ -1110,10 +1128,10 @@ func (s *Site) checkDirectories() (err error) {
// reReadFile resets file to be read from disk again
func (s *Site) reReadFile(absFilePath string) (*source.File, error) {
- s.log.INFO.Println("rereading", absFilePath)
+ s.Log.INFO.Println("rereading", absFilePath)
var file *source.File
- reader, err := source.NewLazyFileReader(hugofs.Source(), absFilePath)
+ reader, err := source.NewLazyFileReader(s.Fs.Source, absFilePath)
if err != nil {
return nil, err
}
@@ -1131,7 +1149,7 @@ func (s *Site) readPagesFromSource() chan error {
panic(fmt.Sprintf("s.Source not set %s", s.absContentDir()))
}
- s.log.DEBUG.Printf("Read %d pages from source", len(s.Source.Files()))
+ s.Log.DEBUG.Printf("Read %d pages from source", len(s.Source.Files()))
errs := make(chan error)
if len(s.Source.Files()) < 1 {
@@ -1231,7 +1249,7 @@ func readSourceFile(s *Site, file *source.File, results chan<- HandledResult) {
if h != nil {
h.Read(file, s, results)
} else {
- s.log.ERROR.Println("Unsupported File Type", file.Path())
+ s.Log.ERROR.Println("Unsupported File Type", file.Path())
}
}
@@ -1372,17 +1390,17 @@ func (s *Site) getMenusFromConfig() Menus {
for name, menu := range menus {
m, err := cast.ToSliceE(menu)
if err != nil {
- s.log.ERROR.Printf("unable to process menus in site config\n")
- s.log.ERROR.Println(err)
+ s.Log.ERROR.Printf("unable to process menus in site config\n")
+ s.Log.ERROR.Println(err)
} else {
for _, entry := range m {
- s.log.DEBUG.Printf("found menu: %q, in site config\n", name)
+ s.Log.DEBUG.Printf("found menu: %q, in site config\n", name)
menuEntry := MenuEntry{Menu: name}
ime, err := cast.ToStringMapE(entry)
if err != nil {
- s.log.ERROR.Printf("unable to process menus in site config\n")
- s.log.ERROR.Println(err)
+ s.Log.ERROR.Printf("unable to process menus in site config\n")
+ s.Log.ERROR.Println(err)
}
menuEntry.marshallMap(ime)
@@ -1407,7 +1425,7 @@ func (s *SiteInfo) createNodeMenuEntryURL(in string) string {
}
// make it match the nodes
menuEntryURL := in
- menuEntryURL = helpers.SanitizeURLKeepTrailingSlash(s.pathSpec.URLize(menuEntryURL))
+ menuEntryURL = helpers.SanitizeURLKeepTrailingSlash(s.s.PathSpec.URLize(menuEntryURL))
if !s.canonifyURLs {
menuEntryURL = helpers.AddContextRoot(string(s.BaseURL), menuEntryURL)
}
@@ -1454,7 +1472,7 @@ func (s *Site) assembleMenus() {
for name, me := range p.Menus() {
if _, ok := flat[twoD{name, me.KeyName()}]; ok {
- s.log.ERROR.Printf("Two or more menu items have the same name/identifier in Menu %q: %q.\nRename or set an unique identifier.\n", name, me.KeyName())
+ s.Log.ERROR.Printf("Two or more menu items have the same name/identifier in Menu %q: %q.\nRename or set an unique identifier.\n", name, me.KeyName())
continue
}
flat[twoD{name, me.KeyName()}] = me
@@ -1490,6 +1508,13 @@ func (s *Site) assembleMenus() {
}
}
+func (s *Site) getTaxonomyKey(key string) string {
+ if s.Info.preserveTaxonomyNames {
+ // Keep as is
+ return key
+ }
+ return s.PathSpec.MakePathSanitized(key)
+}
func (s *Site) assembleTaxonomies() {
s.Taxonomies = make(TaxonomyList)
s.taxonomiesPluralSingular = make(map[string]string)
@@ -1497,7 +1522,7 @@ func (s *Site) assembleTaxonomies() {
taxonomies := s.Language.GetStringMapString("taxonomies")
- s.log.INFO.Printf("found taxonomies: %#v\n", taxonomies)
+ s.Log.INFO.Printf("found taxonomies: %#v\n", taxonomies)
for singular, plural := range taxonomies {
s.Taxonomies[plural] = make(Taxonomy)
@@ -1513,21 +1538,21 @@ func (s *Site) assembleTaxonomies() {
if v, ok := vals.([]string); ok {
for _, idx := range v {
x := WeightedPage{weight.(int), p}
- s.Taxonomies[plural].add(idx, x, s.Info.preserveTaxonomyNames)
+ s.Taxonomies[plural].add(s.getTaxonomyKey(idx), x)
if s.Info.preserveTaxonomyNames {
// Need to track the original
- s.taxonomiesOrigKey[fmt.Sprintf("%s-%s", plural, kp(idx))] = idx
+ s.taxonomiesOrigKey[fmt.Sprintf("%s-%s", plural, s.PathSpec.MakePathSanitized(idx))] = idx
}
}
} else if v, ok := vals.(string); ok {
x := WeightedPage{weight.(int), p}
- s.Taxonomies[plural].add(v, x, s.Info.preserveTaxonomyNames)
+ s.Taxonomies[plural].add(s.getTaxonomyKey(v), x)
if s.Info.preserveTaxonomyNames {
// Need to track the original
- s.taxonomiesOrigKey[fmt.Sprintf("%s-%s", plural, kp(v))] = v
+ s.taxonomiesOrigKey[fmt.Sprintf("%s-%s", plural, s.PathSpec.MakePathSanitized(v))] = v
}
} else {
- s.log.ERROR.Printf("Invalid %s in %s\n", plural, p.File.Path())
+ s.Log.ERROR.Printf("Invalid %s in %s\n", plural, p.File.Path())
}
}
}
@@ -1564,7 +1589,7 @@ func (s *Site) assembleSections() {
sectionPages := s.findPagesByKind(KindSection)
for i, p := range regularPages {
- s.Sections.add(p.Section(), WeightedPage{regularPages[i].Weight, regularPages[i]}, s.Info.preserveTaxonomyNames)
+ s.Sections.add(s.getTaxonomyKey(p.Section()), WeightedPage{regularPages[i].Weight, regularPages[i]})
}
// Add sections without regular pages, but with a content page
@@ -1665,18 +1690,18 @@ func (s *Site) appendThemeTemplates(in []string) []string {
// Stats prints Hugo builds stats to the console.
// This is what you see after a successful hugo build.
func (s *Site) Stats() {
- s.log.FEEDBACK.Printf("Built site for language %s:\n", s.Language.Lang)
- s.log.FEEDBACK.Println(s.draftStats())
- s.log.FEEDBACK.Println(s.futureStats())
- s.log.FEEDBACK.Println(s.expiredStats())
- s.log.FEEDBACK.Printf("%d regular pages created\n", len(s.RegularPages))
- s.log.FEEDBACK.Printf("%d other pages created\n", (len(s.Pages) - len(s.RegularPages)))
- s.log.FEEDBACK.Printf("%d non-page files copied\n", len(s.Files))
- s.log.FEEDBACK.Printf("%d paginator pages created\n", s.Info.paginationPageCount)
+ s.Log.FEEDBACK.Printf("Built site for language %s:\n", s.Language.Lang)
+ s.Log.FEEDBACK.Println(s.draftStats())
+ s.Log.FEEDBACK.Println(s.futureStats())
+ s.Log.FEEDBACK.Println(s.expiredStats())
+ s.Log.FEEDBACK.Printf("%d regular pages created\n", len(s.RegularPages))
+ s.Log.FEEDBACK.Printf("%d other pages created\n", (len(s.Pages) - len(s.RegularPages)))
+ s.Log.FEEDBACK.Printf("%d non-page files copied\n", len(s.Files))
+ s.Log.FEEDBACK.Printf("%d paginator pages created\n", s.Info.paginationPageCount)
taxonomies := s.Language.GetStringMapString("taxonomies")
for _, pl := range taxonomies {
- s.log.FEEDBACK.Printf("%d %s created\n", len(s.Taxonomies[pl]), pl)
+ s.Log.FEEDBACK.Printf("%d %s created\n", len(s.Taxonomies[pl]), pl)
}
}
@@ -1701,11 +1726,11 @@ func (s *SiteInfo) permalink(plink string) string {
func (s *SiteInfo) permalinkStr(plink string) string {
return helpers.MakePermalink(
viper.GetString("baseURL"),
- s.pathSpec.URLizeAndPrep(plink)).String()
+ s.s.PathSpec.URLizeAndPrep(plink)).String()
}
func (s *Site) renderAndWriteXML(name string, dest string, d interface{}, layouts ...string) error {
- s.log.DEBUG.Printf("Render XML for %q to %q", name, dest)
+ s.Log.DEBUG.Printf("Render XML for %q to %q", name, dest)
renderBuffer := bp.GetBuffer()
defer bp.PutBuffer(renderBuffer)
renderBuffer.WriteString("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\" ?>\n")
@@ -1797,7 +1822,7 @@ func (s *Site) renderAndWritePage(name string, dest string, d interface{}, layou
if outBuffer.Len() == 0 {
- s.log.WARN.Printf("%s is rendered empty\n", dest)
+ s.Log.WARN.Printf("%s is rendered empty\n", dest)
if dest == "/" {
debugAddend := ""
if !viper.GetBool("verbose") {
@@ -1829,7 +1854,8 @@ Your rendered home page is blank: /index.html is zero-length
func (s *Site) renderForLayouts(name string, d interface{}, w io.Writer, layouts ...string) error {
layout, found := s.findFirstLayout(layouts...)
if !found {
- s.log.WARN.Printf("Unable to locate layout for %s: %s\n", name, layouts)
+ s.Log.WARN.Printf("[%s] Unable to locate layout for %s: %s\n", s.Language.Lang, name, layouts)
+
return nil
}
@@ -1850,7 +1876,7 @@ func (s *Site) renderForLayouts(name string, d interface{}, w io.Writer, layouts
func (s *Site) findFirstLayout(layouts ...string) (string, bool) {
for _, layout := range layouts {
- if s.owner.tmpl.Lookup(layout) != nil {
+ if s.Tmpl.Lookup(layout) != nil {
return layout, true
}
}
@@ -1860,7 +1886,7 @@ func (s *Site) findFirstLayout(layouts ...string) (string, bool) {
func (s *Site) renderThing(d interface{}, layout string, w io.Writer) error {
// If the template doesn't exist, then return, but leave the Writer open
- if templ := s.owner.tmpl.Lookup(layout); templ != nil {
+ if templ := s.Tmpl.Lookup(layout); templ != nil {
return templ.Execute(w, d)
}
return fmt.Errorf("Layout not found: %s", layout)
@@ -1893,6 +1919,9 @@ func (s *Site) languageAliasTarget() target.AliasPublisher {
}
func (s *Site) initTargetList() {
+ if s.Fs == nil {
+ panic("Must have Fs")
+ }
s.targetListInit.Do(func() {
langDir := ""
if s.Language.Lang != s.Info.multilingual.DefaultLang.Lang || s.Info.defaultContentLanguageInSubdir {
@@ -1900,6 +1929,7 @@ func (s *Site) initTargetList() {
}
if s.targets.page == nil {
s.targets.page = &target.PagePub{
+ Fs: s.Fs,
PublishDir: s.absPublishDir(),
UglyURLs: viper.GetBool("uglyURLs"),
LangDir: langDir,
@@ -1907,6 +1937,7 @@ func (s *Site) initTargetList() {
}
if s.targets.pageUgly == nil {
s.targets.pageUgly = &target.PagePub{
+ Fs: s.Fs,
PublishDir: s.absPublishDir(),
UglyURLs: true,
LangDir: langDir,
@@ -1914,17 +1945,20 @@ func (s *Site) initTargetList() {
}
if s.targets.file == nil {
s.targets.file = &target.Filesystem{
+ Fs: s.Fs,
PublishDir: s.absPublishDir(),
}
}
if s.targets.alias == nil {
s.targets.alias = &target.HTMLRedirectAlias{
+ Fs: s.Fs,
PublishDir: s.absPublishDir(),
- Templates: s.owner.tmpl.Lookup("alias.html"),
+ Templates: s.Tmpl.Lookup("alias.html"),
}
}
if s.targets.languageAlias == nil {
s.targets.languageAlias = &target.HTMLRedirectAlias{
+ Fs: s.Fs,
PublishDir: s.absPublishDir(),
AllowRoot: true,
}
@@ -1933,12 +1967,12 @@ func (s *Site) initTargetList() {
}
func (s *Site) writeDestFile(path string, reader io.Reader) (err error) {
- s.log.DEBUG.Println("creating file:", path)
+ s.Log.DEBUG.Println("creating file:", path)
return s.fileTarget().Publish(path, reader)
}
func (s *Site) writeDestPage(path string, publisher target.Publisher, reader io.Reader) (err error) {
- s.log.DEBUG.Println("creating page:", path)
+ s.Log.DEBUG.Println("creating page:", path)
return publisher.Publish(path, reader)
}
@@ -1956,11 +1990,11 @@ func (s *Site) publishDestAlias(aliasPublisher target.AliasPublisher, path, perm
}
permalink, err = helpers.GetRelativePath(permalink, path)
if err != nil {
- s.log.ERROR.Println("Failed to make a RelativeURL alias:", path, "redirecting to", permalink)
+ s.Log.ERROR.Println("Failed to make a RelativeURL alias:", path, "redirecting to", permalink)
}
permalink = filepath.ToSlash(permalink)
}
- s.log.DEBUG.Println("creating alias:", path, "redirecting to", permalink)
+ s.Log.DEBUG.Println("creating alias:", path, "redirecting to", permalink)
return aliasPublisher.Publish(path, permalink, p)
}
@@ -2051,7 +2085,7 @@ func (s *Site) newHomePage() *Page {
}
func (s *Site) setPageURLs(p *Page, in string) {
- p.URLPath.URL = s.Info.pathSpec.URLizeAndPrep(in)
+ p.URLPath.URL = s.PathSpec.URLizeAndPrep(in)
p.URLPath.Permalink = s.Info.permalink(p.URLPath.URL)
p.RSSLink = template.HTML(s.Info.permalink(in + ".xml"))
}
@@ -2063,7 +2097,7 @@ func (s *Site) newTaxonomyPage(plural, key string) *Page {
p.sections = []string{plural, key}
if s.Info.preserveTaxonomyNames {
- key = s.Info.pathSpec.MakePathSanitized(key)
+ key = s.PathSpec.MakePathSanitized(key)
}
if s.Info.preserveTaxonomyNames {
diff --git a/hugolib/siteJSONEncode_test.go b/hugolib/siteJSONEncode_test.go
index 170db4b4d..1218bfd34 100644
--- a/hugolib/siteJSONEncode_test.go
+++ b/hugolib/siteJSONEncode_test.go
@@ -16,6 +16,11 @@ package hugolib
import (
"encoding/json"
"testing"
+
+ "path/filepath"
+
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
)
// Issue #1123
@@ -23,9 +28,15 @@ import (
// May be smart to run with: -timeout 4000ms
func TestEncodePage(t *testing.T) {
+ fs := hugofs.NewMem()
+
// borrowed from menu_test.go
- s := createTestSite(menuPageSources)
- testSiteSetup(s, t)
+ for _, src := range menuPageSources {
+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
+
+ }
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
_, err := json.Marshal(s)
check(t, err)
diff --git a/hugolib/site_render.go b/hugolib/site_render.go
index b6a9cae54..84df78c1c 100644
--- a/hugolib/site_render.go
+++ b/hugolib/site_render.go
@@ -66,7 +66,7 @@ func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.Wa
for p := range pages {
targetPath := p.TargetPath()
layouts := p.layouts()
- s.log.DEBUG.Printf("Render %s to %q with layouts %q", p.Kind, targetPath, layouts)
+ s.Log.DEBUG.Printf("Render %s to %q with layouts %q", p.Kind, targetPath, layouts)
if err := s.renderAndWritePage("page "+p.FullFilePath(), targetPath, p, s.appendThemeTemplates(layouts)...); err != nil {
results <- err
@@ -88,7 +88,7 @@ func pageRenderer(s *Site, pages <-chan *Page, results chan<- error, wg *sync.Wa
// renderPaginator must be run after the owning Page has been rendered.
func (s *Site) renderPaginator(p *Page) error {
if p.paginator != nil {
- s.log.DEBUG.Printf("Render paginator for page %q", p.Path())
+ s.Log.DEBUG.Printf("Render paginator for page %q", p.Path())
paginatePath := helpers.Config().GetString("paginatePath")
// write alias for page 1
@@ -267,14 +267,14 @@ func (s *Site) renderAliases() error {
if s.owner.multilingual.enabled() {
mainLang := s.owner.multilingual.DefaultLang.Lang
if s.Info.defaultContentLanguageInSubdir {
- mainLangURL := s.Info.pathSpec.AbsURL(mainLang, false)
- s.log.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
+ mainLangURL := s.PathSpec.AbsURL(mainLang, false)
+ s.Log.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
if err := s.publishDestAlias(s.languageAliasTarget(), "/", mainLangURL, nil); err != nil {
return err
}
} else {
- mainLangURL := s.Info.pathSpec.AbsURL("", false)
- s.log.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
+ mainLangURL := s.PathSpec.AbsURL("", false)
+ s.Log.DEBUG.Printf("Write redirect to main language %s: %s", mainLang, mainLangURL)
if err := s.publishDestAlias(s.languageAliasTarget(), mainLang, mainLangURL, nil); err != nil {
return err
}
diff --git a/hugolib/site_test.go b/hugolib/site_test.go
index 342cae615..3f1a8b066 100644
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -18,16 +18,15 @@ import (
"path/filepath"
"strings"
"testing"
- "time"
"github.com/bep/inflect"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
- "github.com/spf13/hugo/target"
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -47,37 +46,6 @@ func init() {
testMode = true
}
-// Issue #1797
-func TestReadPagesFromSourceWithEmptySource(t *testing.T) {
- testCommonResetState()
-
- viper.Set("defaultExtension", "html")
- viper.Set("verbose", true)
- viper.Set("baseURL", "http://auth/bub")
-
- sources := []source.ByteSource{}
-
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: sources},
- targets: targetList{page: &target.PagePub{UglyURLs: true}},
- }
-
- var err error
- d := time.Second * 2
- ticker := time.NewTicker(d)
- select {
- case err = <-s.readPagesFromSource():
- break
- case <-ticker.C:
- err = fmt.Errorf("ReadPagesFromSource() never returns in %s", d.String())
- }
- ticker.Stop()
- if err != nil {
- t.Fatalf("Unable to read source: %s", err)
- }
-}
-
func pageMust(p *Page, err error) *Page {
if err != nil {
panic(err)
@@ -86,11 +54,12 @@ func pageMust(p *Page, err error) *Page {
}
func TestDegenerateRenderThingMissingTemplate(t *testing.T) {
- s := newSiteFromSources("content/a/file.md", pageSimpleTitle)
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ fs := hugofs.NewMem()
+
+ writeSource(t, fs, filepath.Join("content", "a", "file.md"), pageSimpleTitle)
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
require.Len(t, s.RegularPages, 1)
@@ -104,12 +73,15 @@ func TestDegenerateRenderThingMissingTemplate(t *testing.T) {
func TestRenderWithInvalidTemplate(t *testing.T) {
- s := NewSiteDefaultLang()
- if err := buildAndRenderSite(s, "missing", templateMissingFunc); err != nil {
- t.Fatalf("Got build error: %s", err)
- }
+ fs := hugofs.NewMem()
+
+ writeSource(t, fs, filepath.Join("content", "foo.md"), "foo")
+
+ withTemplate := createWithTemplateFromNameValues("missing", templateMissingFunc)
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs, WithTemplate: withTemplate}, BuildCfg{})
- errCount := s.log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)
+ errCount := s.Log.LogCountForLevelsGreaterThanorEqualTo(jww.LevelError)
// TODO(bep) globals clean up the template error handling
// The template errors are stored in a slice etc. so we get 4 log entries
@@ -122,7 +94,6 @@ func TestRenderWithInvalidTemplate(t *testing.T) {
func TestDraftAndFutureRender(t *testing.T) {
testCommonResetState()
- hugofs.InitMemFs()
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc1.md"), Content: []byte("---\ntitle: doc1\ndraft: true\npublishdate: \"2414-05-29\"\n---\n# doc1\n*some content*")},
{Name: filepath.FromSlash("sect/doc2.md"), Content: []byte("---\ntitle: doc2\ndraft: true\npublishdate: \"2012-05-29\"\n---\n# doc2\n*some content*")},
@@ -131,17 +102,14 @@ func TestDraftAndFutureRender(t *testing.T) {
}
siteSetup := func(t *testing.T) *Site {
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: sources},
- Language: helpers.NewDefaultLanguage(),
- }
+ fs := hugofs.NewMem()
+
+ for _, src := range sources {
+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
}
- return s
+ return buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
}
viper.Set("baseURL", "http://auth/bub")
@@ -183,24 +151,20 @@ func TestDraftAndFutureRender(t *testing.T) {
func TestFutureExpirationRender(t *testing.T) {
testCommonResetState()
- hugofs.InitMemFs()
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc3.md"), Content: []byte("---\ntitle: doc1\nexpirydate: \"2400-05-29\"\n---\n# doc1\n*some content*")},
{Name: filepath.FromSlash("sect/doc4.md"), Content: []byte("---\ntitle: doc2\nexpirydate: \"2000-05-29\"\n---\n# doc2\n*some content*")},
}
siteSetup := func(t *testing.T) *Site {
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: sources},
- Language: helpers.NewDefaultLanguage(),
- }
+ fs := hugofs.NewMem()
+
+ for _, src := range sources {
+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
}
- return s
+ return buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
}
viper.Set("baseURL", "http://auth/bub")
@@ -282,17 +246,19 @@ THE END.`, refShortcode)),
},
}
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: sources},
- targets: targetList{page: &target.PagePub{UglyURLs: uglyURLs}},
- Language: helpers.NewDefaultLanguage(),
- }
+ fs := hugofs.NewMem()
- if err := buildAndRenderSite(s, "_default/single.html", "{{.Content}}"); err != nil {
- t.Fatalf("Failed to build site: %s", err)
+ for _, src := range sources {
+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
}
+ s := buildSingleSite(
+ t,
+ deps.DepsCfg{
+ Fs: fs,
+ WithTemplate: createWithTemplateFromNameValues("_default/single.html", "{{.Content}}")},
+ BuildCfg{})
+
if len(s.RegularPages) != 3 {
t.Fatalf("Expected 3 got %d pages", len(s.AllPages))
}
@@ -301,23 +267,14 @@ THE END.`, refShortcode)),
doc string
expected string
}{
- {filepath.FromSlash(fmt.Sprintf("sect/doc1%s", expectedPathSuffix)), fmt.Sprintf("<p>Ref 2: %s/sect/doc2%s</p>\n", expectedBase, expectedURLSuffix)},
- {filepath.FromSlash(fmt.Sprintf("sect/doc2%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong></p>\n\n%s/sect/doc1%s\n\n<p>THE END.</p>\n", expectedBase, expectedURLSuffix)},
- {filepath.FromSlash(fmt.Sprintf("sect/doc3%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong>%s/sect/doc3%s.</p>\n", expectedBase, expectedURLSuffix)},
+ {filepath.FromSlash(fmt.Sprintf("public/sect/doc1%s", expectedPathSuffix)), fmt.Sprintf("<p>Ref 2: %s/sect/doc2%s</p>\n", expectedBase, expectedURLSuffix)},
+ {filepath.FromSlash(fmt.Sprintf("public/sect/doc2%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong></p>\n\n%s/sect/doc1%s\n\n<p>THE END.</p>\n", expectedBase, expectedURLSuffix)},
+ {filepath.FromSlash(fmt.Sprintf("public/sect/doc3%s", expectedPathSuffix)), fmt.Sprintf("<p><strong>Ref 1:</strong>%s/sect/doc3%s.</p>\n", expectedBase, expectedURLSuffix)},
}
for _, test := range tests {
- file, err := hugofs.Destination().Open(test.doc)
-
- if err != nil {
- t.Fatalf("Did not find %s in target: %s", test.doc, err)
- }
-
- content := helpers.ReaderToString(file)
+ assertFileContent(t, fs, test.doc, true, test.expected)
- if content != test.expected {
- t.Fatalf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, content)
- }
}
}
@@ -350,22 +307,20 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
{Name: filepath.FromSlash("sect/doc2.md"), Content: []byte("---\nurl: /ugly.html\nmarkup: markdown\n---\n# title\ndoc2 *content*")},
}
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: sources},
- targets: targetList{page: &target.PagePub{UglyURLs: uglyURLs, PublishDir: "public"}},
- Language: helpers.NewDefaultLanguage(),
- }
+ fs := hugofs.NewMem()
- if err := buildAndRenderSite(s,
- "index.html", "Home Sweet {{ if.IsHome }}Home{{ end }}.",
- "_default/single.html", "{{.Content}}{{ if.IsHome }}This is not home!{{ end }}",
- "404.html", "Page Not Found.{{ if.IsHome }}This is not home!{{ end }}",
- "rss.xml", "<root>RSS</root>",
- "sitemap.xml", "<root>SITEMAP</root>"); err != nil {
- t.Fatalf("Failed to build site: %s", err)
+ for _, src := range sources {
+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
}
+ writeSource(t, fs, filepath.Join("layouts", "index.html"), "Home Sweet {{ if.IsHome }}Home{{ end }}.")
+ writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}{{ if.IsHome }}This is not home!{{ end }}")
+ writeSource(t, fs, filepath.Join("layouts", "404.html"), "Page Not Found.{{ if.IsHome }}This is not home!{{ end }}")
+ writeSource(t, fs, filepath.Join("layouts", "rss.xml"), "<root>RSS</root>")
+ writeSource(t, fs, filepath.Join("layouts", "sitemap.xml"), "<root>SITEMAP</root>")
+
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+
var expectedPagePath string
if uglyURLs {
expectedPagePath = "public/sect/doc1.html"
@@ -391,7 +346,7 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
}
for _, test := range tests {
- content := readDestination(t, test.doc)
+ content := readDestination(t, fs, test.doc)
if content != test.expected {
t.Errorf("%s content expected:\n%q\ngot:\n%q", test.doc, test.expected, content)
@@ -435,17 +390,16 @@ func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
{Name: filepath.FromSlash("ラーメン/doc3.html"), Content: []byte("doc3")},
}
+ fs := hugofs.NewMem()
+
for _, source := range sources {
- writeSource(t, filepath.Join("content", source.Name), string(source.Content))
+ writeSource(t, fs, filepath.Join("content", source.Name), string(source.Content))
}
- s := NewSiteDefaultLang()
+ writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}")
+ writeSource(t, fs, filepath.Join("layouts", "_default/list.html"), "{{.Title}}")
- if err := buildAndRenderSite(s,
- "_default/single.html", "{{.Content}}",
- "_default/list.html", "{{ .Title }}"); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
tests := []struct {
doc string
@@ -466,14 +420,13 @@ func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
test.expected = inflect.Pluralize(test.expected)
}
- assertFileContent(t, filepath.Join("public", test.doc), true, test.expected)
+ assertFileContent(t, fs, filepath.Join("public", test.doc), true, test.expected)
}
}
func TestSkipRender(t *testing.T) {
testCommonResetState()
- hugofs.InitMemFs()
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc1.html"), Content: []byte("---\nmarkup: markdown\n---\n# title\nsome *content*")},
{Name: filepath.FromSlash("sect/doc2.html"), Content: []byte("<!doctype html><html><body>more content</body></html>")},
@@ -488,37 +441,38 @@ func TestSkipRender(t *testing.T) {
viper.Set("defaultExtension", "html")
viper.Set("verbose", true)
viper.Set("canonifyURLs", true)
+ viper.Set("uglyURLs", true)
viper.Set("baseURL", "http://auth/bub")
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: sources},
- targets: targetList{page: &target.PagePub{UglyURLs: true}},
- Language: helpers.NewDefaultLanguage(),
- }
- if err := buildAndRenderSite(s,
- "_default/single.html", "{{.Content}}",
- "head", "<head><script src=\"script.js\"></script></head>",
- "head_abs", "<head><script src=\"/script.js\"></script></head>"); err != nil {
- t.Fatalf("Failed to build site: %s", err)
+ fs := hugofs.NewMem()
+
+ for _, src := range sources {
+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
+
}
+ writeSource(t, fs, filepath.Join("layouts", "_default/single.html"), "{{.Content}}")
+ writeSource(t, fs, filepath.Join("layouts", "head"), "<head><script src=\"script.js\"></script></head>")
+ writeSource(t, fs, filepath.Join("layouts", "head_abs"), "<head><script src=\"/script.js\"></script></head>")
+
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+
tests := []struct {
doc string
expected string
}{
- {filepath.FromSlash("sect/doc1.html"), "\n\n<h1 id=\"title\">title</h1>\n\n<p>some <em>content</em></p>\n"},
- {filepath.FromSlash("sect/doc2.html"), "<!doctype html><html><body>more content</body></html>"},
- {filepath.FromSlash("sect/doc3.html"), "\n\n<h1 id=\"doc3\">doc3</h1>\n\n<p><em>some</em> content</p>\n"},
- {filepath.FromSlash("sect/doc4.html"), "\n\n<h1 id=\"doc4\">doc4</h1>\n\n<p><em>some content</em></p>\n"},
- {filepath.FromSlash("sect/doc5.html"), "<!doctype html><html><head><script src=\"script.js\"></script></head><body>body5</body></html>"},
- {filepath.FromSlash("sect/doc6.html"), "<!doctype html><html><head><script src=\"http://auth/bub/script.js\"></script></head><body>body5</body></html>"},
- {filepath.FromSlash("doc7.html"), "<html><body>doc7 content</body></html>"},
- {filepath.FromSlash("sect/doc8.html"), "\n\n<h1 id=\"title\">title</h1>\n\n<p>some <em>content</em></p>\n"},
+ {filepath.FromSlash("public/sect/doc1.html"), "\n\n<h1 id=\"title\">title</h1>\n\n<p>some <em>content</em></p>\n"},
+ {filepath.FromSlash("public/sect/doc2.html"), "<!doctype html><html><body>more content</body></html>"},
+ {filepath.FromSlash("public/sect/doc3.html"), "\n\n<h1 id=\"doc3\">doc3</h1>\n\n<p><em>some</em> content</p>\n"},
+ {filepath.FromSlash("public/sect/doc4.html"), "\n\n<h1 id=\"doc4\">doc4</h1>\n\n<p><em>some content</em></p>\n"},
+ {filepath.FromSlash("public/sect/doc5.html"), "<!doctype html><html><head><script src=\"script.js\"></script></head><body>body5</body></html>"},
+ {filepath.FromSlash("public/sect/doc6.html"), "<!doctype html><html><head><script src=\"http://auth/bub/script.js\"></script></head><body>body5</body></html>"},
+ {filepath.FromSlash("public/doc7.html"), "<html><body>doc7 content</body></html>"},
+ {filepath.FromSlash("public/sect/doc8.html"), "\n\n<h1 id=\"title\">title</h1>\n\n<p>some <em>content</em></p>\n"},
}
for _, test := range tests {
- file, err := hugofs.Destination().Open(test.doc)
+ file, err := fs.Destination.Open(test.doc)
if err != nil {
t.Fatalf("Did not find %s in target.", test.doc)
}
@@ -535,8 +489,8 @@ func TestAbsURLify(t *testing.T) {
testCommonResetState()
viper.Set("defaultExtension", "html")
+ viper.Set("uglyURLs", true)
- hugofs.InitMemFs()
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc1.html"), Content: []byte("<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>")},
{Name: filepath.FromSlash("blue/doc2.html"), Content: []byte("---\nf: t\n---\n<!doctype html><html><body>more content</body></html>")},
@@ -545,34 +499,27 @@ func TestAbsURLify(t *testing.T) {
for _, canonify := range []bool{true, false} {
viper.Set("canonifyURLs", canonify)
viper.Set("baseURL", baseURL)
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: sources},
- targets: targetList{page: &target.PagePub{UglyURLs: true}},
- Language: helpers.NewDefaultLanguage(),
- }
- t.Logf("Rendering with baseURL %q and canonifyURLs set %v", viper.GetString("baseURL"), canonify)
- if err := buildAndRenderSite(s, "blue/single.html", templateWithURLAbs); err != nil {
- t.Fatalf("Failed to build site: %s", err)
+ fs := hugofs.NewMem()
+
+ for _, src := range sources {
+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
+
}
+ writeSource(t, fs, filepath.Join("layouts", "blue/single.html"), templateWithURLAbs)
+
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+
tests := []struct {
file, expected string
}{
- {"blue/doc2.html", "<a href=\"%s/foobar.jpg\">Going</a>"},
- {"sect/doc1.html", "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"},
+ {"public/blue/doc2.html", "<a href=\"%s/foobar.jpg\">Going</a>"},
+ {"public/sect/doc1.html", "<!doctype html><html><head></head><body><a href=\"#frag1\">link</a></body></html>"},
}
for _, test := range tests {
- file, err := hugofs.Destination().Open(filepath.FromSlash(test.file))
- if err != nil {
- t.Fatalf("Unable to locate rendered content: %s", test.file)
- }
-
- content := helpers.ReaderToString(file)
-
expected := test.expected
if strings.Contains(expected, "%s") {
@@ -583,9 +530,8 @@ func TestAbsURLify(t *testing.T) {
expected = strings.Replace(expected, baseURL, "", -1)
}
- if content != expected {
- t.Errorf("AbsURLify with baseURL %q content expected:\n%q\ngot\n%q", baseURL, expected, content)
- }
+ assertFileContent(t, fs, test.file, true, expected)
+
}
}
}
@@ -639,19 +585,17 @@ var weightedSources = []source.ByteSource{
func TestOrderedPages(t *testing.T) {
testCommonResetState()
- hugofs.InitMemFs()
-
viper.Set("baseURL", "http://auth/bub")
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: weightedSources},
- Language: helpers.NewDefaultLanguage(),
- }
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to process site: %s", err)
+ fs := hugofs.NewMem()
+
+ for _, src := range weightedSources {
+ writeSource(t, fs, filepath.Join("content", src.Name), string(src.Content))
+
}
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{SkipRender: true})
+
if s.Sections["sect"][0].Weight != 2 || s.Sections["sect"][3].Weight != 6 {
t.Errorf("Pages in unexpected order. First should be '%d', got '%d'", 2, s.Sections["sect"][0].Weight)
}
@@ -709,23 +653,17 @@ func TestGroupedPages(t *testing.T) {
}
}()
- hugofs.InitMemFs()
-
viper.Set("baseURL", "http://auth/bub")
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: groupedSources},
- Language: helpers.NewDefaultLanguage(),
- }
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ fs := hugofs.NewMem()
+ writeSourcesToSource(t, "content", fs, groupedSources...)
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
rbysection, err := s.RegularPages.GroupBy("Section", "desc")
if err != nil {
t.Fatalf("Unable to make PageGroup array: %s", err)
}
+
if rbysection[0].Key != "sect3" {
t.Errorf("PageGroup array in unexpected order. First group key should be '%s', got '%s'", "sect3", rbysection[0].Key)
}
@@ -885,7 +823,6 @@ Front Matter with weighted tags and categories`)
func TestWeightedTaxonomies(t *testing.T) {
testCommonResetState()
- hugofs.InitMemFs()
sources := []source.ByteSource{
{Name: filepath.FromSlash("sect/doc1.md"), Content: pageWithWeightedTaxonomies2},
{Name: filepath.FromSlash("sect/doc2.md"), Content: pageWithWeightedTaxonomies1},
@@ -898,15 +835,10 @@ func TestWeightedTaxonomies(t *testing.T) {
viper.Set("baseURL", "http://auth/bub")
viper.Set("taxonomies", taxonomies)
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: sources},
- Language: helpers.NewDefaultLanguage(),
- }
- if err := buildSiteSkipRender(s); err != nil {
- t.Fatalf("Failed to process site: %s", err)
- }
+ fs := hugofs.NewMem()
+ writeSourcesToSource(t, "content", fs, sources...)
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
if s.Taxonomies["tags"]["a"][0].Page.Title != "foo" {
t.Errorf("Pages in unexpected order, 'foo' expected first, got '%v'", s.Taxonomies["tags"]["a"][0].Page.Title)
@@ -935,7 +867,6 @@ func findPage(site *Site, f string) *Page {
}
func setupLinkingMockSite(t *testing.T) *Site {
- hugofs.InitMemFs()
sources := []source.ByteSource{
{Name: filepath.FromSlash("index.md"), Content: []byte("")},
{Name: filepath.FromSlash("rootfile.md"), Content: []byte("")},
@@ -968,17 +899,10 @@ func setupLinkingMockSite(t *testing.T) *Site {
map[string]interface{}{
"sourceRelativeLinksProjectFolder": "/docs"})
- site := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: sources},
- Language: helpers.NewDefaultLanguage(),
- }
-
- if err := buildSiteSkipRender(site); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ fs := hugofs.NewMem()
+ writeSourcesToSource(t, "content", fs, sources...)
+ return buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
- return site
}
func TestRefLinking(t *testing.T) {
diff --git a/hugolib/site_url_test.go b/hugolib/site_url_test.go
index 99d04460d..5706b9fb5 100644
--- a/hugolib/site_url_test.go
+++ b/hugolib/site_url_test.go
@@ -17,13 +17,13 @@ import (
"path/filepath"
"testing"
- "github.com/spf13/hugo/helpers"
-
"html/template"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/hugofs"
"github.com/spf13/hugo/source"
"github.com/spf13/viper"
+ "github.com/stretchr/testify/require"
)
const slugDoc1 = "---\ntitle: slug doc 1\nslug: slug-doc-1\naliases:\n - sd1/foo/\n - sd2\n - sd3/\n - sd4.html\n---\nslug doc 1 content\n"
@@ -62,7 +62,8 @@ func TestShouldNotAddTrailingSlashToBaseURL(t *testing.T) {
{"http://base.com", "http://base.com"}} {
viper.Set("baseURL", this.in)
- s := NewSiteDefaultLang()
+ s, err := NewSiteDefaultLang()
+ require.NoError(t, err)
s.initializeSiteInfo()
if s.Info.BaseURL != template.URL(this.expected) {
@@ -74,32 +75,27 @@ func TestShouldNotAddTrailingSlashToBaseURL(t *testing.T) {
func TestPageCount(t *testing.T) {
testCommonResetState()
- hugofs.InitMemFs()
viper.Set("uglyURLs", false)
viper.Set("paginate", 10)
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: urlFakeSource},
- Language: helpers.NewDefaultLanguage(),
- }
- if err := buildAndRenderSite(s, "indexes/blue.html", indexTemplate); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
- _, err := hugofs.Destination().Open("public/blue")
+ fs := hugofs.NewMem()
+ writeSourcesToSource(t, "content", fs, urlFakeSource...)
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+
+ _, err := s.Fs.Destination.Open("public/blue")
if err != nil {
t.Errorf("No indexed rendered.")
}
- for _, s := range []string{
+ for _, pth := range []string{
"public/sd1/foo/index.html",
"public/sd2/index.html",
"public/sd3/index.html",
"public/sd4.html",
} {
- if _, err := hugofs.Destination().Open(filepath.FromSlash(s)); err != nil {
- t.Errorf("No alias rendered: %s", s)
+ if _, err := s.Fs.Destination.Open(filepath.FromSlash(pth)); err != nil {
+ t.Errorf("No alias rendered: %s", pth)
}
}
}
diff --git a/hugolib/sitemap_test.go b/hugolib/sitemap_test.go
index 95f8739ec..15d71cc6f 100644
--- a/hugolib/sitemap_test.go
+++ b/hugolib/sitemap_test.go
@@ -18,8 +18,9 @@ import (
"reflect"
- "github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/source"
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/tplapi"
"github.com/spf13/viper"
)
@@ -45,24 +46,21 @@ func doTestSitemapOutput(t *testing.T, internal bool) {
viper.Set("baseURL", "http://auth/bub/")
- s := &Site{
- deps: newDeps(DepsCfg{}),
- Source: &source.InMemorySource{ByteSource: weightedSources},
- Language: helpers.NewDefaultLanguage(),
- }
+ fs := hugofs.NewMem()
- if internal {
- if err := buildAndRenderSite(s); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ depsCfg := deps.DepsCfg{Fs: fs}
- } else {
- if err := buildAndRenderSite(s, "sitemap.xml", sitemapTemplate); err != nil {
- t.Fatalf("Failed to build site: %s", err)
+ if !internal {
+ depsCfg.WithTemplate = func(templ tplapi.Template) error {
+ templ.AddTemplate("sitemap.xml", sitemapTemplate)
+ return nil
}
}
- assertFileContent(t, "public/sitemap.xml", true,
+ writeSourcesToSource(t, "content", fs, weightedSources...)
+ s := buildSingleSite(t, depsCfg, BuildCfg{})
+
+ assertFileContent(t, s.Fs, "public/sitemap.xml", true,
// Regular page
" <loc>http://auth/bub/sect/doc1/</loc>",
// Home page
diff --git a/hugolib/taxonomy.go b/hugolib/taxonomy.go
index 68354de89..5faf14d0f 100644
--- a/hugolib/taxonomy.go
+++ b/hugolib/taxonomy.go
@@ -16,8 +16,6 @@ package hugolib
import (
"fmt"
"sort"
-
- "github.com/spf13/hugo/helpers"
)
// The TaxonomyList is a list of all taxonomies and their values
@@ -59,26 +57,15 @@ type OrderedTaxonomyEntry struct {
WeightedPages WeightedPages
}
-// KeyPrep... Taxonomies should be case insensitive. Can make it easily conditional later.
-func kp(in string) string {
- return helpers.CurrentPathSpec().MakePathSanitized(in)
-}
-
// Get the weighted pages for the given key.
func (i Taxonomy) Get(key string) WeightedPages {
- if val, ok := i[key]; ok {
- return val
- }
- return i[kp(key)]
+ return i[key]
}
// Count the weighted pages for the given key.
-func (i Taxonomy) Count(key string) int { return len(i[kp(key)]) }
+func (i Taxonomy) Count(key string) int { return len(i[key]) }
-func (i Taxonomy) add(key string, w WeightedPage, pretty bool) {
- if !pretty {
- key = kp(key)
- }
+func (i Taxonomy) add(key string, w WeightedPage) {
i[key] = append(i[key], w)
}
diff --git a/hugolib/taxonomy_test.go b/hugolib/taxonomy_test.go
index 65b36d4e5..5cbd58d10 100644
--- a/hugolib/taxonomy_test.go
+++ b/hugolib/taxonomy_test.go
@@ -18,6 +18,9 @@ import (
"reflect"
"testing"
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
+
"github.com/spf13/viper"
)
@@ -31,16 +34,14 @@ func TestByCountOrderOfTaxonomies(t *testing.T) {
viper.Set("taxonomies", taxonomies)
- writeSource(t, filepath.Join("content", "page.md"), pageYamlWithTaxonomiesA)
+ fs := hugofs.NewMem()
- site := NewSiteDefaultLang()
+ writeSource(t, fs, filepath.Join("content", "page.md"), pageYamlWithTaxonomiesA)
- if err := buildSiteSkipRender(site); err != nil {
- t.Fatalf("Failed to build site: %s", err)
- }
+ s := buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
st := make([]string, 0)
- for _, t := range site.Taxonomies["tags"].ByCount() {
+ for _, t := range s.Taxonomies["tags"].ByCount() {
st = append(st, t.Name)
}
diff --git a/hugolib/template_engines_test.go b/hugolib/template_engines_test.go
new file mode 100644
index 000000000..424f2562a
--- /dev/null
+++ b/hugolib/template_engines_test.go
@@ -0,0 +1,99 @@
+// Copyright 2017 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 hugolib
+
+import (
+ "fmt"
+ "path/filepath"
+ "testing"
+
+ "strings"
+
+ "github.com/spf13/viper"
+
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
+)
+
+func TestAllTemplateEngines(t *testing.T) {
+ noOp := func(s string) string {
+ return s
+ }
+
+ amberFixer := func(s string) string {
+ fixed := strings.Replace(s, "{{ .Title", "{{ Title", -1)
+ fixed = strings.Replace(fixed, ".Content", "Content", -1)
+ fixed = strings.Replace(fixed, "{{", "#{", -1)
+ fixed = strings.Replace(fixed, "}}", "}", -1)
+ fixed = strings.Replace(fixed, `title "hello world"`, `title("hello world")`, -1)
+
+ return fixed
+ }
+
+ for _, config := range []struct {
+ suffix string
+ templateFixer func(s string) string
+ }{
+ {"amber", amberFixer},
+ {"html", noOp},
+ {"ace", noOp},
+ } {
+ doTestTemplateEngine(t, config.suffix, config.templateFixer)
+
+ }
+
+}
+
+func doTestTemplateEngine(t *testing.T, suffix string, templateFixer func(s string) string) {
+
+ testCommonResetState()
+
+ fs := hugofs.NewMem()
+ viper.SetFs(fs.Source)
+
+ writeSource(t, fs, filepath.Join("content", "p.md"), `
+---
+title: My Title
+---
+My Content
+`)
+
+ t.Log("Testing", suffix)
+
+ templTemplate := `
+p
+ |
+ | Page Title: {{ .Title }}
+ br
+ | Page Content: {{ .Content }}
+ br
+ | {{ title "hello world" }}
+
+`
+
+ templ := templateFixer(templTemplate)
+
+ t.Log(templ)
+
+ writeSource(t, fs, filepath.Join("layouts", "_default", fmt.Sprintf("single.%s", suffix)), templ)
+
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
+
+ assertFileContent(t, fs, filepath.Join("public", "p", "index.html"), true,
+ "Page Title: My Title",
+ "My Content",
+ "Hello World",
+ )
+
+}
diff --git a/hugolib/template_test.go b/hugolib/template_test.go
index 20db56f8a..2690b172a 100644
--- a/hugolib/template_test.go
+++ b/hugolib/template_test.go
@@ -17,127 +17,133 @@ import (
"path/filepath"
"testing"
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/hugofs"
+
"github.com/spf13/viper"
)
func TestBaseGoTemplate(t *testing.T) {
+
+ var fs *hugofs.Fs
+
// Variants:
// 1. <current-path>/<template-name>-baseof.<suffix>, e.g. list-baseof.<suffix>.
// 2. <current-path>/baseof.<suffix>
// 3. _default/<template-name>-baseof.<suffix>, e.g. list-baseof.<suffix>.
// 4. _default/baseof.<suffix>
- for i, this := range []struct {
+ for _, this := range []struct {
setup func(t *testing.T)
assert func(t *testing.T)
}{
{
// Variant 1
func(t *testing.T) {
- writeSource(t, filepath.Join("layouts", "section", "sect-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("layouts", "section", "sect.html"), `{{define "main"}}sect{{ end }}`)
+ writeSource(t, fs, filepath.Join("layouts", "section", "sect-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("layouts", "section", "sect.html"), `{{define "main"}}sect{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, filepath.Join("public", "sect", "index.html"), false, "Base: sect")
+ assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: sect")
},
},
{
// Variant 2
func(t *testing.T) {
- writeSource(t, filepath.Join("layouts", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("layouts", "index.html"), `{{define "main"}}index{{ end }}`)
+ writeSource(t, fs, filepath.Join("layouts", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("layouts", "index.html"), `{{define "main"}}index{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, filepath.Join("public", "index.html"), false, "Base: index")
+ assertFileContent(t, fs, filepath.Join("public", "index.html"), false, "Base: index")
},
},
{
// Variant 3
func(t *testing.T) {
- writeSource(t, filepath.Join("layouts", "_default", "list-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
+ writeSource(t, fs, filepath.Join("layouts", "_default", "list-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, filepath.Join("public", "sect", "index.html"), false, "Base: list")
+ assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: list")
},
},
{
// Variant 4
func(t *testing.T) {
- writeSource(t, filepath.Join("layouts", "_default", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
+ writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, filepath.Join("public", "sect", "index.html"), false, "Base: list")
+ assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: list")
},
},
{
// Variant 1, theme, use project's base
func(t *testing.T) {
viper.Set("theme", "mytheme")
- writeSource(t, filepath.Join("layouts", "section", "sect-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("themes", "mytheme", "layouts", "section", "sect-baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("layouts", "section", "sect.html"), `{{define "main"}}sect{{ end }}`)
+ writeSource(t, fs, filepath.Join("layouts", "section", "sect-baseof.html"), `Base: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "section", "sect-baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("layouts", "section", "sect.html"), `{{define "main"}}sect{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, filepath.Join("public", "sect", "index.html"), false, "Base: sect")
+ assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: sect")
},
},
{
// Variant 1, theme, use theme's base
func(t *testing.T) {
viper.Set("theme", "mytheme")
- writeSource(t, filepath.Join("themes", "mytheme", "layouts", "section", "sect-baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("layouts", "section", "sect.html"), `{{define "main"}}sect{{ end }}`)
+ writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "section", "sect-baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("layouts", "section", "sect.html"), `{{define "main"}}sect{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, filepath.Join("public", "sect", "index.html"), false, "Base Theme: sect")
+ assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base Theme: sect")
},
},
{
// Variant 4, theme, use project's base
func(t *testing.T) {
viper.Set("theme", "mytheme")
- writeSource(t, filepath.Join("layouts", "_default", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("themes", "mytheme", "layouts", "_default", "baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("themes", "mytheme", "layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
+ writeSource(t, fs, filepath.Join("layouts", "_default", "baseof.html"), `Base: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, filepath.Join("public", "sect", "index.html"), false, "Base: list")
+ assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base: list")
},
},
{
// Variant 4, theme, use themes's base
func(t *testing.T) {
viper.Set("theme", "mytheme")
- writeSource(t, filepath.Join("themes", "mytheme", "layouts", "_default", "baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
- writeSource(t, filepath.Join("themes", "mytheme", "layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
+ writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "baseof.html"), `Base Theme: {{block "main" .}}block{{end}}`)
+ writeSource(t, fs, filepath.Join("themes", "mytheme", "layouts", "_default", "list.html"), `{{define "main"}}list{{ end }}`)
},
func(t *testing.T) {
- assertFileContent(t, filepath.Join("public", "sect", "index.html"), false, "Base Theme: list")
+ assertFileContent(t, fs, filepath.Join("public", "sect", "index.html"), false, "Base Theme: list")
},
},
} {
testCommonResetState()
- writeSource(t, filepath.Join("content", "sect", "page.md"), `---
+ fs = hugofs.NewMem()
+
+ writeSource(t, fs, filepath.Join("content", "sect", "page.md"), `---
title: Template test
---
Some content
`)
this.setup(t)
- if err := buildAndRenderSite(NewSiteDefaultLang()); err != nil {
- t.Fatalf("[%d] Failed to build site: %s", i, err)
- }
+ buildSingleSite(t, deps.DepsCfg{Fs: fs}, BuildCfg{})
this.assert(t)
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
new file mode 100644
index 000000000..1d775aca8
--- /dev/null
+++ b/hugolib/testhelpers_test.go
@@ -0,0 +1,53 @@
+package hugolib
+
+import (
+ "path/filepath"
+ "testing"
+
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/helpers"
+ "github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/source"
+ "github.com/spf13/hugo/tplapi"
+ "github.com/spf13/viper"
+
+ "github.com/stretchr/testify/require"
+)
+
+func newTestDepsConfig() deps.DepsCfg {
+ return deps.DepsCfg{Fs: hugofs.NewMem()}
+}
+
+func newTestPathSpec() *helpers.PathSpec {
+ return helpers.NewPathSpec(hugofs.NewMem(), viper.GetViper())
+}
+
+func createWithTemplateFromNameValues(additionalTemplates ...string) func(templ tplapi.Template) error {
+
+ return func(templ tplapi.Template) error {
+ for i := 0; i < len(additionalTemplates); i += 2 {
+ err := templ.AddTemplate(additionalTemplates[i], additionalTemplates[i+1])
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+ }
+}
+
+func buildSingleSite(t *testing.T, depsCfg deps.DepsCfg, buildCfg BuildCfg) *Site {
+ h, err := NewHugoSitesFromConfiguration(depsCfg)
+
+ require.NoError(t, err)
+ require.Len(t, h.Sites, 1)
+
+ require.NoError(t, h.Build(buildCfg))
+
+ return h.Sites[0]
+}
+
+func writeSourcesToSource(t *testing.T, base string, fs *hugofs.Fs, sources ...source.ByteSource) {
+ for _, src := range sources {
+ writeSource(t, fs, filepath.Join(base, src.Name), string(src.Content))
+ }
+}
diff --git a/source/filesystem.go b/source/filesystem.go
index 7873b47f1..6089824a0 100644
--- a/source/filesystem.go
+++ b/source/filesystem.go
@@ -38,6 +38,12 @@ type Filesystem struct {
files []*File
Base string
AvoidPaths []string
+
+ fs *hugofs.Fs
+}
+
+func NewFilesystem(fs *hugofs.Fs, base string, avoidPaths ...string) *Filesystem {
+ return &Filesystem{fs: fs, Base: base, AvoidPaths: avoidPaths}
}
func (f *Filesystem) FilesByExts(exts ...string) []*File {
@@ -92,7 +98,7 @@ func (f *Filesystem) captureFiles() {
return err
}
if b {
- rd, err := NewLazyFileReader(hugofs.Source(), filePath)
+ rd, err := NewLazyFileReader(f.fs.Source, filePath)
if err != nil {
return err
}
@@ -101,7 +107,10 @@ func (f *Filesystem) captureFiles() {
return err
}
- err := helpers.SymbolicWalk(hugofs.Source(), f.Base, walker)
+ if f.fs == nil {
+ panic("Must have a fs")
+ }
+ err := helpers.SymbolicWalk(f.fs.Source, f.Base, walker)
if err != nil {
jww.ERROR.Println(err)
@@ -119,7 +128,7 @@ func (f *Filesystem) shouldRead(filePath string, fi os.FileInfo) (bool, error) {
jww.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", filePath, err)
return false, nil
}
- linkfi, err := hugofs.Source().Stat(link)
+ linkfi, err := f.fs.Source.Stat(link)
if err != nil {
jww.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
return false, nil
diff --git a/source/filesystem_test.go b/source/filesystem_test.go
index a1e111d2f..598a1b81d 100644
--- a/source/filesystem_test.go
+++ b/source/filesystem_test.go
@@ -19,10 +19,12 @@ import (
"runtime"
"strings"
"testing"
+
+ "github.com/spf13/hugo/hugofs"
)
func TestEmptySourceFilesystem(t *testing.T) {
- src := &Filesystem{Base: "Empty"}
+ src := NewFilesystem(hugofs.NewMem(), "Empty")
if len(src.Files()) != 0 {
t.Errorf("new filesystem should contain 0 files.")
}
@@ -37,13 +39,12 @@ type TestPath struct {
}
func TestAddFile(t *testing.T) {
+ fs := hugofs.NewMem()
tests := platformPaths
for _, test := range tests {
base := platformBase
- srcDefault := new(Filesystem)
- srcWithBase := &Filesystem{
- Base: base,
- }
+ srcDefault := NewFilesystem(fs, "")
+ srcWithBase := NewFilesystem(fs, base)
for _, src := range []*Filesystem{srcDefault, srcWithBase} {
@@ -99,8 +100,10 @@ func TestUnicodeNorm(t *testing.T) {
{NFC: "é", NFD: "\x65\xcc\x81"},
}
+ fs := hugofs.NewMem()
+
for _, path := range paths {
- src := new(Filesystem)
+ src := NewFilesystem(fs, "")
_ = src.add(path.NFD, strings.NewReader(""))
f := src.Files()[0]
if f.BaseFileName() != path.NFC {
diff --git a/target/file.go b/target/file.go
index 740741bba..6bf27ba00 100644
--- a/target/file.go
+++ b/target/file.go
@@ -41,6 +41,8 @@ type Output interface {
type Filesystem struct {
PublishDir string
+
+ Fs *hugofs.Fs
}
func (fs *Filesystem) Publish(path string, r io.Reader) (err error) {
@@ -49,7 +51,7 @@ func (fs *Filesystem) Publish(path string, r io.Reader) (err error) {
return
}
- return helpers.WriteToDisk(translated, r, hugofs.Destination())
+ return helpers.WriteToDisk(translated, r, fs.Fs.Destination)
}
func (fs *Filesystem) Translate(src string) (dest string, err error) {
diff --git a/target/htmlredirect.go b/target/htmlredirect.go
index 7444dd42b..00f5d71de 100644
--- a/target/htmlredirect.go
+++ b/target/htmlredirect.go
@@ -46,6 +46,8 @@ type HTMLRedirectAlias struct {
PublishDir string
Templates *template.Template
AllowRoot bool // for the language redirects
+
+ Fs *hugofs.Fs
}
func (h *HTMLRedirectAlias) Translate(alias string) (aliasPath string, err error) {
@@ -145,5 +147,5 @@ func (h *HTMLRedirectAlias) Publish(path string, permalink string, page interfac
return
}
- return helpers.WriteToDisk(path, buffer, hugofs.Destination())
+ return helpers.WriteToDisk(path, buffer, h.Fs.Destination)
}
diff --git a/target/page.go b/target/page.go
index ab38ded58..bfa431aa3 100644
--- a/target/page.go
+++ b/target/page.go
@@ -35,6 +35,8 @@ type PagePub struct {
// LangDir will contain the subdir for the language, i.e. "en", "de" etc.
// It will be empty if the site is rendered in root.
LangDir string
+
+ Fs *hugofs.Fs
}
func (pp *PagePub) Publish(path string, r io.Reader) (err error) {
@@ -44,7 +46,7 @@ func (pp *PagePub) Publish(path string, r io.Reader) (err error) {
return
}
- return helpers.WriteToDisk(translated, r, hugofs.Destination())
+ return helpers.WriteToDisk(translated, r, pp.Fs.Destination)
}
func (pp *PagePub) Translate(src string) (dest string, err error) {
diff --git a/target/page_test.go b/target/page_test.go
index b55726af7..844120048 100644
--- a/target/page_test.go
+++ b/target/page_test.go
@@ -16,9 +16,13 @@ package target
import (
"path/filepath"
"testing"
+
+ "github.com/spf13/hugo/hugofs"
)
func TestPageTranslator(t *testing.T) {
+ fs := hugofs.NewMem()
+
tests := []struct {
content string
expected string
@@ -37,7 +41,7 @@ func TestPageTranslator(t *testing.T) {
}
for _, test := range tests {
- f := new(PagePub)
+ f := &PagePub{Fs: fs}
dest, err := f.Translate(filepath.FromSlash(test.content))
expected := filepath.FromSlash(test.expected)
if err != nil {
diff --git a/tpl/amber_compiler.go b/tpl/amber_compiler.go
new file mode 100644
index 000000000..4477f6ac0
--- /dev/null
+++ b/tpl/amber_compiler.go
@@ -0,0 +1,42 @@
+// Copyright 2017 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 tpl
+
+import (
+ "html/template"
+
+ "github.com/eknkc/amber"
+)
+
+func (gt *GoHTMLTemplate) CompileAmberWithTemplate(b []byte, path string, t *template.Template) (*template.Template, error) {
+ c := amber.New()
+
+ if err := c.ParseData(b, path); err != nil {
+ return nil, err
+ }
+
+ data, err := c.CompileString()
+
+ if err != nil {
+ return nil, err
+ }
+
+ tpl, err := t.Funcs(gt.amberFuncMap).Parse(data)
+
+ if err != nil {
+ return nil, err
+ }
+
+ return tpl, nil
+}
diff --git a/tpl/template.go b/tpl/template.go
index 867c0a2ef..1c71989f4 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -24,33 +24,12 @@ import (
"github.com/eknkc/amber"
"github.com/spf13/afero"
bp "github.com/spf13/hugo/bufferpool"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
- jww "github.com/spf13/jwalterweatherman"
"github.com/yosssi/ace"
)
// TODO(bep) globals get rid of the rest of the jww.ERR etc.
-//var tmpl *GoHTMLTemplate
-
-// TODO(bep) an interface with hundreds of methods ... remove it.
-// And unexport most of these methods.
-type Template interface {
- ExecuteTemplate(wr io.Writer, name string, data interface{}) error
- Lookup(name string) *template.Template
- Templates() []*template.Template
- New(name string) *template.Template
- GetClone() *template.Template
- LoadTemplates(absPath string)
- LoadTemplatesWithPrefix(absPath, prefix string)
- AddTemplate(name, tpl string) error
- AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error
- AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error
- AddInternalTemplate(prefix, name, tpl string) error
- AddInternalShortcode(name, tpl string) error
- PrintErrors()
- Funcs(funcMap template.FuncMap)
-}
type templateErr struct {
name string
@@ -70,52 +49,105 @@ type GoHTMLTemplate struct {
funcster *templateFuncster
- // TODO(bep) globals template
- log *jww.Notepad
+ amberFuncMap template.FuncMap
+
+ *deps.Deps
}
-// New returns a new Hugo Template System
+type TemplateProvider struct{}
+
+var DefaultTemplateProvider *TemplateProvider
+
+// Update updates the Hugo Template System in the provided Deps.
// with all the additional features, templates & functions
-func New(logger *jww.Notepad, withTemplate ...func(templ Template) error) *GoHTMLTemplate {
+func (*TemplateProvider) Update(deps *deps.Deps) error {
+ // TODO(bep) check that this isn't called too many times.
tmpl := &GoHTMLTemplate{
Template: template.New(""),
overlays: make(map[string]*template.Template),
errors: make([]*templateErr, 0),
- log: logger,
+ Deps: deps,
}
- tmpl.funcster = newTemplateFuncster(tmpl)
+ deps.Tmpl = tmpl
- // The URL funcs in the funcMap is somewhat language dependent,
- // so we need to wait until the language and site config is loaded.
- // TODO(bep) globals
- tmpl.funcster.initFuncMap()
-
- // TODO(bep) globals
- for k, v := range tmpl.funcster.funcMap {
- amber.FuncMap[k] = v
- }
+ tmpl.initFuncs(deps)
tmpl.LoadEmbedded()
- for _, wt := range withTemplate {
- err := wt(tmpl)
+ if deps.WithTemplate != nil {
+ err := deps.WithTemplate(tmpl)
if err != nil {
tmpl.errors = append(tmpl.errors, &templateErr{"init", err})
}
}
- tmpl.markReady()
+ tmpl.MarkReady()
+
+ return nil
+
+}
+
+// Clone clones
+func (*TemplateProvider) Clone(d *deps.Deps) error {
+
+ t := d.Tmpl.(*GoHTMLTemplate)
+
+ // 1. Clone the clone with new template funcs
+ // 2. Clone any overlays with new template funcs
+
+ tmpl := &GoHTMLTemplate{
+ Template: template.Must(t.Template.Clone()),
+ overlays: make(map[string]*template.Template),
+ errors: make([]*templateErr, 0),
+ Deps: d,
+ }
+
+ d.Tmpl = tmpl
+ tmpl.initFuncs(d)
+
+ for k, v := range t.overlays {
+ vc := template.Must(v.Clone())
+ vc.Funcs(tmpl.funcster.funcMap)
+ tmpl.overlays[k] = vc
+ }
+
+ tmpl.MarkReady()
+
+ return nil
+}
+
+func (t *GoHTMLTemplate) initFuncs(d *deps.Deps) {
+
+ t.funcster = newTemplateFuncster(d)
+
+ // The URL funcs in the funcMap is somewhat language dependent,
+ // so we need to wait until the language and site config is loaded.
+ t.funcster.initFuncMap()
+
+ t.amberFuncMap = template.FuncMap{}
+
+ for k, v := range amber.FuncMap {
+ t.amberFuncMap[k] = v
+ }
+
+ for k, v := range t.funcster.funcMap {
+ t.amberFuncMap[k] = v
+ // Hacky, but we need to make sure that the func names are in the global map.
+ amber.FuncMap[k] = func() string {
+ panic("should never be invoked")
+ return ""
+ }
+ }
- return tmpl
}
func (t *GoHTMLTemplate) Funcs(funcMap template.FuncMap) {
t.Template.Funcs(funcMap)
}
-func (t *GoHTMLTemplate) partial(name string, contextList ...interface{}) template.HTML {
+func (t *GoHTMLTemplate) Partial(name string, contextList ...interface{}) template.HTML {
if strings.HasPrefix("partials/", name) {
name = name[8:]
}
@@ -147,8 +179,8 @@ func (t *GoHTMLTemplate) executeTemplate(context interface{}, w io.Writer, layou
}
}
if !worked {
- t.log.ERROR.Println("Unable to render", layouts)
- t.log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
+ t.Log.ERROR.Println("Unable to render", layouts)
+ t.Log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
}
}
@@ -186,9 +218,9 @@ func (t *GoHTMLTemplate) LoadEmbedded() {
t.EmbedTemplates()
}
-// markReady marks the template as "ready for execution". No changes allowed
+// MarkReady marks the template as "ready for execution". No changes allowed
// after this is set.
-func (t *GoHTMLTemplate) markReady() {
+func (t *GoHTMLTemplate) MarkReady() {
if t.clone == nil {
t.clone = template.Must(t.Template.Clone())
}
@@ -244,7 +276,7 @@ func (t *GoHTMLTemplate) AddTemplateFileWithMaster(name, overlayFilename, master
masterTpl := t.Lookup(masterFilename)
if masterTpl == nil {
- b, err := afero.ReadFile(hugofs.Source(), masterFilename)
+ b, err := afero.ReadFile(t.Fs.Source, masterFilename)
if err != nil {
return err
}
@@ -257,7 +289,7 @@ func (t *GoHTMLTemplate) AddTemplateFileWithMaster(name, overlayFilename, master
}
}
- b, err := afero.ReadFile(hugofs.Source(), overlayFilename)
+ b, err := afero.ReadFile(t.Fs.Source, overlayFilename)
if err != nil {
return err
}
@@ -315,19 +347,13 @@ func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) er
switch ext {
case ".amber":
templateName := strings.TrimSuffix(name, filepath.Ext(name)) + ".html"
- compiler := amber.New()
- b, err := afero.ReadFile(hugofs.Source(), path)
+ b, err := afero.ReadFile(t.Fs.Source, path)
if err != nil {
return err
}
- // Parse the input data
- if err := compiler.ParseData(b, path); err != nil {
- return err
- }
-
- templ, err := compiler.CompileWithTemplate(t.New(templateName))
+ templ, err := t.CompileAmberWithTemplate(b, path, t.New(templateName))
if err != nil {
return err
}
@@ -335,14 +361,14 @@ func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) er
return applyTemplateTransformers(templ)
case ".ace":
var innerContent, baseContent []byte
- innerContent, err := afero.ReadFile(hugofs.Source(), path)
+ innerContent, err := afero.ReadFile(t.Fs.Source, path)
if err != nil {
return err
}
if baseTemplatePath != "" {
- baseContent, err = afero.ReadFile(hugofs.Source(), baseTemplatePath)
+ baseContent, err = afero.ReadFile(t.Fs.Source, baseTemplatePath)
if err != nil {
return err
}
@@ -355,13 +381,13 @@ func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) er
return t.AddTemplateFileWithMaster(name, path, baseTemplatePath)
}
- b, err := afero.ReadFile(hugofs.Source(), path)
+ b, err := afero.ReadFile(t.Fs.Source, path)
if err != nil {
return err
}
- t.log.DEBUG.Printf("Add template file from path %s", path)
+ t.Log.DEBUG.Printf("Add template file from path %s", path)
return t.AddTemplate(name, string(b))
}
@@ -391,25 +417,25 @@ func isBaseTemplate(path string) bool {
}
func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
- t.log.DEBUG.Printf("Load templates from path %q prefix %q", absPath, prefix)
+ t.Log.DEBUG.Printf("Load templates from path %q prefix %q", absPath, prefix)
walker := func(path string, fi os.FileInfo, err error) error {
if err != nil {
return nil
}
- t.log.DEBUG.Println("Template path", path)
+ t.Log.DEBUG.Println("Template path", path)
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
link, err := filepath.EvalSymlinks(absPath)
if err != nil {
- t.log.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", absPath, err)
+ t.Log.ERROR.Printf("Cannot read symbolic link '%s', error was: %s", absPath, err)
return nil
}
- linkfi, err := hugofs.Source().Stat(link)
+ linkfi, err := t.Fs.Source.Stat(link)
if err != nil {
- t.log.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
+ t.Log.ERROR.Printf("Cannot stat '%s', error was: %s", link, err)
return nil
}
if !linkfi.Mode().IsRegular() {
- t.log.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", absPath)
+ t.Log.ERROR.Printf("Symbolic links for directories not supported, skipping '%s'", absPath)
}
return nil
}
@@ -441,7 +467,7 @@ func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
// This may be a view that shouldn't have base template
// Have to look inside it to make sure
- needsBase, err := helpers.FileContainsAny(path, innerMarkers, hugofs.Source())
+ needsBase, err := helpers.FileContainsAny(path, innerMarkers, t.Fs.Source)
if err != nil {
return err
}
@@ -482,7 +508,7 @@ func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
for _, pair := range pairsToCheck {
pathsToCheck := basePathsToCheck(pair, layoutDir, themeDir)
for _, pathToCheck := range pathsToCheck {
- if ok, err := helpers.Exists(pathToCheck, hugofs.Source()); err == nil && ok {
+ if ok, err := helpers.Exists(pathToCheck, t.Fs.Source); err == nil && ok {
baseTemplatePath = pathToCheck
break Loop
}
@@ -492,14 +518,14 @@ func (t *GoHTMLTemplate) loadTemplates(absPath string, prefix string) {
}
if err := t.AddTemplateFile(tplName, baseTemplatePath, path); err != nil {
- t.log.ERROR.Printf("Failed to add template %s in path %s: %s", tplName, path, err)
+ t.Log.ERROR.Printf("Failed to add template %s in path %s: %s", tplName, path, err)
}
}
return nil
}
- if err := helpers.SymbolicWalk(hugofs.Source(), absPath, walker); err != nil {
- t.log.ERROR.Printf("Failed to load templates: %s", err)
+ if err := helpers.SymbolicWalk(t.Fs.Source, absPath, walker); err != nil {
+ t.Log.ERROR.Printf("Failed to load templates: %s", err)
}
}
@@ -526,6 +552,6 @@ func (t *GoHTMLTemplate) LoadTemplates(absPath string) {
func (t *GoHTMLTemplate) PrintErrors() {
for i, e := range t.errors {
- t.log.ERROR.Println(i, ":", e.err)
+ t.Log.ERROR.Println(i, ":", e.err)
}
}
diff --git a/tpl/template_funcs.go b/tpl/template_funcs.go
index 8f653808b..5db5e54ac 100644
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -43,8 +43,8 @@ import (
"github.com/bep/inflect"
"github.com/spf13/afero"
"github.com/spf13/cast"
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
@@ -56,14 +56,15 @@ import (
// Some of the template funcs are'nt entirely stateless.
type templateFuncster struct {
- t *GoHTMLTemplate
funcMap template.FuncMap
cachedPartials partialCache
+
+ *deps.Deps
}
-func newTemplateFuncster(t *GoHTMLTemplate) *templateFuncster {
+func newTemplateFuncster(deps *deps.Deps) *templateFuncster {
return &templateFuncster{
- t: t,
+ Deps: deps,
cachedPartials: partialCache{p: make(map[string]template.HTML)},
}
}
@@ -424,7 +425,7 @@ func resetImageConfigCache() {
// imageConfig returns the image.Config for the specified path relative to the
// working directory. resetImageConfigCache must be run beforehand.
-func imageConfig(path interface{}) (image.Config, error) {
+func (t *templateFuncster) imageConfig(path interface{}) (image.Config, error) {
filename, err := cast.ToStringE(path)
if err != nil {
return image.Config{}, err
@@ -443,7 +444,7 @@ func imageConfig(path interface{}) (image.Config, error) {
return config, nil
}
- f, err := hugofs.WorkingDir().Open(filename)
+ f, err := t.Fs.WorkingDir.Open(filename)
if err != nil {
return image.Config{}, err
}
@@ -1013,7 +1014,7 @@ func where(seq, key interface{}, args ...interface{}) (interface{}, error) {
}
// apply takes a map, array, or slice and returns a new slice with the function fname applied over it.
-func (tf *templateFuncster) apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
+func (t *templateFuncster) apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
if seq == nil {
return make([]interface{}, 0), nil
}
@@ -1028,7 +1029,7 @@ func (tf *templateFuncster) apply(seq interface{}, fname string, args ...interfa
return nil, errors.New("can't iterate over a nil value")
}
- fn, found := tf.funcMap[fname]
+ fn, found := t.funcMap[fname]
if !found {
return nil, errors.New("can't find function " + fname)
}
@@ -1528,26 +1529,27 @@ type partialCache struct {
// Get retrieves partial output from the cache based upon the partial name.
// If the partial is not found in the cache, the partial is rendered and added
// to the cache.
-func (tf *templateFuncster) Get(key, name string, context interface{}) (p template.HTML) {
+func (t *templateFuncster) Get(key, name string, context interface{}) (p template.HTML) {
var ok bool
- tf.cachedPartials.RLock()
- p, ok = tf.cachedPartials.p[key]
- tf.cachedPartials.RUnlock()
+ t.cachedPartials.RLock()
+ p, ok = t.cachedPartials.p[key]
+ t.cachedPartials.RUnlock()
if ok {
return p
}
- tf.cachedPartials.Lock()
- if p, ok = tf.cachedPartials.p[key]; !ok {
- tf.cachedPartials.Unlock()
- p = tf.t.partial(name, context)
+ t.cachedPartials.Lock()
+ if p, ok = t.cachedPartials.p[key]; !ok {
+ t.cachedPartials.Unlock()
+ p = t.Tmpl.Partial(name, context)
+
+ t.cachedPartials.Lock()
+ t.cachedPartials.p[key] = p
- tf.cachedPartials.Lock()
- tf.cachedPartials.p[key] = p
}
- tf.cachedPartials.Unlock()
+ t.cachedPartials.Unlock()
return p
}
@@ -1556,14 +1558,14 @@ func (tf *templateFuncster) Get(key, name string, context interface{}) (p templa
// string parameter (a string slice actually, but be only use a variadic
// argument to make it optional) can be passed so that a given partial can have
// multiple uses. The cache is created with name+variant as the key.
-func (tf *templateFuncster) partialCached(name string, context interface{}, variant ...string) template.HTML {
+func (t *templateFuncster) partialCached(name string, context interface{}, variant ...string) template.HTML {
key := name
if len(variant) > 0 {
for i := 0; i < len(variant); i++ {
key += variant[i]
}
}
- return tf.Get(key, name, context)
+ return t.Get(key, name, context)
}
// regexpCache represents a cache of regexp objects protected by a mutex.
@@ -1814,23 +1816,23 @@ func readFile(fs *afero.BasePathFs, filename string) (string, error) {
// configured WorkingDir.
// It returns the contents as a string.
// There is a upper size limit set at 1 megabytes.
-func readFileFromWorkingDir(i interface{}) (string, error) {
+func (t *templateFuncster) readFileFromWorkingDir(i interface{}) (string, error) {
s, err := cast.ToStringE(i)
if err != nil {
return "", err
}
- return readFile(hugofs.WorkingDir(), s)
+ return readFile(t.Fs.WorkingDir, s)
}
// readDirFromWorkingDir listst the directory content relative to the
// configured WorkingDir.
-func readDirFromWorkingDir(i interface{}) ([]os.FileInfo, error) {
+func (t *templateFuncster) readDirFromWorkingDir(i interface{}) ([]os.FileInfo, error) {
path, err := cast.ToStringE(i)
if err != nil {
return nil, err
}
- list, err := afero.ReadDir(hugofs.WorkingDir(), path)
+ list, err := afero.ReadDir(t.Fs.WorkingDir, path)
if err != nil {
return nil, fmt.Errorf("Failed to read Directory %s with error message %s", path, err)
@@ -2074,20 +2076,20 @@ func htmlUnescape(in interface{}) (string, error) {
return html.UnescapeString(conv), nil
}
-func absURL(a interface{}) (template.HTML, error) {
+func (t *templateFuncster) absURL(a interface{}) (template.HTML, error) {
s, err := cast.ToStringE(a)
if err != nil {
return "", nil
}
- return template.HTML(helpers.CurrentPathSpec().AbsURL(s, false)), nil
+ return template.HTML(t.PathSpec.AbsURL(s, false)), nil
}
-func relURL(a interface{}) (template.HTML, error) {
+func (t *templateFuncster) relURL(a interface{}) (template.HTML, error) {
s, err := cast.ToStringE(a)
if err != nil {
return "", nil
}
- return template.HTML(helpers.CurrentPathSpec().RelURL(s, false)), nil
+ return template.HTML(t.PathSpec.RelURL(s, false)), nil
}
// getenv retrieves the value of the environment variable named by the key.
@@ -2101,19 +2103,19 @@ func getenv(key interface{}) (string, error) {
return os.Getenv(skey), nil
}
-func (tf *templateFuncster) initFuncMap() {
+func (t *templateFuncster) initFuncMap() {
funcMap := template.FuncMap{
- "absURL": absURL,
+ "absURL": t.absURL,
"absLangURL": func(i interface{}) (template.HTML, error) {
s, err := cast.ToStringE(i)
if err != nil {
return "", err
}
- return template.HTML(helpers.CurrentPathSpec().AbsURL(s, true)), nil
+ return template.HTML(t.PathSpec.AbsURL(s, true)), nil
},
"add": func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '+') },
"after": after,
- "apply": tf.apply,
+ "apply": t.apply,
"base64Decode": base64Decode,
"base64Encode": base64Encode,
"chomp": chomp,
@@ -2130,8 +2132,8 @@ func (tf *templateFuncster) initFuncMap() {
"findRE": findRE,
"first": first,
"ge": ge,
- "getCSV": getCSV,
- "getJSON": getJSON,
+ "getCSV": t.getCSV,
+ "getJSON": t.getJSON,
"getenv": getenv,
"gt": gt,
"hasPrefix": hasPrefix,
@@ -2139,7 +2141,7 @@ func (tf *templateFuncster) initFuncMap() {
"htmlEscape": htmlEscape,
"htmlUnescape": htmlUnescape,
"humanize": humanize,
- "imageConfig": imageConfig,
+ "imageConfig": t.imageConfig,
"in": in,
"index": index,
"int": func(v interface{}) (int, error) { return cast.ToIntE(v) },
@@ -2158,21 +2160,21 @@ func (tf *templateFuncster) initFuncMap() {
"mul": func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '*') },
"ne": ne,
"now": func() time.Time { return time.Now() },
- "partial": tf.t.partial,
- "partialCached": tf.partialCached,
+ "partial": t.Tmpl.Partial,
+ "partialCached": t.partialCached,
"plainify": plainify,
"pluralize": pluralize,
"querify": querify,
- "readDir": readDirFromWorkingDir,
- "readFile": readFileFromWorkingDir,
+ "readDir": t.readDirFromWorkingDir,
+ "readFile": t.readFileFromWorkingDir,
"ref": ref,
- "relURL": relURL,
+ "relURL": t.relURL,
"relLangURL": func(i interface{}) (template.HTML, error) {
s, err := cast.ToStringE(i)
if err != nil {
return "", err
}
- return template.HTML(helpers.CurrentPathSpec().RelURL(s, true)), nil
+ return template.HTML(t.PathSpec.RelURL(s, true)), nil
},
"relref": relRef,
"replace": replace,
@@ -2201,12 +2203,12 @@ func (tf *templateFuncster) initFuncMap() {
"trim": trim,
"truncate": truncate,
"upper": upper,
- "urlize": helpers.CurrentPathSpec().URLize,
+ "urlize": t.PathSpec.URLize,
"where": where,
"i18n": i18nTranslate,
"T": i18nTranslate,
}
- tf.funcMap = funcMap
- tf.t.Funcs(funcMap)
+ t.funcMap = funcMap
+ t.Tmpl.Funcs(funcMap)
}
diff --git a/tpl/template_funcs_test.go b/tpl/template_funcs_test.go
index e0c185092..e5d0193a6 100644
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -31,6 +31,9 @@ import (
"testing"
"time"
+ "github.com/spf13/hugo/tplapi"
+
+ "github.com/spf13/hugo/deps"
"github.com/spf13/hugo/helpers"
"io/ioutil"
@@ -43,9 +46,17 @@ import (
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
)
-var logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+var (
+ logger = jww.NewNotepad(jww.LevelFatal, jww.LevelFatal, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
+ defaultDepsConfig = deps.DepsCfg{
+ Language: helpers.NewLanguage("en"),
+ Logger: logger,
+ TemplateProvider: DefaultTemplateProvider,
+ }
+)
type tstNoStringer struct {
}
@@ -80,8 +91,7 @@ func tstInitTemplates() {
func TestFuncsInTemplate(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
+ testReset()
workingDir := "/home/hugo"
@@ -89,10 +99,9 @@ func TestFuncsInTemplate(t *testing.T) {
viper.Set("currentContentLanguage", helpers.NewDefaultLanguage())
viper.Set("multilingual", true)
- fs := &afero.MemMapFs{}
- hugofs.InitFs(fs)
+ fs := hugofs.NewMem()
- afero.WriteFile(fs, filepath.Join(workingDir, "README.txt"), []byte("Hugo Rocks!"), 0755)
+ afero.WriteFile(fs.Source, filepath.Join(workingDir, "README.txt"), []byte("Hugo Rocks!"), 0755)
// Add the examples from the docs: As a smoke test and to make sure the examples work.
// TODO(bep): docs: fix title example
@@ -244,7 +253,7 @@ urlize: bat-man
`
var b bytes.Buffer
- templ, err := New(logger).New("test").Parse(in)
+
var data struct {
Title string
Section string
@@ -259,11 +268,21 @@ urlize: bat-man
tstInitTemplates()
- if err != nil {
- t.Fatal("Got error on parse", err)
+ config := defaultDepsConfig
+ config.WithTemplate = func(templ tplapi.Template) error {
+ if _, err := templ.New("test").Parse(in); err != nil {
+ t.Fatal("Got error on parse", err)
+ }
+ return nil
+ }
+ config.Fs = fs
+
+ d := deps.New(config)
+ if err := d.LoadTemplates(); err != nil {
+ t.Fatal(err)
}
- err = templ.Execute(&b, &data)
+ err := d.Tmpl.Lookup("test").Execute(&b, &data)
if err != nil {
t.Fatal("Got error on execute", err)
@@ -624,15 +643,13 @@ func blankImage(width, height int) []byte {
}
func TestImageConfig(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
+ testReset()
workingDir := "/home/hugo"
viper.Set("workingDir", workingDir)
- fs := &afero.MemMapFs{}
- hugofs.InitFs(fs)
+ f := newTestFuncster()
for i, this := range []struct {
resetCache bool
@@ -692,13 +709,13 @@ func TestImageConfig(t *testing.T) {
},
},
} {
- afero.WriteFile(fs, filepath.Join(workingDir, this.path), this.input, 0755)
+ afero.WriteFile(f.Fs.Source, filepath.Join(workingDir, this.path), this.input, 0755)
if this.resetCache {
resetImageConfigCache()
}
- result, err := imageConfig(this.path)
+ result, err := f.imageConfig(this.path)
if err != nil {
t.Errorf("imageConfig returned error: %s", err)
}
@@ -712,15 +729,15 @@ func TestImageConfig(t *testing.T) {
}
}
- if _, err := imageConfig(t); err == nil {
+ if _, err := f.imageConfig(t); err == nil {
t.Error("Expected error from imageConfig when passed invalid path")
}
- if _, err := imageConfig("non-existent.png"); err == nil {
+ if _, err := f.imageConfig("non-existent.png"); err == nil {
t.Error("Expected error from imageConfig when passed non-existent file")
}
- if _, err := imageConfig(""); err == nil {
+ if _, err := f.imageConfig(""); err == nil {
t.Error("Expected error from imageConfig when passed empty path")
}
@@ -2381,14 +2398,11 @@ func TestDefault(t *testing.T) {
{map[string]string{"foo": "dog"}, `{{ default "nope" .foo "extra" }}`, ``, false},
{map[string]interface{}{"images": []string{}}, `{{ default "default.jpg" (index .images 0) }}`, `default.jpg`, true},
} {
- tmpl, err := New(logger).New("test").Parse(this.tpl)
- if err != nil {
- t.Errorf("[%d] unable to create new html template %q: %s", i, this.tpl, err)
- continue
- }
+
+ tmpl := newTestTemplate(t, "test", this.tpl)
buf := new(bytes.Buffer)
- err = tmpl.Execute(buf, this.input)
+ err := tmpl.Execute(buf, this.input)
if (err == nil) != this.ok {
t.Errorf("[%d] execute template returned unexpected error: %s", i, err)
continue
@@ -2520,6 +2534,7 @@ func TestSafeCSS(t *testing.T) {
}
}
+// TODO(bep) what is this? Also look above.
func TestSafeJS(t *testing.T) {
for i, this := range []struct {
str string
@@ -2560,6 +2575,7 @@ func TestSafeJS(t *testing.T) {
}
}
+// TODO(bep) what is this?
func TestSafeURL(t *testing.T) {
for i, this := range []struct {
str string
@@ -2716,18 +2732,16 @@ func TestSHA256(t *testing.T) {
}
func TestReadFile(t *testing.T) {
- viper.Reset()
- defer viper.Reset()
+ testReset()
workingDir := "/home/hugo"
viper.Set("workingDir", workingDir)
- fs := &afero.MemMapFs{}
- hugofs.InitFs(fs)
+ f := newTestFuncster()
- afero.WriteFile(fs, filepath.Join(workingDir, "/f/f1.txt"), []byte("f1-content"), 0755)
- afero.WriteFile(fs, filepath.Join("/home", "f2.txt"), []byte("f2-content"), 0755)
+ afero.WriteFile(f.Fs.Source, filepath.Join(workingDir, "/f/f1.txt"), []byte("f1-content"), 0755)
+ afero.WriteFile(f.Fs.Source, filepath.Join("/home", "f2.txt"), []byte("f2-content"), 0755)
for i, this := range []struct {
filename string
@@ -2739,7 +2753,7 @@ func TestReadFile(t *testing.T) {
{filepath.FromSlash("f/f1.txt"), "f1-content"},
{filepath.FromSlash("../f2.txt"), false},
} {
- result, err := readFileFromWorkingDir(this.filename)
+ result, err := f.readFileFromWorkingDir(this.filename)
if b, ok := this.expect.(bool); ok && !b {
if err == nil {
t.Errorf("[%d] readFile didn't return an expected error", i)
@@ -2770,8 +2784,6 @@ func TestPartialCached(t *testing.T) {
{"test1", "{{ .Title }} seq: {{ shuffle (seq 1 20) }}", `{{ partialCached "test1" . "%s" }}`, "header"},
}
- results := make(map[string]string, len(testCases))
-
var data struct {
Title string
Section string
@@ -2791,26 +2803,32 @@ func TestPartialCached(t *testing.T) {
tmp = tc.tmpl
}
- tmpl, err := New(logger).New("testroot").Parse(tmp)
- if err != nil {
- t.Fatalf("[%d] unable to create new html template: %s", i, err)
- }
+ defaultDepsConfig.WithTemplate = func(templ tplapi.Template) error {
+ err := templ.AddTemplate("testroot", tmp)
+ if err != nil {
+ return err
+ }
+ err = templ.AddTemplate("partials/"+tc.name, tc.partial)
+ if err != nil {
+ return err
+ }
- if tmpl == nil {
- t.Fatalf("[%d] tmpl should not be nil!", i)
+ return nil
}
- tmpl.New("partials/" + tc.name).Parse(tc.partial)
+ de := deps.New(defaultDepsConfig)
+ require.NoError(t, de.LoadTemplates())
buf := new(bytes.Buffer)
- err = tmpl.Execute(buf, &data)
+ templ := de.Tmpl.Lookup("testroot")
+ err := templ.Execute(buf, &data)
if err != nil {
t.Fatalf("[%d] error executing template: %s", i, err)
}
for j := 0; j < 10; j++ {
buf2 := new(bytes.Buffer)
- err = tmpl.Execute(buf2, nil)
+ err := templ.Execute(buf2, nil)
if err != nil {
t.Fatalf("[%d] error executing template 2nd time: %s", i, err)
}
@@ -2819,33 +2837,33 @@ func TestPartialCached(t *testing.T) {
t.Fatalf("[%d] cached results do not match:\nResult 1:\n%q\nResult 2:\n%q", i, buf, buf2)
}
}
-
- // double-check against previous test cases of the same variant
- previous, ok := results[tc.name+tc.variant]
- if !ok {
- results[tc.name+tc.variant] = buf.String()
- } else {
- if previous != buf.String() {
- t.Errorf("[%d] cached variant differs from previous rendering; got:\n%q\nwant:\n%q", i, buf.String(), previous)
- }
- }
}
}
func BenchmarkPartial(b *testing.B) {
- tstInitTemplates()
- tmpl, err := New(logger).New("testroot").Parse(`{{ partial "bench1" . }}`)
- if err != nil {
- b.Fatalf("unable to create new html template: %s", err)
+ defaultDepsConfig.WithTemplate = func(templ tplapi.Template) error {
+ err := templ.AddTemplate("testroot", `{{ partial "bench1" . }}`)
+ if err != nil {
+ return err
+ }
+ err = templ.AddTemplate("partials/bench1", `{{ shuffle (seq 1 10) }}`)
+ if err != nil {
+ return err
+ }
+
+ return nil
}
- tmpl.New("partials/bench1").Parse(`{{ shuffle (seq 1 10) }}`)
+ de := deps.New(defaultDepsConfig)
+ require.NoError(b, de.LoadTemplates())
+
buf := new(bytes.Buffer)
+ tmpl := de.Tmpl.Lookup("testroot")
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
- if err = tmpl.Execute(buf, nil); err != nil {
+ if err := tmpl.Execute(buf, nil); err != nil {
b.Fatalf("error executing template: %s", err)
}
buf.Reset()
@@ -2853,44 +2871,55 @@ func BenchmarkPartial(b *testing.B) {
}
func BenchmarkPartialCached(b *testing.B) {
- tstInitTemplates()
- tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . }}`)
- if err != nil {
- b.Fatalf("unable to create new html template: %s", err)
+ defaultDepsConfig.WithTemplate = func(templ tplapi.Template) error {
+ err := templ.AddTemplate("testroot", `{{ partialCached "bench1" . }}`)
+ if err != nil {
+ return err
+ }
+ err = templ.AddTemplate("partials/bench1", `{{ shuffle (seq 1 10) }}`)
+ if err != nil {
+ return err
+ }
+
+ return nil
}
- tmpl.New("partials/bench1").Parse(`{{ shuffle (seq 1 10) }}`)
+ de := deps.New(defaultDepsConfig)
+ require.NoError(b, de.LoadTemplates())
+
buf := new(bytes.Buffer)
+ tmpl := de.Tmpl.Lookup("testroot")
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
- if err = tmpl.Execute(buf, nil); err != nil {
+ if err := tmpl.Execute(buf, nil); err != nil {
b.Fatalf("error executing template: %s", err)
}
buf.Reset()
}
}
-func BenchmarkPartialCachedVariants(b *testing.B) {
- tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . "header" }}`)
- if err != nil {
- b.Fatalf("unable to create new html template: %s", err)
+func newTestFuncster() *templateFuncster {
+ d := deps.New(defaultDepsConfig)
+ if err := d.LoadTemplates(); err != nil {
+ panic(err)
}
- tmpl.New("partials/bench1").Parse(`{{ shuffle (seq 1 10) }}`)
- buf := new(bytes.Buffer)
+ return d.Tmpl.(*GoHTMLTemplate).funcster
+}
- b.ReportAllocs()
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- if err = tmpl.Execute(buf, nil); err != nil {
- b.Fatalf("error executing template: %s", err)
+func newTestTemplate(t *testing.T, name, template string) *template.Template {
+ defaultDepsConfig.WithTemplate = func(templ tplapi.Template) error {
+ err := templ.AddTemplate(name, template)
+ if err != nil {
+ return err
}
- buf.Reset()
+ return nil
}
-}
-func newTestFuncster() *templateFuncster {
- return New(logger).funcster
+ de := deps.New(defaultDepsConfig)
+ require.NoError(t, de.LoadTemplates())
+
+ return de.Tmpl.Lookup(name)
}
diff --git a/tpl/template_i18n.go b/tpl/template_i18n.go
index a725856e4..a7ec0df98 100644
--- a/tpl/template_i18n.go
+++ b/tpl/template_i18n.go
@@ -33,6 +33,7 @@ type translate struct {
current bundle.TranslateFunc
}
+// TODO(bep) global translator
var translator *translate
// SetTranslateLang sets the translations language to use during template processing.
diff --git a/tpl/template_resources.go b/tpl/template_resources.go
index ee6305793..13ebfb698 100644
--- a/tpl/template_resources.go
+++ b/tpl/template_resources.go
@@ -28,7 +28,6 @@ import (
"github.com/spf13/afero"
"github.com/spf13/hugo/helpers"
- "github.com/spf13/hugo/hugofs"
jww "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"
)
@@ -165,25 +164,25 @@ func resGetLocal(url string, fs afero.Fs) ([]byte, error) {
}
// resGetResource loads the content of a local or remote file
-func resGetResource(url string) ([]byte, error) {
+func (t *templateFuncster) resGetResource(url string) ([]byte, error) {
if url == "" {
return nil, nil
}
if strings.Contains(url, "://") {
- return resGetRemote(url, hugofs.Source(), http.DefaultClient)
+ return resGetRemote(url, t.Fs.Source, http.DefaultClient)
}
- return resGetLocal(url, hugofs.Source())
+ return resGetLocal(url, t.Fs.Source)
}
// getJSON expects one or n-parts of a URL to a resource which can either be a local or a remote one.
// If you provide multiple parts they will be joined together to the final URL.
// GetJSON returns nil or parsed JSON to use in a short code.
-func getJSON(urlParts ...string) interface{} {
+func (t *templateFuncster) getJSON(urlParts ...string) interface{} {
var v interface{}
url := strings.Join(urlParts, "")
for i := 0; i <= resRetries; i++ {
- c, err := resGetResource(url)
+ c, err := t.resGetResource(url)
if err != nil {
jww.ERROR.Printf("Failed to get json resource %s with error message %s", url, err)
return nil
@@ -194,7 +193,7 @@ func getJSON(urlParts ...string) interface{} {
jww.ERROR.Printf("Cannot read json from resource %s with error message %s", url, err)
jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
time.Sleep(resSleep)
- resDeleteCache(url, hugofs.Source())
+ resDeleteCache(url, t.Fs.Source)
continue
}
break
@@ -220,18 +219,18 @@ func parseCSV(c []byte, sep string) ([][]string, error) {
// The data separator can be a comma, semi-colon, pipe, etc, but only one character.
// If you provide multiple parts for the URL they will be joined together to the final URL.
// GetCSV returns nil or a slice slice to use in a short code.
-func getCSV(sep string, urlParts ...string) [][]string {
+func (t *templateFuncster) getCSV(sep string, urlParts ...string) [][]string {
var d [][]string
url := strings.Join(urlParts, "")
var clearCacheSleep = func(i int, u string) {
jww.ERROR.Printf("Retry #%d for %s and sleeping for %s", i, url, resSleep)
time.Sleep(resSleep)
- resDeleteCache(url, hugofs.Source())
+ resDeleteCache(url, t.Fs.Source)
}
for i := 0; i <= resRetries; i++ {
- c, err := resGetResource(url)
+ c, err := t.resGetResource(url)
if err == nil && !bytes.Contains(c, []byte(sep)) {
err = errors.New("Cannot find separator " + sep + " in CSV.")
diff --git a/tpl/template_resources_test.go b/tpl/template_resources_test.go
index 3385c5ef7..5a92f98d3 100644
--- a/tpl/template_resources_test.go
+++ b/tpl/template_resources_test.go
@@ -80,8 +80,10 @@ func TestScpCache(t *testing.T) {
}
func TestScpGetLocal(t *testing.T) {
- fs := new(afero.MemMapFs)
+ testReset()
+ fs := hugofs.NewMem()
ps := helpers.FilePathSeparator
+
tests := []struct {
path string
content []byte
@@ -95,12 +97,12 @@ func TestScpGetLocal(t *testing.T) {
for _, test := range tests {
r := bytes.NewReader(test.content)
- err := helpers.WriteToDisk(test.path, r, fs)
+ err := helpers.WriteToDisk(test.path, r, fs.Source)
if err != nil {
t.Error(err)
}
- c, err := resGetLocal(test.path, fs)
+ c, err := resGetLocal(test.path, fs.Source)
if err != nil {
t.Errorf("Error getting resource content: %s", err)
}
@@ -212,9 +214,9 @@ type wd struct {
Reset func()
}
-func testRetryWhenDone() wd {
+func testRetryWhenDone(f *templateFuncster) wd {
cd := viper.GetString("cacheDir")
- viper.Set("cacheDir", helpers.GetTempDir("", hugofs.Source()))
+ viper.Set("cacheDir", helpers.GetTempDir("", f.Fs.Source))
var tmpSleep time.Duration
tmpSleep, resSleep = resSleep, time.Millisecond
return wd{func() {
@@ -224,7 +226,10 @@ func testRetryWhenDone() wd {
}
func TestGetJSONFailParse(t *testing.T) {
- defer testRetryWhenDone().Reset()
+
+ f := newTestFuncster()
+
+ defer testRetryWhenDone(f).Reset()
reqCount := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -242,7 +247,7 @@ func TestGetJSONFailParse(t *testing.T) {
defer os.Remove(getCacheFileID(url))
want := map[string]interface{}{"gomeetup": []interface{}{"Sydney", "San Francisco", "Stockholm"}}
- have := getJSON(url)
+ have := f.getJSON(url)
assert.NotNil(t, have)
if have != nil {
assert.EqualValues(t, want, have)
@@ -250,7 +255,9 @@ func TestGetJSONFailParse(t *testing.T) {
}
func TestGetCSVFailParseSep(t *testing.T) {
- defer testRetryWhenDone().Reset()
+ f := newTestFuncster()
+
+ defer testRetryWhenDone(f).Reset()
reqCount := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -271,7 +278,7 @@ func TestGetCSVFailParseSep(t *testing.T) {
defer os.Remove(getCacheFileID(url))
want := [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}}
- have := getCSV(",", url)
+ have := f.getCSV(",", url)
assert.NotNil(t, have)
if have != nil {
assert.EqualValues(t, want, have)
@@ -279,7 +286,10 @@ func TestGetCSVFailParseSep(t *testing.T) {
}
func TestGetCSVFailParse(t *testing.T) {
- defer testRetryWhenDone().Reset()
+
+ f := newTestFuncster()
+
+ defer testRetryWhenDone(f).Reset()
reqCount := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -302,7 +312,7 @@ func TestGetCSVFailParse(t *testing.T) {
defer os.Remove(getCacheFileID(url))
want := [][]string{{"gomeetup", "city"}, {"yes", "Sydney"}, {"yes", "San Francisco"}, {"yes", "Stockholm"}}
- have := getCSV(",", url)
+ have := f.getCSV(",", url)
assert.NotNil(t, have)
if have != nil {
assert.EqualValues(t, want, have)
diff --git a/tpl/template_test.go b/tpl/template_test.go
index cf691858b..f22eb78ee 100644
--- a/tpl/template_test.go
+++ b/tpl/template_test.go
@@ -25,9 +25,21 @@ import (
"testing"
"github.com/spf13/afero"
+ "github.com/spf13/hugo/deps"
+ "github.com/spf13/hugo/helpers"
"github.com/spf13/hugo/hugofs"
+ "github.com/spf13/hugo/tplapi"
+ "github.com/spf13/viper"
+ "github.com/stretchr/testify/require"
)
+func testReset() {
+ viper.Reset()
+
+ // TODO(bep) viper-globals
+ viper.Set("currentContentLanguage", helpers.NewLanguage("en"))
+}
+
// Some tests for Issue #1178 -- Ace
func TestAceTemplates(t *testing.T) {
@@ -68,11 +80,19 @@ html lang=en
d := "DATA"
- templ := New(logger, func(templ Template) error {
+ config := defaultDepsConfig
+ config.WithTemplate = func(templ tplapi.Template) error {
return templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
[]byte(this.baseContent), []byte(this.innerContent))
+ }
- })
+ a := deps.New(config)
+
+ if err := a.LoadTemplates(); err != nil {
+ t.Fatal(err)
+ }
+
+ templ := a.Tmpl.(*GoHTMLTemplate)
if len(templ.errors) > 0 && this.expectErr == 0 {
t.Errorf("Test %d with root '%s' errored: %v", i, root, templ.errors)
@@ -81,7 +101,7 @@ html lang=en
}
var buff bytes.Buffer
- err := templ.ExecuteTemplate(&buff, "mytemplate.html", d)
+ err := a.Tmpl.ExecuteTemplate(&buff, "mytemplate.html", d)
if err != nil && this.expectErr == 0 {
t.Errorf("Test %d with root '%s' errored: %s", i, root, err)
@@ -93,6 +113,7 @@ html lang=en
t.Errorf("Test %d with root '%s' got\n%s\nexpected\n%s", i, root, result, this.expect)
}
}
+
}
}
@@ -124,53 +145,60 @@ func TestAddTemplateFileWithMaster(t *testing.T) {
{`tpl`, `{{.0.E}}`, 0, false},
} {
- hugofs.InitMemFs()
- templ := New(logger)
overlayTplName := "ot"
masterTplName := "mt"
finalTplName := "tp"
- if this.writeSkipper != 1 {
- afero.WriteFile(hugofs.Source(), masterTplName, []byte(this.masterTplContent), 0644)
- }
- if this.writeSkipper != 2 {
- afero.WriteFile(hugofs.Source(), overlayTplName, []byte(this.overlayTplContent), 0644)
- }
+ defaultDepsConfig.WithTemplate = func(templ tplapi.Template) error {
- err := templ.AddTemplateFileWithMaster(finalTplName, overlayTplName, masterTplName)
+ err := templ.AddTemplateFileWithMaster(finalTplName, overlayTplName, masterTplName)
- if b, ok := this.expect.(bool); ok && !b {
- if err == nil {
- t.Errorf("[%d] AddTemplateFileWithMaster didn't return an expected error", i)
- }
- } else {
+ if b, ok := this.expect.(bool); ok && !b {
+ if err == nil {
+ t.Errorf("[%d] AddTemplateFileWithMaster didn't return an expected error", i)
+ }
+ } else {
- if err != nil {
- t.Errorf("[%d] AddTemplateFileWithMaster failed: %s", i, err)
- continue
- }
+ if err != nil {
+ t.Errorf("[%d] AddTemplateFileWithMaster failed: %s", i, err)
+ return nil
+ }
- resultTpl := templ.Lookup(finalTplName)
+ resultTpl := templ.Lookup(finalTplName)
- if resultTpl == nil {
- t.Errorf("[%d] AddTemplateFileWithMaster: Result template not found", i)
- continue
- }
+ if resultTpl == nil {
+ t.Errorf("[%d] AddTemplateFileWithMaster: Result template not found", i)
+ return nil
+ }
- var b bytes.Buffer
- err := resultTpl.Execute(&b, nil)
+ var b bytes.Buffer
+ err := resultTpl.Execute(&b, nil)
- if err != nil {
- t.Errorf("[%d] AddTemplateFileWithMaster execute failed: %s", i, err)
- continue
- }
- resultContent := b.String()
+ if err != nil {
+ t.Errorf("[%d] AddTemplateFileWithMaster execute failed: %s", i, err)
+ return nil
+ }
+ resultContent := b.String()
- if resultContent != this.expect {
- t.Errorf("[%d] AddTemplateFileWithMaster got \n%s but expected \n%v", i, resultContent, this.expect)
+ if resultContent != this.expect {
+ t.Errorf("[%d] AddTemplateFileWithMaster got \n%s but expected \n%v", i, resultContent, this.expect)
+ }
}
+
+ return nil
}
+ defaultDepsConfig.Fs = hugofs.NewMem()
+
+ if this.writeSkipper != 1 {
+ afero.WriteFile(defaultDepsConfig.Fs.Source, masterTplName, []byte(this.masterTplContent), 0644)
+ }
+ if this.writeSkipper != 2 {
+ afero.WriteFile(defaultDepsConfig.Fs.Source, overlayTplName, []byte(this.overlayTplContent), 0644)
+ }
+
+ deps.New(defaultDepsConfig)
+
}
}
@@ -258,23 +286,29 @@ func TestTplGoFuzzReports(t *testing.T) {
H: "a,b,c,d,e,f",
}
- templ := New(logger, func(templ Template) error {
+ defaultDepsConfig.WithTemplate = func(templ tplapi.Template) error {
return templ.AddTemplate("fuzz", this.data)
+ }
+
+ de := deps.New(defaultDepsConfig)
+ require.NoError(t, de.LoadTemplates())
- })
+ templ := de.Tmpl.(*GoHTMLTemplate)
if len(templ.errors) > 0 && this.expectErr == 0 {
t.Errorf("Test %d errored: %v", i, templ.errors)
} else if len(templ.errors) == 0 && this.expectErr == 1 {
t.Errorf("#1 Test %d should have errored", i)
}
- err := templ.ExecuteTemplate(ioutil.Discard, "fuzz", d)
+
+ err := de.Tmpl.ExecuteTemplate(ioutil.Discard, "fuzz", d)
if err != nil && this.expectErr == 0 {
t.Fatalf("Test %d errored: %s", i, err)
} else if err == nil && this.expectErr == 2 {
t.Fatalf("#2 Test %d should have errored", i)
}
+
}
}
diff --git a/tplapi/template.go b/tplapi/template.go
new file mode 100644
index 000000000..58bc5ecf9
--- /dev/null
+++ b/tplapi/template.go
@@ -0,0 +1,28 @@
+package tplapi
+
+import (
+ "html/template"
+ "io"
+)
+
+// TODO(bep) make smaller
+// TODO(bep) consider putting this into /tpl and the implementation in /tpl/tplimpl or something
+type Template interface {
+ ExecuteTemplate(wr io.Writer, name string, data interface{}) error
+ ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML
+ Lookup(name string) *template.Template
+ Templates() []*template.Template
+ New(name string) *template.Template
+ GetClone() *template.Template
+ LoadTemplates(absPath string)
+ LoadTemplatesWithPrefix(absPath, prefix string)
+ AddTemplate(name, tpl string) error
+ AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error
+ AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error
+ AddInternalTemplate(prefix, name, tpl string) error
+ AddInternalShortcode(name, tpl string) error
+ Partial(name string, contextList ...interface{}) template.HTML
+ PrintErrors()
+ Funcs(funcMap template.FuncMap)
+ MarkReady()
+}