Skip to content

Commit

Permalink
Fix some server rebuild issues for non-HTML custom output formats
Browse files Browse the repository at this point in the history
The failing test case here is

* A custom search output format defined on the home page, marked as ´noAlternative` and not `permalinkable`
* In fast render mode, when making a change to a data source for that search output format, the JSON file was not refreshed.

There are variants of the above, but the gist of it is:

* The change set was correctly determined, but since the search JSON file was not in the recently visited browser stack, we skipped rendering it.

Running with `hugo server --disableFastRender` would be a workaround for the above.

This commit fixes this by:

* Adding a check for the HTTP request header `Sec-Fetch-Mode = navigation` to the condition for if we should track server request as a user navigation (and not e.g. a HTTP request for a linked CSS stylesheet).
* Making sure that we compare against the real relative URL for non-permalinkable output formats.

Fixes #13014
  • Loading branch information
bep committed Jan 24, 2025
1 parent a563783 commit 8b8295d
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 19 deletions.
6 changes: 5 additions & 1 deletion commands/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,11 @@ func (f *fileServer) createEndpoint(i int) (*http.ServeMux, net.Listener, string
}

if f.c.fastRenderMode && f.c.errState.buildErr() == nil {
if strings.HasSuffix(requestURI, "/") || strings.HasSuffix(requestURI, "html") || strings.HasSuffix(requestURI, "htm") {
// Sec-Fetch-Dest = document
// Sec-Fetch-Mode should be sent by all recent browser versions, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Mode#navigate
// Fall back to the file extension if not set.
// The main take here is that we don't want to have CSS/JS files etc. partake in this logic.
if r.Header.Get("Sec-Fetch-Mode") == "navigate" || strings.HasSuffix(requestURI, "/") || strings.HasSuffix(requestURI, "html") || strings.HasSuffix(requestURI, "htm") {
if !f.c.visitedURLs.Contains(requestURI) {
// If not already on stack, re-render that single page.
if err := f.c.partialReRender(requestURI); err != nil {
Expand Down
3 changes: 3 additions & 0 deletions common/types/evictingqueue.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ func (q *EvictingStringQueue) Peek() string {

// PeekAll looks at all the elements in the queue, with the newest first.
func (q *EvictingStringQueue) PeekAll() []string {
if q == nil {
return nil
}
q.mu.Lock()
vals := make([]string, len(q.vals))
copy(vals, q.vals)
Expand Down
11 changes: 6 additions & 5 deletions hugolib/hugo_sites.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ type BuildCfg struct {

// shouldRender returns whether this output format should be rendered or not.
func (cfg *BuildCfg) shouldRender(p *pageState) bool {
// TODO1
if p.skipRender() {
return false
}
Expand Down Expand Up @@ -457,18 +458,18 @@ func (cfg *BuildCfg) shouldRender(p *pageState) bool {
return false
}

if p.outputFormat().IsHTML {
// This is fast render mode and the output format is HTML,
// rerender if this page is one of the recently visited.
return cfg.RecentlyVisited.Contains(p.RelPermalink())
if relURL := p.getRelURL(); relURL != "" {
if cfg.RecentlyVisited.Contains(relURL) {
return true
}
}

// In fast render mode, we want to avoid re-rendering the sitemaps etc. and
// other big listings whenever we e.g. change a content file,
// but we want partial renders of the recently visited pages to also include
// alternative formats of the same HTML page (e.g. RSS, JSON).
for _, po := range p.pageOutputs {
if po.render && po.f.IsHTML && cfg.RecentlyVisited.Contains(po.RelPermalink()) {
if po.render && po.f.IsHTML && cfg.RecentlyVisited.Contains(po.getRelURL()) {
return true
}
}
Expand Down
11 changes: 0 additions & 11 deletions hugolib/hugo_sites_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ import (
"github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/common/rungroup"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/page/siteidentities"
"github.com/gohugoio/hugo/resources/postpub"

Expand Down Expand Up @@ -902,16 +901,6 @@ func (h *HugoSites) processPartialFileEvents(ctx context.Context, l logg.LevelLo

needsPagesAssemble = true

if config.RecentlyVisited != nil {
// Fast render mode. Adding them to the visited queue
// avoids rerendering them on navigation.
for _, id := range changes {
if p, ok := id.(page.Page); ok {
config.RecentlyVisited.Add(p.RelPermalink())
}
}
}

h.pageTrees.treeTaxonomyEntries.DeletePrefix("")

if delete && !isContentDataFile {
Expand Down
8 changes: 7 additions & 1 deletion hugolib/page__paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,17 @@ func newPagePaths(ps *pageState) (pagePaths, error) {
// Use the main format for permalinks, usually HTML.
permalinksIndex := 0
if f.Permalinkable {
// Unless it's permalinkable
// Unless it's permalinkable.
permalinksIndex = i
}

relURL := relPermalink
if relURL == "" {
relURL = paths.RelPermalink(s.PathSpec)
}

targets[f.Name] = targetPathsHolder{
relURL: relURL,
paths: paths,
OutputFormat: pageOutputFormats[permalinksIndex],
}
Expand Down
10 changes: 9 additions & 1 deletion hugolib/page__per_output.go
Original file line number Diff line number Diff line change
Expand Up @@ -469,13 +469,21 @@ type pagePerOutputProviders interface {

type targetPather interface {
targetPaths() page.TargetPaths
getRelURL() string
}

type targetPathsHolder struct {
paths page.TargetPaths
// relURL is usually the same as OutputFormat.RelPermalink, but can be different
// for non-permalinkable output formats. These shares RelPermalink with the main (first) output format.
relURL string
paths page.TargetPaths
page.OutputFormat
}

func (t targetPathsHolder) getRelURL() string {
return t.relURL
}

func (t targetPathsHolder) targetPaths() page.TargetPaths {
return t.paths
}
Expand Down
43 changes: 43 additions & 0 deletions hugolib/rebuild_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1181,6 +1181,49 @@ Content: {{ .Content }}
b.AssertFileContent("public/index.html", "Content: <p>Home</p>")
}

// Issue #13014.
func TestRebuildEditNotPermalinkableCustomOutputFormatTemplateInFastRenderMode(t *testing.T) {
t.Parallel()

files := `
-- hugo.toml --
baseURL = "https://example.com/docs/"
disableLiveReload = true
[internal]
fastRenderMode = true
disableKinds = ["taxonomy", "term", "sitemap", "robotsTXT", "404"]
[outputFormats]
[outputFormats.SearchIndex]
baseName = 'Search'
isPlainText = true
mediaType = 'text/plain'
noAlternative = true
permalinkable = false
[outputs]
home = ['HTML', 'SearchIndex']
-- content/_index.md --
---
title: "Home"
---
Home.
-- layouts/index.html --
Home.
-- layouts/_default/index.searchindex.txt --
Text. {{ .Title }}|{{ .RelPermalink }}|
`
b := TestRunning(t, files, TestOptInfo())

b.AssertFileContent("public/search.txt", "Text.")

b.EditFileReplaceAll("layouts/_default/index.searchindex.txt", "Text.", "Text Edited.").Build()

b.BuildPartial("/docs/search.txt")

b.AssertFileContent("public/search.txt", "Text Edited.")
}

func TestRebuildVariationsAssetsJSImport(t *testing.T) {
t.Parallel()
files := `
Expand Down

0 comments on commit 8b8295d

Please sign in to comment.