summaryrefslogtreecommitdiffstats
path: root/hugolib
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-12-20 08:11:36 +0100
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2019-12-20 11:38:44 +0100
commit8a58ebb311fd079f65068e7e37725e4d43f17ab5 (patch)
treeb214284b27d2a2cf35ba0689bea688bdd0597e5f /hugolib
parent0453683816cfbc94e1e19c644f5f84213bb8cf35 (diff)
hugolib: Improve error and reload handling of hook templates in server mode
Fixes #6635
Diffstat (limited to 'hugolib')
-rw-r--r--hugolib/content_render_hooks_test.go54
-rw-r--r--hugolib/hugo_sites.go19
-rw-r--r--hugolib/page.go7
-rw-r--r--hugolib/site.go17
-rw-r--r--hugolib/testhelpers_test.go32
5 files changed, 111 insertions, 18 deletions
diff --git a/hugolib/content_render_hooks_test.go b/hugolib/content_render_hooks_test.go
index d206013ba..ee7a02074 100644
--- a/hugolib/content_render_hooks_test.go
+++ b/hugolib/content_render_hooks_test.go
@@ -158,6 +158,60 @@ SHORT3|
}
+func TestRenderHooksDeleteTemplate(t *testing.T) {
+ config := `
+baseURL="https://example.org"
+workingDir="/mywork"
+`
+ b := newTestSitesBuilder(t).WithWorkingDir("/mywork").WithConfigFile("toml", config).Running()
+ b.WithTemplatesAdded("_default/single.html", `{{ .Content }}`)
+ b.WithTemplatesAdded("_default/_markup/render-link.html", `html-render-link`)
+
+ b.WithContent("p1.md", `---
+title: P1
+---
+[First Link](https://www.google.com "Google's Homepage")
+
+`)
+ b.Build(BuildCfg{})
+
+ b.AssertFileContent("public/p1/index.html", `<p>html-render-link</p>`)
+
+ b.RemoveFiles(
+ "layouts/_default/_markup/render-link.html",
+ )
+
+ b.Build(BuildCfg{})
+ b.AssertFileContent("public/p1/index.html", `<p><a href="https://www.google.com" title="Google's Homepage">First Link</a></p>`)
+
+}
+
+func TestRenderHookAddTemplate(t *testing.T) {
+ config := `
+baseURL="https://example.org"
+workingDir="/mywork"
+`
+ b := newTestSitesBuilder(t).WithWorkingDir("/mywork").WithConfigFile("toml", config).Running()
+ b.WithTemplatesAdded("_default/single.html", `{{ .Content }}`)
+
+ b.WithContent("p1.md", `---
+title: P1
+---
+[First Link](https://www.google.com "Google's Homepage")
+
+`)
+ b.Build(BuildCfg{})
+
+ b.AssertFileContent("public/p1/index.html", `<p><a href="https://www.google.com" title="Google's Homepage">First Link</a></p>`)
+
+ b.EditFiles("layouts/_default/_markup/render-link.html", `html-render-link`)
+
+ b.Build(BuildCfg{})
+
+ b.AssertFileContent("public/p1/index.html", `<p>html-render-link</p>`)
+
+}
+
func TestRenderHooksRSS(t *testing.T) {
b := newTestSitesBuilder(t)
diff --git a/hugolib/hugo_sites.go b/hugolib/hugo_sites.go
index 526f39fca..8c29e2a88 100644
--- a/hugolib/hugo_sites.go
+++ b/hugolib/hugo_sites.go
@@ -564,6 +564,9 @@ type BuildCfg struct {
// we should skip most of the processing.
PartialReRender bool
+ // Set in server mode when the last build failed for some reason.
+ ErrRecovery bool
+
// Recently visited URLs. This is used for partial re-rendering.
RecentlyVisited map[string]bool
}
@@ -807,8 +810,20 @@ func (h *HugoSites) findPagesByKindIn(kind string, inPages page.Pages) page.Page
return h.Sites[0].findPagesByKindIn(kind, inPages)
}
-func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) {
+func (h *HugoSites) resetPageState() {
+ for _, s := range h.Sites {
+ for _, p := range s.rawAllPages {
+ for _, po := range p.pageOutputs {
+ if po.cp == nil {
+ continue
+ }
+ po.cp.Reset()
+ }
+ }
+ }
+}
+func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) {
for _, s := range h.Sites {
PAGES:
for _, p := range s.rawAllPages {
@@ -820,7 +835,6 @@ func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) {
for id, _ := range idset {
if po.cp.dependencyTracker.Search(id) != nil {
po.cp.Reset()
- p.forceRender = true
continue OUTPUTS
}
}
@@ -834,7 +848,6 @@ func (h *HugoSites) resetPageStateFromEvents(idset identity.Identities) {
po.cp.Reset()
}
}
- p.forceRender = true
continue PAGES
}
}
diff --git a/hugolib/page.go b/hugolib/page.go
index fb3b597be..af3deb59f 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -629,9 +629,12 @@ func (p *pageState) Render(layout ...string) (template.HTML, error) {
}
-// wrapError adds some more context to the given error if possible
+// wrapError adds some more context to the given error if possible/needed
func (p *pageState) wrapError(err error) error {
-
+ if _, ok := err.(*herrors.ErrorWithFileContext); ok {
+ // Preserve the first file context.
+ return err
+ }
var filename string
if !p.File().IsZero() {
filename = p.File().Filename()
diff --git a/hugolib/site.go b/hugolib/site.go
index 866ff5624..eb232c629 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -909,6 +909,7 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
contentFilesChanged []string
tmplChanged bool
+ tmplAdded bool
dataChanged bool
i18nChanged bool
@@ -934,8 +935,16 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
logger.Println("Source changed", ev)
sourceChanged = append(sourceChanged, ev)
case files.ComponentFolderLayouts:
- logger.Println("Template changed", ev)
tmplChanged = true
+ if _, found := s.Tmpl.Lookup(id.Path); !found {
+ tmplAdded = true
+ }
+ if tmplAdded {
+ logger.Println("Template added", ev)
+ } else {
+ logger.Println("Template changed", ev)
+ }
+
case files.ComponentFolderData:
logger.Println("Data changed", ev)
dataChanged = true
@@ -1021,7 +1030,11 @@ func (s *Site) processPartial(config *BuildCfg, init func(config *BuildCfg) erro
sourceFilesChanged[ev.Name] = true
}
- h.resetPageStateFromEvents(changeIdentities)
+ if config.ErrRecovery || tmplAdded {
+ h.resetPageState()
+ } else {
+ h.resetPageStateFromEvents(changeIdentities)
+ }
if len(sourceReallyChanged) > 0 || len(contentFilesChanged) > 0 {
var filenamesChanged []string
diff --git a/hugolib/testhelpers_test.go b/hugolib/testhelpers_test.go
index 80aafe052..93ea9477c 100644
--- a/hugolib/testhelpers_test.go
+++ b/hugolib/testhelpers_test.go
@@ -68,6 +68,7 @@ type sitesBuilder struct {
// Used to test partial rebuilds.
changedFiles []string
+ removedFiles []string
// Aka the Hugo server mode.
running bool
@@ -386,16 +387,22 @@ func (s *sitesBuilder) WithI18nAdded(filenameContent ...string) *sitesBuilder {
}
func (s *sitesBuilder) EditFiles(filenameContent ...string) *sitesBuilder {
- var changedFiles []string
for i := 0; i < len(filenameContent); i += 2 {
filename, content := filepath.FromSlash(filenameContent[i]), filenameContent[i+1]
absFilename := s.absFilename(filename)
- changedFiles = append(changedFiles, absFilename)
+ s.changedFiles = append(s.changedFiles, absFilename)
writeSource(s.T, s.Fs, absFilename, content)
}
- s.changedFiles = changedFiles
+ return s
+}
+func (s *sitesBuilder) RemoveFiles(filenames ...string) *sitesBuilder {
+ for _, filename := range filenames {
+ absFilename := s.absFilename(filename)
+ s.removedFiles = append(s.removedFiles, absFilename)
+ s.Assert(s.Fs.Source.Remove(absFilename), qt.IsNil)
+ }
return s
}
@@ -523,17 +530,20 @@ func (s *sitesBuilder) BuildFail(cfg BuildCfg) *sitesBuilder {
}
func (s *sitesBuilder) changeEvents() []fsnotify.Event {
- if len(s.changedFiles) == 0 {
- return nil
- }
- events := make([]fsnotify.Event, len(s.changedFiles))
- // TODO(bep) remove?
- for i, v := range s.changedFiles {
- events[i] = fsnotify.Event{
+ var events []fsnotify.Event
+
+ for _, v := range s.changedFiles {
+ events = append(events, fsnotify.Event{
Name: v,
Op: fsnotify.Write,
- }
+ })
+ }
+ for _, v := range s.removedFiles {
+ events = append(events, fsnotify.Event{
+ Name: v,
+ Op: fsnotify.Remove,
+ })
}
return events