mirror of
https://github.com/gohugoio/hugo.git
synced 2025-04-10 23:37:51 +00:00
parent
7966c0b5b7
commit
f7375c4972
8 changed files with 154 additions and 25 deletions
|
@ -761,16 +761,16 @@ func (h *HugoSites) createPageCollections() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Site) preparePagesForRender(idx int) error {
|
func (s *Site) preparePagesForRender(isRenderingSite bool, idx int) error {
|
||||||
|
|
||||||
for _, p := range s.workAllPages {
|
for _, p := range s.workAllPages {
|
||||||
if err := p.initOutputFormat(idx); err != nil {
|
if err := p.initOutputFormat(isRenderingSite, idx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range s.headlessPages {
|
for _, p := range s.headlessPages {
|
||||||
if err := p.initOutputFormat(idx); err != nil {
|
if err := p.initOutputFormat(isRenderingSite, idx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -288,7 +288,7 @@ func (h *HugoSites) render(config *BuildCfg) error {
|
||||||
// needs this set.
|
// needs this set.
|
||||||
s2.rc = &siteRenderingContext{Format: renderFormat}
|
s2.rc = &siteRenderingContext{Format: renderFormat}
|
||||||
|
|
||||||
if err := s2.preparePagesForRender(siteRenderContext.sitesOutIdx); err != nil {
|
if err := s2.preparePagesForRender(s == s2, siteRenderContext.sitesOutIdx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
77
hugolib/hugo_sites_rebuild_test.go
Normal file
77
hugolib/hugo_sites_rebuild_test.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright 2019 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 (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSitesRebuild(t *testing.T) {
|
||||||
|
|
||||||
|
configFile := `
|
||||||
|
baseURL = "https://example.com"
|
||||||
|
title = "Rebuild this"
|
||||||
|
contentDir = "content"
|
||||||
|
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
contentFilename := "content/blog/page1.md"
|
||||||
|
|
||||||
|
b := newTestSitesBuilder(t).WithConfigFile("toml", configFile)
|
||||||
|
|
||||||
|
// To simulate https://github.com/gohugoio/hugo/issues/5838, the home page
|
||||||
|
// needs a content page.
|
||||||
|
b.WithContent("content/_index.md", `---
|
||||||
|
title: Home, Sweet Home!
|
||||||
|
---
|
||||||
|
|
||||||
|
`)
|
||||||
|
|
||||||
|
b.WithContent(contentFilename, `
|
||||||
|
---
|
||||||
|
title: "Page 1"
|
||||||
|
summary: "Initial summary"
|
||||||
|
paginate: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
Content.
|
||||||
|
|
||||||
|
`)
|
||||||
|
|
||||||
|
b.WithTemplatesAdded("index.html", `
|
||||||
|
{{ range (.Paginate .Site.RegularPages).Pages }}
|
||||||
|
* Page: {{ .Title }}|Summary: {{ .Summary }}|Content: {{ .Content }}
|
||||||
|
{{ end }}
|
||||||
|
`)
|
||||||
|
|
||||||
|
b.Running().Build(BuildCfg{})
|
||||||
|
|
||||||
|
b.AssertFileContent("public/index.html", "* Page: Page 1|Summary: Initial summary|Content: <p>Content.</p>")
|
||||||
|
|
||||||
|
b.EditFiles(contentFilename, `
|
||||||
|
---
|
||||||
|
title: "Page 1 edit"
|
||||||
|
summary: "Edited summary"
|
||||||
|
---
|
||||||
|
|
||||||
|
Edited content.
|
||||||
|
|
||||||
|
`)
|
||||||
|
|
||||||
|
b.Build(BuildCfg{})
|
||||||
|
|
||||||
|
b.AssertFileContent("public/index.html", "* Page: Page 1 edit|Summary: Edited summary|Content: <p>Edited content.</p>")
|
||||||
|
|
||||||
|
}
|
|
@ -332,8 +332,8 @@ func (p *pageState) getLayouts(layouts ...string) ([]string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is serialized
|
// This is serialized
|
||||||
func (p *pageState) initOutputFormat(idx int) error {
|
func (p *pageState) initOutputFormat(isRenderingSite bool, idx int) error {
|
||||||
if err := p.shiftToOutputFormat(idx); err != nil {
|
if err := p.shiftToOutputFormat(isRenderingSite, idx); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,7 +700,7 @@ func (p *pageState) posOffset(offset int) text.Position {
|
||||||
|
|
||||||
// shiftToOutputFormat is serialized. The output format idx refers to the
|
// shiftToOutputFormat is serialized. The output format idx refers to the
|
||||||
// full set of output formats for all sites.
|
// full set of output formats for all sites.
|
||||||
func (p *pageState) shiftToOutputFormat(idx int) error {
|
func (p *pageState) shiftToOutputFormat(isRenderingSite bool, idx int) error {
|
||||||
if err := p.initPage(); err != nil {
|
if err := p.initPage(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -715,6 +715,12 @@ func (p *pageState) shiftToOutputFormat(idx int) error {
|
||||||
panic(fmt.Sprintf("pageOutput is nil for output idx %d", idx))
|
panic(fmt.Sprintf("pageOutput is nil for output idx %d", idx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset any built paginator. This will trigger when re-rendering pages in
|
||||||
|
// server mode.
|
||||||
|
if isRenderingSite && p.pageOutput.paginator != nil && p.pageOutput.paginator.current != nil {
|
||||||
|
p.pageOutput.paginator.reset()
|
||||||
|
}
|
||||||
|
|
||||||
if idx > 0 {
|
if idx > 0 {
|
||||||
// Check if we can reuse content from one of the previous formats.
|
// Check if we can reuse content from one of the previous formats.
|
||||||
for i := idx - 1; i >= 0; i-- {
|
for i := idx - 1; i >= 0; i-- {
|
||||||
|
@ -728,7 +734,7 @@ func (p *pageState) shiftToOutputFormat(idx int) error {
|
||||||
|
|
||||||
for _, r := range p.Resources().ByType(pageResourceType) {
|
for _, r := range p.Resources().ByType(pageResourceType) {
|
||||||
rp := r.(*pageState)
|
rp := r.(*pageState)
|
||||||
if err := rp.shiftToOutputFormat(idx); err != nil {
|
if err := rp.shiftToOutputFormat(isRenderingSite, idx); err != nil {
|
||||||
return errors.Wrap(err, "failed to shift outputformat in Page resource")
|
return errors.Wrap(err, "failed to shift outputformat in Page resource")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ func newPageOutput(
|
||||||
var pag *pagePaginator
|
var pag *pagePaginator
|
||||||
|
|
||||||
if render && ps.IsNode() {
|
if render && ps.IsNode() {
|
||||||
pag = &pagePaginator{source: ps}
|
pag = newPagePaginator(ps)
|
||||||
paginatorProvider = pag
|
paginatorProvider = pag
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,16 +19,31 @@ import (
|
||||||
"github.com/gohugoio/hugo/resources/page"
|
"github.com/gohugoio/hugo/resources/page"
|
||||||
)
|
)
|
||||||
|
|
||||||
type pagePaginator struct {
|
func newPagePaginator(source *pageState) *pagePaginator {
|
||||||
paginatorInit sync.Once
|
return &pagePaginator{
|
||||||
current *page.Pager
|
source: source,
|
||||||
|
pagePaginatorInit: &pagePaginatorInit{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type pagePaginator struct {
|
||||||
|
*pagePaginatorInit
|
||||||
source *pageState
|
source *pageState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type pagePaginatorInit struct {
|
||||||
|
init sync.Once
|
||||||
|
current *page.Pager
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset resets the paginator to allow for a rebuild.
|
||||||
|
func (p *pagePaginator) reset() {
|
||||||
|
p.pagePaginatorInit = &pagePaginatorInit{}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *pagePaginator) Paginate(seq interface{}, options ...interface{}) (*page.Pager, error) {
|
func (p *pagePaginator) Paginate(seq interface{}, options ...interface{}) (*page.Pager, error) {
|
||||||
var initErr error
|
var initErr error
|
||||||
p.paginatorInit.Do(func() {
|
p.init.Do(func() {
|
||||||
pagerSize, err := page.ResolvePagerSize(p.source.s.Cfg, options...)
|
pagerSize, err := page.ResolvePagerSize(p.source.s.Cfg, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
initErr = err
|
initErr = err
|
||||||
|
@ -56,7 +71,7 @@ func (p *pagePaginator) Paginate(seq interface{}, options ...interface{}) (*page
|
||||||
|
|
||||||
func (p *pagePaginator) Paginator(options ...interface{}) (*page.Pager, error) {
|
func (p *pagePaginator) Paginator(options ...interface{}) (*page.Pager, error) {
|
||||||
var initErr error
|
var initErr error
|
||||||
p.paginatorInit.Do(func() {
|
p.init.Do(func() {
|
||||||
pagerSize, err := page.ResolvePagerSize(p.source.s.Cfg, options...)
|
pagerSize, err := page.ResolvePagerSize(p.source.s.Cfg, options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
initErr = err
|
initErr = err
|
||||||
|
@ -81,7 +96,3 @@ func (p *pagePaginator) Paginator(options ...interface{}) (*page.Pager, error) {
|
||||||
|
|
||||||
return p.current, nil
|
return p.current, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *pagePaginator) rewind() {
|
|
||||||
p.current = p.current.First()
|
|
||||||
}
|
|
||||||
|
|
|
@ -171,12 +171,9 @@ func (s *Site) renderPaginator(p *pageState, layouts []string) error {
|
||||||
f := p.s.rc.Format
|
f := p.s.rc.Format
|
||||||
d.Type = f
|
d.Type = f
|
||||||
|
|
||||||
// Rewind
|
if p.paginator.current == nil || p.paginator.current != p.paginator.current.First() {
|
||||||
p.paginator.rewind()
|
panic(fmt.Sprintf("invalid paginator state for %q", p.pathOrTitle()))
|
||||||
defer func() {
|
}
|
||||||
// Prepare for any re-rendering in server mode.
|
|
||||||
p.paginator.rewind()
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Write alias for page 1
|
// Write alias for page 1
|
||||||
d.Addends = fmt.Sprintf("/%s/%d", paginatePath, 1)
|
d.Addends = fmt.Sprintf("/%s/%d", paginatePath, 1)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
"github.com/gohugoio/hugo/common/herrors"
|
"github.com/gohugoio/hugo/common/herrors"
|
||||||
"github.com/gohugoio/hugo/config"
|
"github.com/gohugoio/hugo/config"
|
||||||
"github.com/gohugoio/hugo/deps"
|
"github.com/gohugoio/hugo/deps"
|
||||||
|
@ -45,6 +46,9 @@ type sitesBuilder struct {
|
||||||
|
|
||||||
dumper litter.Options
|
dumper litter.Options
|
||||||
|
|
||||||
|
// Used to test partial rebuilds.
|
||||||
|
changedFiles []string
|
||||||
|
|
||||||
// Aka the Hugo server mode.
|
// Aka the Hugo server mode.
|
||||||
running bool
|
running bool
|
||||||
|
|
||||||
|
@ -296,6 +300,19 @@ func (s *sitesBuilder) WithI18nAdded(filenameContent ...string) *sitesBuilder {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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]
|
||||||
|
changedFiles = append(changedFiles, filename)
|
||||||
|
writeSource(s.T, s.Fs, filename, content)
|
||||||
|
|
||||||
|
}
|
||||||
|
s.changedFiles = changedFiles
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
func (s *sitesBuilder) writeFilePairs(folder string, filenameContent []string) *sitesBuilder {
|
func (s *sitesBuilder) writeFilePairs(folder string, filenameContent []string) *sitesBuilder {
|
||||||
if len(filenameContent)%2 != 0 {
|
if len(filenameContent)%2 != 0 {
|
||||||
s.Fatalf("expect filenameContent for %q in pairs (%d)", folder, len(filenameContent))
|
s.Fatalf("expect filenameContent for %q in pairs (%d)", folder, len(filenameContent))
|
||||||
|
@ -376,12 +393,33 @@ func (s *sitesBuilder) BuildFail(cfg BuildCfg) *sitesBuilder {
|
||||||
return s.build(cfg, true)
|
return s.build(cfg, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{
|
||||||
|
Name: v,
|
||||||
|
Op: fsnotify.Write,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return events
|
||||||
|
}
|
||||||
|
|
||||||
func (s *sitesBuilder) build(cfg BuildCfg, shouldFail bool) *sitesBuilder {
|
func (s *sitesBuilder) build(cfg BuildCfg, shouldFail bool) *sitesBuilder {
|
||||||
|
defer func() {
|
||||||
|
s.changedFiles = nil
|
||||||
|
}()
|
||||||
|
|
||||||
if s.H == nil {
|
if s.H == nil {
|
||||||
s.CreateSites()
|
s.CreateSites()
|
||||||
}
|
}
|
||||||
|
|
||||||
err := s.H.Build(cfg)
|
err := s.H.Build(cfg, s.changeEvents()...)
|
||||||
|
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logErrorCount := s.H.NumLogErrors()
|
logErrorCount := s.H.NumLogErrors()
|
||||||
|
|
Loading…
Add table
Reference in a new issue