aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <[email protected]>2024-11-13 11:07:32 +0100
committerBjørn Erik Pedersen <[email protected]>2024-11-13 12:41:16 +0100
commita7df536a52912f34d7d20c970c38590bf5e0c513 (patch)
tree2773e4660f458f32d19784cfb5f557f3b7ffb6e2
parent3477d9fcec39d17f99cbc891e337658e8660f5db (diff)
downloadhugo-a7df536a52912f34d7d20c970c38590bf5e0c513.tar.gz
hugo-a7df536a52912f34d7d20c970c38590bf5e0c513.zip
Add site.Store and hugo.Store and Shortcode.Store
Closes #13021
-rw-r--r--common/hugo/hugo.go10
-rw-r--r--common/maps/scratch.go8
-rw-r--r--hugolib/page_test.go49
-rw-r--r--hugolib/shortcode.go34
-rw-r--r--hugolib/site.go6
-rw-r--r--resources/page/page.go4
-rw-r--r--resources/page/site.go10
7 files changed, 108 insertions, 13 deletions
diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go
index f21103940..dd43cf7b6 100644
--- a/common/hugo/hugo.go
+++ b/common/hugo/hugo.go
@@ -33,6 +33,7 @@ import (
"github.com/gohugoio/hugo/common/hcontext"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
+ "github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/hugofs/files"
"github.com/spf13/afero"
@@ -55,6 +56,8 @@ var (
vendorInfo string
)
+var _ maps.StoreProvider = (*HugoInfo)(nil)
+
// HugoInfo contains information about the current Hugo environment
type HugoInfo struct {
CommitHash string
@@ -72,6 +75,8 @@ type HugoInfo struct {
conf ConfigProvider
deps []*Dependency
+ store *maps.Scratch
+
// Context gives access to some of the context scoped variables.
Context Context
}
@@ -116,6 +121,10 @@ func (i HugoInfo) Deps() []*Dependency {
return i.deps
}
+func (i HugoInfo) Store() *maps.Scratch {
+ return i.store
+}
+
// Deprecated: Use hugo.IsMultihost instead.
func (i HugoInfo) IsMultiHost() bool {
Deprecate("hugo.IsMultiHost", "Use hugo.IsMultihost instead.", "v0.124.0")
@@ -185,6 +194,7 @@ func NewInfo(conf ConfigProvider, deps []*Dependency) HugoInfo {
Environment: conf.Environment(),
conf: conf,
deps: deps,
+ store: maps.NewScratch(),
GoVersion: goVersion,
}
}
diff --git a/common/maps/scratch.go b/common/maps/scratch.go
index 638377216..cf5231783 100644
--- a/common/maps/scratch.go
+++ b/common/maps/scratch.go
@@ -22,7 +22,13 @@ import (
"github.com/gohugoio/hugo/common/math"
)
-// Scratch is a writable context used for stateful operations in Page/Node rendering.
+type StoreProvider interface {
+ // Store returns a Scratch that can be used to store temporary state.
+ // Store is not reset on server rebuilds.
+ Store() *Scratch
+}
+
+// Scratch is a writable context used for stateful build operations
type Scratch struct {
values map[string]any
mu sync.RWMutex
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index bdd1be6f7..39a16d948 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -1893,3 +1893,52 @@ func TestRenderWithoutArgument(t *testing.T) {
b.Assert(err, qt.IsNotNil)
}
+
+// Issue #13021
+func TestAllStores(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- hugo.toml --
+disableKinds = ["taxonomy", "term", "page", "section"]
+disableLiveReload = true
+-- content/_index.md --
+---
+title: "Home"
+---
+{{< s >}}
+-- layouts/shortcodes/s.html --
+{{ if not (.Store.Get "Shortcode") }}{{ .Store.Set "Shortcode" (printf "sh-%s" $.Page.Title) }}{{ end }}
+Shortcode: {{ .Store.Get "Shortcode" }}|
+-- layouts/index.html --
+{{ .Content }}
+{{ if not (.Store.Get "Page") }}{{ .Store.Set "Page" (printf "p-%s" $.Title) }}{{ end }}
+{{ if not (hugo.Store.Get "Hugo") }}{{ hugo.Store.Set "Hugo" (printf "h-%s" $.Title) }}{{ end }}
+{{ if not (site.Store.Get "Site") }}{{ site.Store.Set "Site" (printf "s-%s" $.Title) }}{{ end }}
+Page: {{ .Store.Get "Page" }}|
+Hugo: {{ hugo.Store.Get "Hugo" }}|
+Site: {{ site.Store.Get "Site" }}|
+`
+
+ b := TestRunning(t, files)
+
+ b.AssertFileContent("public/index.html",
+ `
+Shortcode: sh-Home|
+Page: p-Home|
+Site: s-Home|
+Hugo: h-Home|
+`,
+ )
+
+ b.EditFileReplaceAll("content/_index.md", "Home", "Homer").Build()
+
+ b.AssertFileContent("public/index.html",
+ `
+Shortcode: sh-Homer|
+Page: p-Homer|
+Site: s-Home|
+Hugo: h-Home|
+`,
+ )
+}
diff --git a/hugolib/shortcode.go b/hugolib/shortcode.go
index 8a478c9df..69e891adb 100644
--- a/hugolib/shortcode.go
+++ b/hugolib/shortcode.go
@@ -43,9 +43,10 @@ import (
)
var (
- _ urls.RefLinker = (*ShortcodeWithPage)(nil)
- _ types.Unwrapper = (*ShortcodeWithPage)(nil)
- _ text.Positioner = (*ShortcodeWithPage)(nil)
+ _ urls.RefLinker = (*ShortcodeWithPage)(nil)
+ _ types.Unwrapper = (*ShortcodeWithPage)(nil)
+ _ text.Positioner = (*ShortcodeWithPage)(nil)
+ _ maps.StoreProvider = (*ShortcodeWithPage)(nil)
)
// ShortcodeWithPage is the "." context in a shortcode template.
@@ -72,7 +73,7 @@ type ShortcodeWithPage struct {
posOffset int
pos text.Position
- scratch *maps.Scratch
+ store *maps.Scratch
}
// InnerDeindent returns the (potentially de-indented) inner content of the shortcode.
@@ -124,13 +125,19 @@ func (scp *ShortcodeWithPage) RelRef(args map[string]any) (string, error) {
return scp.Page.RelRefFrom(args, scp)
}
+// Store returns this shortcode's Store.
+func (scp *ShortcodeWithPage) Store() *maps.Scratch {
+ if scp.store == nil {
+ scp.store = maps.NewScratch()
+ }
+ return scp.store
+}
+
// Scratch returns a scratch-pad scoped for this shortcode. This can be used
// as a temporary storage for variables, counters etc.
+// Deprecated: Use Store instead. Note that from the templates this should be considered a "soft deprecation".
func (scp *ShortcodeWithPage) Scratch() *maps.Scratch {
- if scp.scratch == nil {
- scp.scratch = maps.NewScratch()
- }
- return scp.scratch
+ return scp.Store()
}
// Get is a convenience method to look up shortcode parameters by its key.
@@ -399,7 +406,16 @@ func doRenderShortcode(
hasVariants = hasVariants || more
}
- data := &ShortcodeWithPage{Ordinal: sc.ordinal, posOffset: sc.pos, indentation: sc.indentation, Params: sc.params, Page: newPageForShortcode(p), Parent: parent, Name: sc.name}
+ data := &ShortcodeWithPage{
+ Ordinal: sc.ordinal,
+ posOffset: sc.pos,
+ indentation: sc.indentation,
+ Params: sc.params,
+ Page: newPageForShortcode(p),
+ Parent: parent,
+ Name: sc.name,
+ }
+
if sc.params != nil {
data.IsNamedParams = reflect.TypeOf(sc.params).Kind() == reflect.Map
}
diff --git a/hugolib/site.go b/hugolib/site.go
index c5a4956e2..c434ff2b4 100644
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -95,6 +95,7 @@ type Site struct {
language *langs.Language
languagei int
pageMap *pageMap
+ store *maps.Scratch
// The owning container.
h *HugoSites
@@ -248,6 +249,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
language: language,
languagei: i,
frontmatterHandler: frontmatterHandler,
+ store: maps.NewScratch(),
}
if i == 0 {
@@ -614,6 +616,10 @@ func (s *Site) AllRegularPages() page.Pages {
return s.h.RegularPages()
}
+func (s *Site) Store() *maps.Scratch {
+ return s.store
+}
+
func (s *Site) CheckReady() {
if s.state != siteStateReady {
panic("this method cannot be called before the site is fully initialized")
diff --git a/resources/page/page.go b/resources/page/page.go
index ea7f4bf1b..acd4ce313 100644
--- a/resources/page/page.go
+++ b/resources/page/page.go
@@ -331,9 +331,7 @@ type PageWithoutContent interface {
// Deprecated: From Hugo v0.138.0 this is just an alias for Store.
Scratch() *maps.Scratch
- // Store returns a Scratch that can be used to store temporary state.
- // In contrast to Scratch(), this Scratch is not reset on server rebuilds.
- Store() *maps.Scratch
+ maps.StoreProvider
RelatedKeywordsProvider
diff --git a/resources/page/site.go b/resources/page/site.go
index 9f7871a02..4a99982fd 100644
--- a/resources/page/site.go
+++ b/resources/page/site.go
@@ -135,6 +135,8 @@ type Site interface {
// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
RSSLink() template.URL
+ maps.StoreProvider
+
// For internal use only.
// This will panic if the site is not fully initialized.
// This is typically used to inform the user in the content adapter templates,
@@ -327,6 +329,10 @@ func (s *siteWrapper) RSSLink() template.URL {
return s.s.RSSLink()
}
+func (s *siteWrapper) Store() *maps.Scratch {
+ return s.s.Store()
+}
+
// For internal use only.
func (s *siteWrapper) ForEeachIdentityByName(name string, f func(identity.Identity) bool) {
s.s.(identity.ForEeachIdentityByNameProvider).ForEeachIdentityByName(name, f)
@@ -491,6 +497,10 @@ func (s testSite) RSSLink() template.URL {
return ""
}
+func (s testSite) Store() *maps.Scratch {
+ return maps.NewScratch()
+}
+
func (s testSite) CheckReady() {
}