mirror of
				https://github.com/gohugoio/hugo.git
				synced 2024-05-11 05:54:58 +00:00 
			
		
		
		
	@@ -18,9 +18,11 @@ import (
 | 
				
			|||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gohugoio/hugo/common/types"
 | 
						"github.com/gohugoio/hugo/common/types"
 | 
				
			||||||
	"github.com/gohugoio/hugo/hugofs/files"
 | 
						"github.com/gohugoio/hugo/hugofs/files"
 | 
				
			||||||
 | 
						"github.com/gohugoio/hugo/identity"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var defaultPathParser PathParser
 | 
					var defaultPathParser PathParser
 | 
				
			||||||
@@ -50,19 +52,42 @@ func NormalizePathStringBasic(s string) string {
 | 
				
			|||||||
	return s
 | 
						return s
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ParseIdentity parses component c with path s into a StringIdentity.
 | 
				
			||||||
 | 
					func (pp *PathParser) ParseIdentity(c, s string) identity.StringIdentity {
 | 
				
			||||||
 | 
						s = NormalizePathStringBasic(s)
 | 
				
			||||||
 | 
						p := getPath()
 | 
				
			||||||
 | 
						p.component = c
 | 
				
			||||||
 | 
						defer putPath(p)
 | 
				
			||||||
 | 
						p, err := pp.doParse(c, s, p)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return identity.StringIdentity(p.IdentifierBase())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Parse parses component c with path s into Path using Hugo's content path rules.
 | 
					// Parse parses component c with path s into Path using Hugo's content path rules.
 | 
				
			||||||
func (parser PathParser) Parse(c, s string) *Path {
 | 
					func (pp *PathParser) Parse(c, s string) *Path {
 | 
				
			||||||
	p, err := parser.parse(c, s)
 | 
						p, err := pp.parse(c, s)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		panic(err)
 | 
							panic(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return p
 | 
						return p
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (pp *PathParser) newPath(component string) *Path {
 | 
				
			||||||
 | 
						return &Path{
 | 
				
			||||||
 | 
							component:             component,
 | 
				
			||||||
 | 
							posContainerLow:       -1,
 | 
				
			||||||
 | 
							posContainerHigh:      -1,
 | 
				
			||||||
 | 
							posSectionHigh:        -1,
 | 
				
			||||||
 | 
							posIdentifierLanguage: -1,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pp *PathParser) parse(component, s string) (*Path, error) {
 | 
					func (pp *PathParser) parse(component, s string) (*Path, error) {
 | 
				
			||||||
	ss := NormalizePathStringBasic(s)
 | 
						ss := NormalizePathStringBasic(s)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p, err := pp.doParse(component, ss)
 | 
						p, err := pp.doParse(component, ss, pp.newPath(component))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -70,7 +95,7 @@ func (pp *PathParser) parse(component, s string) (*Path, error) {
 | 
				
			|||||||
	if s != ss {
 | 
						if s != ss {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		// Preserve the original case for titles etc.
 | 
							// Preserve the original case for titles etc.
 | 
				
			||||||
		p.unnormalized, err = pp.doParse(component, s)
 | 
							p.unnormalized, err = pp.doParse(component, s, pp.newPath(component))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
@@ -82,15 +107,7 @@ func (pp *PathParser) parse(component, s string) (*Path, error) {
 | 
				
			|||||||
	return p, nil
 | 
						return p, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (pp *PathParser) doParse(component, s string) (*Path, error) {
 | 
					func (pp *PathParser) doParse(component, s string, p *Path) (*Path, error) {
 | 
				
			||||||
	p := &Path{
 | 
					 | 
				
			||||||
		component:             component,
 | 
					 | 
				
			||||||
		posContainerLow:       -1,
 | 
					 | 
				
			||||||
		posContainerHigh:      -1,
 | 
					 | 
				
			||||||
		posSectionHigh:        -1,
 | 
					 | 
				
			||||||
		posIdentifierLanguage: -1,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	hasLang := pp.LanguageIndex != nil
 | 
						hasLang := pp.LanguageIndex != nil
 | 
				
			||||||
	hasLang = hasLang && (component == files.ComponentFolderContent || component == files.ComponentFolderLayouts)
 | 
						hasLang = hasLang && (component == files.ComponentFolderContent || component == files.ComponentFolderLayouts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -220,6 +237,7 @@ const (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Path struct {
 | 
					type Path struct {
 | 
				
			||||||
 | 
						// Note: Any additions to this struct should also be added to the pathPool.
 | 
				
			||||||
	s string
 | 
						s string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	posContainerLow  int
 | 
						posContainerLow  int
 | 
				
			||||||
@@ -239,6 +257,31 @@ type Path struct {
 | 
				
			|||||||
	unnormalized *Path
 | 
						unnormalized *Path
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var pathPool = &sync.Pool{
 | 
				
			||||||
 | 
						New: func() any {
 | 
				
			||||||
 | 
							return &Path{}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getPath() *Path {
 | 
				
			||||||
 | 
						return pathPool.Get().(*Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func putPath(p *Path) {
 | 
				
			||||||
 | 
						p.s = ""
 | 
				
			||||||
 | 
						p.posContainerLow = -1
 | 
				
			||||||
 | 
						p.posContainerHigh = -1
 | 
				
			||||||
 | 
						p.posSectionHigh = -1
 | 
				
			||||||
 | 
						p.component = ""
 | 
				
			||||||
 | 
						p.bundleType = 0
 | 
				
			||||||
 | 
						p.identifiers = p.identifiers[:0]
 | 
				
			||||||
 | 
						p.posIdentifierLanguage = -1
 | 
				
			||||||
 | 
						p.disabled = false
 | 
				
			||||||
 | 
						p.trimLeadingSlash = false
 | 
				
			||||||
 | 
						p.unnormalized = nil
 | 
				
			||||||
 | 
						pathPool.Put(p)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TrimLeadingSlash returns a copy of the Path with the leading slash removed.
 | 
					// TrimLeadingSlash returns a copy of the Path with the leading slash removed.
 | 
				
			||||||
func (p Path) TrimLeadingSlash() *Path {
 | 
					func (p Path) TrimLeadingSlash() *Path {
 | 
				
			||||||
	p.trimLeadingSlash = true
 | 
						p.trimLeadingSlash = true
 | 
				
			||||||
@@ -254,7 +297,7 @@ func (p *Path) norm(s string) string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IdentifierBase satifies identity.Identity.
 | 
					// IdentifierBase satifies identity.Identity.
 | 
				
			||||||
func (p *Path) IdentifierBase() string {
 | 
					func (p *Path) IdentifierBase() string {
 | 
				
			||||||
	return p.Base()[1:]
 | 
						return p.Base()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Component returns the component for this path (e.g. "content").
 | 
					// Component returns the component for this path (e.g. "content").
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -349,3 +349,9 @@ func TestHasExt(t *testing.T) {
 | 
				
			|||||||
	c.Assert(HasExt("/a/b/c"), qt.IsFalse)
 | 
						c.Assert(HasExt("/a/b/c"), qt.IsFalse)
 | 
				
			||||||
	c.Assert(HasExt("/a/b.c/d"), qt.IsFalse)
 | 
						c.Assert(HasExt("/a/b.c/d"), qt.IsFalse)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func BenchmarkParseIdentity(b *testing.B) {
 | 
				
			||||||
 | 
						for i := 0; i < b.N; i++ {
 | 
				
			||||||
 | 
							testParser.ParseIdentity(files.ComponentFolderAssets, "/a/b.css")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -663,7 +663,7 @@ type Configs struct {
 | 
				
			|||||||
	// All below is set in Init.
 | 
						// All below is set in Init.
 | 
				
			||||||
	Languages             langs.Languages
 | 
						Languages             langs.Languages
 | 
				
			||||||
	LanguagesDefaultFirst langs.Languages
 | 
						LanguagesDefaultFirst langs.Languages
 | 
				
			||||||
	ContentPathParser     paths.PathParser
 | 
						ContentPathParser     *paths.PathParser
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	configLangs []config.AllProvider
 | 
						configLangs []config.AllProvider
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -735,7 +735,7 @@ func (c *Configs) Init() error {
 | 
				
			|||||||
	c.Languages = languages
 | 
						c.Languages = languages
 | 
				
			||||||
	c.LanguagesDefaultFirst = languagesDefaultFirst
 | 
						c.LanguagesDefaultFirst = languagesDefaultFirst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.ContentPathParser = paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled}
 | 
						c.ContentPathParser = &paths.PathParser{LanguageIndex: languagesDefaultFirst.AsIndexSet(), IsLangDisabled: c.Base.IsLangDisabled}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.configLangs = make([]config.AllProvider, len(c.Languages))
 | 
						c.configLangs = make([]config.AllProvider, len(c.Languages))
 | 
				
			||||||
	for i, l := range c.LanguagesDefaultFirst {
 | 
						for i, l := range c.LanguagesDefaultFirst {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ import (
 | 
				
			|||||||
	"github.com/gohugoio/hugo/common/paths"
 | 
						"github.com/gohugoio/hugo/common/paths"
 | 
				
			||||||
	"github.com/gohugoio/hugo/common/urls"
 | 
						"github.com/gohugoio/hugo/common/urls"
 | 
				
			||||||
	"github.com/gohugoio/hugo/config"
 | 
						"github.com/gohugoio/hugo/config"
 | 
				
			||||||
 | 
						"github.com/gohugoio/hugo/identity"
 | 
				
			||||||
	"github.com/gohugoio/hugo/langs"
 | 
						"github.com/gohugoio/hugo/langs"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -42,7 +43,7 @@ func (c ConfigLanguage) LanguagesDefaultFirst() langs.Languages {
 | 
				
			|||||||
	return c.m.LanguagesDefaultFirst
 | 
						return c.m.LanguagesDefaultFirst
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c ConfigLanguage) PathParser() paths.PathParser {
 | 
					func (c ConfigLanguage) PathParser() *paths.PathParser {
 | 
				
			||||||
	return c.m.ContentPathParser
 | 
						return c.m.ContentPathParser
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,6 +134,13 @@ func (c ConfigLanguage) Watching() bool {
 | 
				
			|||||||
	return c.m.Base.Internal.Watch
 | 
						return c.m.Base.Internal.Watch
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (c ConfigLanguage) NewIdentityManager(name string) identity.Manager {
 | 
				
			||||||
 | 
						if !c.Watching() {
 | 
				
			||||||
 | 
							return identity.NopManager
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return identity.NewManager(name)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetConfigSection is mostly used in tests. The switch statement isn't complete, but what's in use.
 | 
					// GetConfigSection is mostly used in tests. The switch statement isn't complete, but what's in use.
 | 
				
			||||||
func (c ConfigLanguage) GetConfigSection(s string) any {
 | 
					func (c ConfigLanguage) GetConfigSection(s string) any {
 | 
				
			||||||
	switch s {
 | 
						switch s {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import (
 | 
				
			|||||||
	"github.com/gohugoio/hugo/common/paths"
 | 
						"github.com/gohugoio/hugo/common/paths"
 | 
				
			||||||
	"github.com/gohugoio/hugo/common/types"
 | 
						"github.com/gohugoio/hugo/common/types"
 | 
				
			||||||
	"github.com/gohugoio/hugo/common/urls"
 | 
						"github.com/gohugoio/hugo/common/urls"
 | 
				
			||||||
 | 
						"github.com/gohugoio/hugo/identity"
 | 
				
			||||||
	"github.com/gohugoio/hugo/langs"
 | 
						"github.com/gohugoio/hugo/langs"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -31,7 +32,7 @@ type AllProvider interface {
 | 
				
			|||||||
	LanguagePrefix() string
 | 
						LanguagePrefix() string
 | 
				
			||||||
	BaseURL() urls.BaseURL
 | 
						BaseURL() urls.BaseURL
 | 
				
			||||||
	BaseURLLiveReload() urls.BaseURL
 | 
						BaseURLLiveReload() urls.BaseURL
 | 
				
			||||||
	PathParser() paths.PathParser
 | 
						PathParser() *paths.PathParser
 | 
				
			||||||
	Environment() string
 | 
						Environment() string
 | 
				
			||||||
	IsMultihost() bool
 | 
						IsMultihost() bool
 | 
				
			||||||
	IsMultiLingual() bool
 | 
						IsMultiLingual() bool
 | 
				
			||||||
@@ -57,6 +58,7 @@ type AllProvider interface {
 | 
				
			|||||||
	BuildDrafts() bool
 | 
						BuildDrafts() bool
 | 
				
			||||||
	Running() bool
 | 
						Running() bool
 | 
				
			||||||
	Watching() bool
 | 
						Watching() bool
 | 
				
			||||||
 | 
						NewIdentityManager(name string) identity.Manager
 | 
				
			||||||
	FastRenderMode() bool
 | 
						FastRenderMode() bool
 | 
				
			||||||
	PrintUnusedTemplates() bool
 | 
						PrintUnusedTemplates() bool
 | 
				
			||||||
	EnableMissingTranslationPlaceholders() bool
 | 
						EnableMissingTranslationPlaceholders() bool
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -241,7 +241,7 @@ type ComponentFsOptions struct {
 | 
				
			|||||||
	DefaultContentLanguage string
 | 
						DefaultContentLanguage string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// The parser used to parse paths provided by this filesystem.
 | 
						// The parser used to parse paths provided by this filesystem.
 | 
				
			||||||
	PathParser paths.PathParser
 | 
						PathParser *paths.PathParser
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (fs *componentFs) Open(name string) (afero.File, error) {
 | 
					func (fs *componentFs) Open(name string) (afero.File, error) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,8 +93,8 @@ func (r *resourceSource) GetIdentity() identity.Identity {
 | 
				
			|||||||
	return r.path
 | 
						return r.path
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *resourceSource) ForEeachIdentity(f func(identity.Identity) bool) {
 | 
					func (r *resourceSource) ForEeachIdentity(f func(identity.Identity) bool) bool {
 | 
				
			||||||
	f(r.GetIdentity())
 | 
						return f(r.GetIdentity())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *resourceSource) Path() string {
 | 
					func (r *resourceSource) Path() string {
 | 
				
			||||||
@@ -142,14 +142,15 @@ func (n resourceSources) GetIdentity() identity.Identity {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (n resourceSources) ForEeachIdentity(f func(identity.Identity) bool) {
 | 
					func (n resourceSources) ForEeachIdentity(f func(identity.Identity) bool) bool {
 | 
				
			||||||
	for _, r := range n {
 | 
						for _, r := range n {
 | 
				
			||||||
		if r != nil {
 | 
							if r != nil {
 | 
				
			||||||
			if f(r.GetIdentity()) {
 | 
								if f(r.GetIdentity()) {
 | 
				
			||||||
				return
 | 
									return true
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (cfg contentMapConfig) getTaxonomyConfig(s string) (v viewName) {
 | 
					func (cfg contentMapConfig) getTaxonomyConfig(s string) (v viewName) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -605,12 +605,15 @@ func (n contentNodeIs) GetIdentity() identity.Identity {
 | 
				
			|||||||
	return n[0].GetIdentity()
 | 
						return n[0].GetIdentity()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (n contentNodeIs) ForEeachIdentity(f func(identity.Identity) bool) {
 | 
					func (n contentNodeIs) ForEeachIdentity(f func(identity.Identity) bool) bool {
 | 
				
			||||||
	for _, nn := range n {
 | 
						for _, nn := range n {
 | 
				
			||||||
		if nn != nil {
 | 
							if nn != nil {
 | 
				
			||||||
			nn.ForEeachIdentity(f)
 | 
								if nn.ForEeachIdentity(f) {
 | 
				
			||||||
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (n contentNodeIs) resetBuildState() {
 | 
					func (n contentNodeIs) resetBuildState() {
 | 
				
			||||||
@@ -1151,7 +1154,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
 | 
				
			|||||||
			// First check the top level dependency manager.
 | 
								// First check the top level dependency manager.
 | 
				
			||||||
			for _, id := range changes {
 | 
								for _, id := range changes {
 | 
				
			||||||
				checkedCounter.Add(1)
 | 
									checkedCounter.Add(1)
 | 
				
			||||||
				if r := depsFinder.Contains(id, p.dependencyManager, 100); r > identity.FinderFoundOneOfManyRepetition {
 | 
									if r := depsFinder.Contains(id, p.dependencyManager, 2); r > identity.FinderFoundOneOfManyRepetition {
 | 
				
			||||||
					for _, po := range p.pageOutputs {
 | 
										for _, po := range p.pageOutputs {
 | 
				
			||||||
						resetPo(po, r)
 | 
											resetPo(po, r)
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
@@ -1167,7 +1170,7 @@ func (h *HugoSites) resolveAndResetDependententPageOutputs(ctx context.Context,
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
				for _, id := range changes {
 | 
									for _, id := range changes {
 | 
				
			||||||
					checkedCounter.Add(1)
 | 
										checkedCounter.Add(1)
 | 
				
			||||||
					if r := depsFinder.Contains(id, po.dependencyManagerOutput, 2); r > identity.FinderFoundOneOfManyRepetition {
 | 
										if r := depsFinder.Contains(id, po.dependencyManagerOutput, 50); r > identity.FinderFoundOneOfManyRepetition {
 | 
				
			||||||
						resetPo(po, r)
 | 
											resetPo(po, r)
 | 
				
			||||||
						continue OUTPUTS
 | 
											continue OUTPUTS
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -120,8 +120,8 @@ func (p *pageState) GetIdentity() identity.Identity {
 | 
				
			|||||||
	return p
 | 
						return p
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *pageState) ForEeachIdentity(f func(identity.Identity) bool) {
 | 
					func (p *pageState) ForEeachIdentity(f func(identity.Identity) bool) bool {
 | 
				
			||||||
	f(p)
 | 
						return f(p)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (p *pageState) GetDependencyManager() identity.Manager {
 | 
					func (p *pageState) GetDependencyManager() identity.Manager {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,7 +20,6 @@ import (
 | 
				
			|||||||
	"sync/atomic"
 | 
						"sync/atomic"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gohugoio/hugo/hugofs/files"
 | 
						"github.com/gohugoio/hugo/hugofs/files"
 | 
				
			||||||
	"github.com/gohugoio/hugo/identity"
 | 
					 | 
				
			||||||
	"github.com/gohugoio/hugo/resources"
 | 
						"github.com/gohugoio/hugo/resources"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gohugoio/hugo/common/maps"
 | 
						"github.com/gohugoio/hugo/common/maps"
 | 
				
			||||||
@@ -160,12 +159,6 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
 | 
				
			|||||||
			return nil, nil
 | 
								return nil, nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var dependencyManager identity.Manager = identity.NopManager
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		if m.s.conf.Internal.Watch {
 | 
					 | 
				
			||||||
			dependencyManager = identity.NewManager(m.Path())
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Parse the rest of the page content.
 | 
							// Parse the rest of the page content.
 | 
				
			||||||
		m.content, err = m.newCachedContent(h, pi)
 | 
							m.content, err = m.newCachedContent(h, pi)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -178,7 +171,7 @@ func (h *HugoSites) newPage(m *pageMeta) (*pageState, *paths.Path, error) {
 | 
				
			|||||||
			pageOutputTemplateVariationsState: &atomic.Uint32{},
 | 
								pageOutputTemplateVariationsState: &atomic.Uint32{},
 | 
				
			||||||
			resourcesPublishInit:              &sync.Once{},
 | 
								resourcesPublishInit:              &sync.Once{},
 | 
				
			||||||
			Staler:                            m,
 | 
								Staler:                            m,
 | 
				
			||||||
			dependencyManager:                 dependencyManager,
 | 
								dependencyManager:                 m.s.Conf.NewIdentityManager(m.Path()),
 | 
				
			||||||
			pageCommon: &pageCommon{
 | 
								pageCommon: &pageCommon{
 | 
				
			||||||
				FileProvider:              m,
 | 
									FileProvider:              m,
 | 
				
			||||||
				AuthorProvider:            m,
 | 
									AuthorProvider:            m,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,11 +51,6 @@ func newPageOutput(
 | 
				
			|||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var dependencyManager identity.Manager = identity.NopManager
 | 
					 | 
				
			||||||
	if ps.s.conf.Internal.Watch {
 | 
					 | 
				
			||||||
		dependencyManager = identity.NewManager(ps.Path() + "/" + f.Name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	providers := struct {
 | 
						providers := struct {
 | 
				
			||||||
		page.PaginatorProvider
 | 
							page.PaginatorProvider
 | 
				
			||||||
		resource.ResourceLinksProvider
 | 
							resource.ResourceLinksProvider
 | 
				
			||||||
@@ -75,7 +70,7 @@ func newPageOutput(
 | 
				
			|||||||
		TableOfContentsProvider: page.NopPage,
 | 
							TableOfContentsProvider: page.NopPage,
 | 
				
			||||||
		render:                  render,
 | 
							render:                  render,
 | 
				
			||||||
		paginator:               pag,
 | 
							paginator:               pag,
 | 
				
			||||||
		dependencyManagerOutput: dependencyManager,
 | 
							dependencyManagerOutput: ps.s.Conf.NewIdentityManager((ps.Path() + "/" + f.Name)),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return po
 | 
						return po
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1131,7 +1131,7 @@ Single.
 | 
				
			|||||||
			Running:         true,
 | 
								Running:         true,
 | 
				
			||||||
			NeedsOsFS:       true,
 | 
								NeedsOsFS:       true,
 | 
				
			||||||
			NeedsNpmInstall: true,
 | 
								NeedsNpmInstall: true,
 | 
				
			||||||
			// LogLevel:        logg.LevelTrace,
 | 
								// LogLevel:        logg.LevelDebug,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	).Build()
 | 
						).Build()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1261,3 +1261,35 @@ func BenchmarkRebuildContentFileChange(b *testing.B) {
 | 
				
			|||||||
		// fmt.Println(bb.LogString())
 | 
							// fmt.Println(bb.LogString())
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestRebuildConcat(t *testing.T) {
 | 
				
			||||||
 | 
						files := `
 | 
				
			||||||
 | 
					-- hugo.toml --
 | 
				
			||||||
 | 
					baseURL = "https://example.com"
 | 
				
			||||||
 | 
					disableLiveReload = true
 | 
				
			||||||
 | 
					disableKinds = ["taxonomy", "term", "sitemap", "robotsTXT", "404", "rss"]
 | 
				
			||||||
 | 
					-- assets/a.css --
 | 
				
			||||||
 | 
					a
 | 
				
			||||||
 | 
					-- assets/b.css --
 | 
				
			||||||
 | 
					b
 | 
				
			||||||
 | 
					-- assets/c.css --
 | 
				
			||||||
 | 
					c
 | 
				
			||||||
 | 
					-- assets/common/c1.css --
 | 
				
			||||||
 | 
					c1
 | 
				
			||||||
 | 
					-- assets/common/c2.css --
 | 
				
			||||||
 | 
					c2
 | 
				
			||||||
 | 
					-- layouts/index.html --
 | 
				
			||||||
 | 
					{{ $a := resources.Get "a.css" }}
 | 
				
			||||||
 | 
					{{ $b := resources.Get "b.css" }}
 | 
				
			||||||
 | 
					{{ $common := resources.Match "common/*.css" | resources.Concat "common.css" | minify }}
 | 
				
			||||||
 | 
					{{ $ab := slice $a $b $common | resources.Concat "ab.css" }}
 | 
				
			||||||
 | 
					all: {{ $ab.RelPermalink }}
 | 
				
			||||||
 | 
					`
 | 
				
			||||||
 | 
						b := TestRunning(t, files)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						b.AssertFileContent("public/ab.css", "abc1c2")
 | 
				
			||||||
 | 
						b.EditFileReplaceAll("assets/common/c2.css", "c2", "c2 edited").Build()
 | 
				
			||||||
 | 
						b.AssertFileContent("public/ab.css", "abc1c2 edited")
 | 
				
			||||||
 | 
						b.AddFiles("assets/common/c3.css", "c3").Build()
 | 
				
			||||||
 | 
						b.AssertFileContent("public/ab.css", "abc1c2 editedc3")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -169,12 +169,7 @@ func (f *Finder) checkManager(sid *searchID, m Manager, level int) FinderResult
 | 
				
			|||||||
		return r
 | 
							return r
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ids := m.getIdentities()
 | 
						r = f.search(sid, m, level)
 | 
				
			||||||
	if len(ids) == 0 {
 | 
					 | 
				
			||||||
		r = FinderNotFound
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		r = f.search(sid, ids, level)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if r == FinderFoundOneOfMany {
 | 
						if r == FinderFoundOneOfMany {
 | 
				
			||||||
		// Don't cache this one.
 | 
							// Don't cache this one.
 | 
				
			||||||
@@ -270,11 +265,7 @@ func (f *Finder) doCheckOne(sid *searchID, v Identity, depth int) FinderResult {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// search searches for id in ids.
 | 
					// search searches for id in ids.
 | 
				
			||||||
func (f *Finder) search(sid *searchID, ids Identities, depth int) FinderResult {
 | 
					func (f *Finder) search(sid *searchID, m Manager, depth int) FinderResult {
 | 
				
			||||||
	if len(ids) == 0 {
 | 
					 | 
				
			||||||
		return FinderNotFound
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	id := sid.id
 | 
						id := sid.id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if id == Anonymous {
 | 
						if id == Anonymous {
 | 
				
			||||||
@@ -285,19 +276,24 @@ func (f *Finder) search(sid *searchID, ids Identities, depth int) FinderResult {
 | 
				
			|||||||
		return FinderNotFound
 | 
							return FinderNotFound
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for v := range ids {
 | 
						var r FinderResult
 | 
				
			||||||
		r := f.checkOne(sid, v, depth)
 | 
						m.forEeachIdentity(
 | 
				
			||||||
		if r > 0 {
 | 
							func(v Identity) bool {
 | 
				
			||||||
			return r
 | 
								if r > 0 {
 | 
				
			||||||
		}
 | 
									panic("should be terminated")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		m := GetDependencyManager(v)
 | 
								r = f.checkOne(sid, v, depth)
 | 
				
			||||||
		if r := f.checkManager(sid, m, depth+1); r > 0 {
 | 
								if r > 0 {
 | 
				
			||||||
			return r
 | 
									return true
 | 
				
			||||||
		}
 | 
								}
 | 
				
			||||||
	}
 | 
								m := GetDependencyManager(v)
 | 
				
			||||||
 | 
								if r = f.checkManager(sid, m, depth+1); r > 0 {
 | 
				
			||||||
	return FinderNotFound
 | 
									return true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
						return r
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// FinderConfig provides configuration for the Finder.
 | 
					// FinderConfig provides configuration for the Finder.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,8 +55,8 @@ func NewManager(name string, opts ...ManagerOption) Manager {
 | 
				
			|||||||
// CleanString cleans s to be suitable as an identifier.
 | 
					// CleanString cleans s to be suitable as an identifier.
 | 
				
			||||||
func CleanString(s string) string {
 | 
					func CleanString(s string) string {
 | 
				
			||||||
	s = strings.ToLower(s)
 | 
						s = strings.ToLower(s)
 | 
				
			||||||
	s = strings.TrimPrefix(filepath.ToSlash(s), "/")
 | 
						s = strings.Trim(filepath.ToSlash(s), "/")
 | 
				
			||||||
	return path.Clean(s)
 | 
						return "/" + path.Clean(s)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CleanStringIdentity cleans s to be suitable as an identifier and wraps it in a StringIdentity.
 | 
					// CleanStringIdentity cleans s to be suitable as an identifier and wraps it in a StringIdentity.
 | 
				
			||||||
@@ -77,23 +77,6 @@ func GetDependencyManager(v any) Manager {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetDependencyManagerForScope returns the DependencyManager for the given scope from v or nil if none found.
 | 
					 | 
				
			||||||
// Note that it will fall back to an unscoped manager if none found for the given scope.
 | 
					 | 
				
			||||||
func GetDependencyManagerForScope(v any, scope int) Manager {
 | 
					 | 
				
			||||||
	switch vv := v.(type) {
 | 
					 | 
				
			||||||
	case DependencyManagerScopedProvider:
 | 
					 | 
				
			||||||
		return vv.GetDependencyManagerForScope(scope)
 | 
					 | 
				
			||||||
	case types.Unwrapper:
 | 
					 | 
				
			||||||
		return GetDependencyManagerForScope(vv.Unwrapv(), scope)
 | 
					 | 
				
			||||||
	case Manager:
 | 
					 | 
				
			||||||
		return vv
 | 
					 | 
				
			||||||
	case DependencyManagerProvider:
 | 
					 | 
				
			||||||
		return vv.GetDependencyManager()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FirstIdentity returns the first Identity in v, Anonymous if none found
 | 
					// FirstIdentity returns the first Identity in v, Anonymous if none found
 | 
				
			||||||
func FirstIdentity(v any) Identity {
 | 
					func FirstIdentity(v any) Identity {
 | 
				
			||||||
	var result Identity = Anonymous
 | 
						var result Identity = Anonymous
 | 
				
			||||||
@@ -169,7 +152,15 @@ type DependencyManagerScopedProvider interface {
 | 
				
			|||||||
type ForEeachIdentityProvider interface {
 | 
					type ForEeachIdentityProvider interface {
 | 
				
			||||||
	// ForEeachIdentityProvider calls cb for each Identity.
 | 
						// ForEeachIdentityProvider calls cb for each Identity.
 | 
				
			||||||
	// If cb returns true, the iteration is terminated.
 | 
						// If cb returns true, the iteration is terminated.
 | 
				
			||||||
	ForEeachIdentity(cb func(id Identity) bool)
 | 
						// The return value is whether the iteration was terminated.
 | 
				
			||||||
 | 
						ForEeachIdentity(cb func(id Identity) bool) bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ForEeachIdentityProviderFunc is a function that implements the ForEeachIdentityProvider interface.
 | 
				
			||||||
 | 
					type ForEeachIdentityProviderFunc func(func(id Identity) bool) bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (f ForEeachIdentityProviderFunc) ForEeachIdentity(cb func(id Identity) bool) bool {
 | 
				
			||||||
 | 
						return f(cb)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ForEeachIdentityByNameProvider provides a way to look up identities by name.
 | 
					// ForEeachIdentityByNameProvider provides a way to look up identities by name.
 | 
				
			||||||
@@ -279,9 +270,10 @@ type IsProbablyDependencyProvider interface {
 | 
				
			|||||||
type Manager interface {
 | 
					type Manager interface {
 | 
				
			||||||
	Identity
 | 
						Identity
 | 
				
			||||||
	AddIdentity(ids ...Identity)
 | 
						AddIdentity(ids ...Identity)
 | 
				
			||||||
 | 
						AddIdentityForEach(ids ...ForEeachIdentityProvider)
 | 
				
			||||||
	GetIdentity() Identity
 | 
						GetIdentity() Identity
 | 
				
			||||||
	Reset()
 | 
						Reset()
 | 
				
			||||||
	getIdentities() Identities
 | 
						forEeachIdentity(func(id Identity) bool) bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type ManagerOption func(m *identityManager)
 | 
					type ManagerOption func(m *identityManager)
 | 
				
			||||||
@@ -301,8 +293,9 @@ type identityManager struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// mu protects _changes_ to this manager,
 | 
						// mu protects _changes_ to this manager,
 | 
				
			||||||
	// reads currently assumes no concurrent writes.
 | 
						// reads currently assumes no concurrent writes.
 | 
				
			||||||
	mu  sync.RWMutex
 | 
						mu         sync.RWMutex
 | 
				
			||||||
	ids Identities
 | 
						ids        Identities
 | 
				
			||||||
 | 
						forEachIds []ForEeachIdentityProvider
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Hooks used in debugging.
 | 
						// Hooks used in debugging.
 | 
				
			||||||
	onAddIdentity func(id Identity)
 | 
						onAddIdentity func(id Identity)
 | 
				
			||||||
@@ -312,7 +305,7 @@ func (im *identityManager) AddIdentity(ids ...Identity) {
 | 
				
			|||||||
	im.mu.Lock()
 | 
						im.mu.Lock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, id := range ids {
 | 
						for _, id := range ids {
 | 
				
			||||||
		if id == Anonymous {
 | 
							if id == nil || id == Anonymous {
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if _, found := im.ids[id]; !found {
 | 
							if _, found := im.ids[id]; !found {
 | 
				
			||||||
@@ -325,6 +318,12 @@ func (im *identityManager) AddIdentity(ids ...Identity) {
 | 
				
			|||||||
	im.mu.Unlock()
 | 
						im.mu.Unlock()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (im *identityManager) AddIdentityForEach(ids ...ForEeachIdentityProvider) {
 | 
				
			||||||
 | 
						im.mu.Lock()
 | 
				
			||||||
 | 
						im.forEachIds = append(im.forEachIds, ids...)
 | 
				
			||||||
 | 
						im.mu.Unlock()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (im *identityManager) ContainsIdentity(id Identity) FinderResult {
 | 
					func (im *identityManager) ContainsIdentity(id Identity) FinderResult {
 | 
				
			||||||
	if im.Identity != Anonymous && id == im.Identity {
 | 
						if im.Identity != Anonymous && id == im.Identity {
 | 
				
			||||||
		return FinderFound
 | 
							return FinderFound
 | 
				
			||||||
@@ -355,10 +354,20 @@ func (im *identityManager) String() string {
 | 
				
			|||||||
	return fmt.Sprintf("IdentityManager(%s)", im.name)
 | 
						return fmt.Sprintf("IdentityManager(%s)", im.name)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO(bep) these identities are currently only read on server reloads
 | 
					func (im *identityManager) forEeachIdentity(fn func(id Identity) bool) bool {
 | 
				
			||||||
// so there should be no concurrency issues, but that may change.
 | 
						// The absense of a lock here is debliberate. This is currently opnly used on server reloads
 | 
				
			||||||
func (im *identityManager) getIdentities() Identities {
 | 
						// in a single-threaded context.
 | 
				
			||||||
	return im.ids
 | 
						for id := range im.ids {
 | 
				
			||||||
 | 
							if fn(id) {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, fe := range im.forEachIds {
 | 
				
			||||||
 | 
							if fe.ForEeachIdentity(fn) {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type nopManager int
 | 
					type nopManager int
 | 
				
			||||||
@@ -366,6 +375,9 @@ type nopManager int
 | 
				
			|||||||
func (m *nopManager) AddIdentity(ids ...Identity) {
 | 
					func (m *nopManager) AddIdentity(ids ...Identity) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (m *nopManager) AddIdentityForEach(ids ...ForEeachIdentityProvider) {
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *nopManager) IdentifierBase() string {
 | 
					func (m *nopManager) IdentifierBase() string {
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -377,8 +389,8 @@ func (m *nopManager) GetIdentity() Identity {
 | 
				
			|||||||
func (m *nopManager) Reset() {
 | 
					func (m *nopManager) Reset() {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (m *nopManager) getIdentities() Identities {
 | 
					func (m *nopManager) forEeachIdentity(func(id Identity) bool) bool {
 | 
				
			||||||
	return nil
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// returns whether further walking should be terminated.
 | 
					// returns whether further walking should be terminated.
 | 
				
			||||||
@@ -401,11 +413,9 @@ func walkIdentities(v any, level int, deep bool, seen map[Identity]bool, cb func
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if deep {
 | 
							if deep {
 | 
				
			||||||
			if m := GetDependencyManager(id); m != nil {
 | 
								if m := GetDependencyManager(id); m != nil {
 | 
				
			||||||
				for id2 := range m.getIdentities() {
 | 
									m.forEeachIdentity(func(id2 Identity) bool {
 | 
				
			||||||
					if walkIdentitiesShallow(id2, level+1, cbRecursive) {
 | 
										return walkIdentitiesShallow(id2, level+1, cbRecursive)
 | 
				
			||||||
						return true
 | 
									})
 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return false
 | 
							return false
 | 
				
			||||||
@@ -420,6 +430,9 @@ func walkIdentitiesShallow(v any, level int, cb func(level int, id Identity) boo
 | 
				
			|||||||
		if id == Anonymous {
 | 
							if id == Anonymous {
 | 
				
			||||||
			return false
 | 
								return false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if id == nil {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		return cb(level, id)
 | 
							return cb(level, id)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,7 +171,7 @@ func (fd *ResourceSourceDescriptor) init(r *Spec) error {
 | 
				
			|||||||
	fd.MediaType = mediaType
 | 
						fd.MediaType = mediaType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if fd.DependencyManager == nil {
 | 
						if fd.DependencyManager == nil {
 | 
				
			||||||
		fd.DependencyManager = identity.NopManager
 | 
							fd.DependencyManager = r.Cfg.NewIdentityManager("resource")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ func newResourceCache(rs *Spec, memCache *dynacache.Cache) *ResourceCache {
 | 
				
			|||||||
		cacheResources: dynacache.GetOrCreatePartition[string, resource.Resources](
 | 
							cacheResources: dynacache.GetOrCreatePartition[string, resource.Resources](
 | 
				
			||||||
			memCache,
 | 
								memCache,
 | 
				
			||||||
			"/ress",
 | 
								"/ress",
 | 
				
			||||||
			dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnChange, Weight: 40},
 | 
								dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnRebuild, Weight: 40},
 | 
				
			||||||
		),
 | 
							),
 | 
				
			||||||
		cacheResourceTransformation: dynacache.GetOrCreatePartition[string, *resourceAdapterInner](
 | 
							cacheResourceTransformation: dynacache.GetOrCreatePartition[string, *resourceAdapterInner](
 | 
				
			||||||
			memCache,
 | 
								memCache,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import (
 | 
				
			|||||||
	"path"
 | 
						"path"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/gohugoio/hugo/common/hugio"
 | 
						"github.com/gohugoio/hugo/common/hugio"
 | 
				
			||||||
 | 
						"github.com/gohugoio/hugo/identity"
 | 
				
			||||||
	"github.com/gohugoio/hugo/media"
 | 
						"github.com/gohugoio/hugo/media"
 | 
				
			||||||
	"github.com/gohugoio/hugo/resources"
 | 
						"github.com/gohugoio/hugo/resources"
 | 
				
			||||||
	"github.com/gohugoio/hugo/resources/resource"
 | 
						"github.com/gohugoio/hugo/resources/resource"
 | 
				
			||||||
@@ -86,13 +87,32 @@ func (c *Client) Concat(targetPath string, r resource.Resources) (resource.Resou
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		// The given set of resources must be of the same Media Type.
 | 
							// The given set of resources must be of the same Media Type.
 | 
				
			||||||
		// We may improve on that in the future, but then we need to know more.
 | 
							// We may improve on that in the future, but then we need to know more.
 | 
				
			||||||
		for i, r := range r {
 | 
							for i, rr := range r {
 | 
				
			||||||
			if i > 0 && r.MediaType().Type != resolvedm.Type {
 | 
								if i > 0 && rr.MediaType().Type != resolvedm.Type {
 | 
				
			||||||
				return nil, fmt.Errorf("resources in Concat must be of the same Media Type, got %q and %q", r.MediaType().Type, resolvedm.Type)
 | 
									return nil, fmt.Errorf("resources in Concat must be of the same Media Type, got %q and %q", rr.MediaType().Type, resolvedm.Type)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			resolvedm = r.MediaType()
 | 
								resolvedm = rr.MediaType()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							idm := c.rs.Cfg.NewIdentityManager("concat")
 | 
				
			||||||
 | 
							// Add the concatenated resources as dependencies to the composite resource
 | 
				
			||||||
 | 
							// so that we can track changes to the individual resources.
 | 
				
			||||||
 | 
							idm.AddIdentityForEach(identity.ForEeachIdentityProviderFunc(
 | 
				
			||||||
 | 
								func(f func(identity.Identity) bool) bool {
 | 
				
			||||||
 | 
									var terminate bool
 | 
				
			||||||
 | 
									for _, rr := range r {
 | 
				
			||||||
 | 
										identity.WalkIdentitiesShallow(rr, func(depth int, id identity.Identity) bool {
 | 
				
			||||||
 | 
											terminate = f(id)
 | 
				
			||||||
 | 
											return terminate
 | 
				
			||||||
 | 
										})
 | 
				
			||||||
 | 
										if terminate {
 | 
				
			||||||
 | 
											break
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return terminate
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		concatr := func() (hugio.ReadSeekCloser, error) {
 | 
							concatr := func() (hugio.ReadSeekCloser, error) {
 | 
				
			||||||
			var rcsources []hugio.ReadSeekCloser
 | 
								var rcsources []hugio.ReadSeekCloser
 | 
				
			||||||
			for _, s := range r {
 | 
								for _, s := range r {
 | 
				
			||||||
@@ -136,6 +156,7 @@ func (c *Client) Concat(targetPath string, r resource.Resources) (resource.Resou
 | 
				
			|||||||
				LazyPublish:        true,
 | 
									LazyPublish:        true,
 | 
				
			||||||
				OpenReadSeekCloser: concatr,
 | 
									OpenReadSeekCloser: concatr,
 | 
				
			||||||
				TargetPath:         targetPath,
 | 
									TargetPath:         targetPath,
 | 
				
			||||||
 | 
									DependencyManager:  idm,
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,13 +63,6 @@ func (c *Client) Copy(r resource.Resource, targetPath string) (resource.Resource
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Client) newDependencyManager() identity.Manager {
 | 
					 | 
				
			||||||
	if c.rs.Cfg.Running() {
 | 
					 | 
				
			||||||
		return identity.NewManager("resources")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return identity.NopManager
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Get creates a new Resource by opening the given pathname in the assets filesystem.
 | 
					// Get creates a new Resource by opening the given pathname in the assets filesystem.
 | 
				
			||||||
func (c *Client) Get(pathname string) (resource.Resource, error) {
 | 
					func (c *Client) Get(pathname string) (resource.Resource, error) {
 | 
				
			||||||
	pathname = path.Clean(pathname)
 | 
						pathname = path.Clean(pathname)
 | 
				
			||||||
@@ -79,7 +72,8 @@ func (c *Client) Get(pathname string) (resource.Resource, error) {
 | 
				
			|||||||
		// The resource file will not be read before it gets used (e.g. in .Content),
 | 
							// The resource file will not be read before it gets used (e.g. in .Content),
 | 
				
			||||||
		// so we need to check that the file exists here.
 | 
							// so we need to check that the file exists here.
 | 
				
			||||||
		filename := filepath.FromSlash(pathname)
 | 
							filename := filepath.FromSlash(pathname)
 | 
				
			||||||
		if _, err := c.rs.BaseFs.Assets.Fs.Stat(filename); err != nil {
 | 
							fi, err := c.rs.BaseFs.Assets.Fs.Stat(filename)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
			if os.IsNotExist(err) {
 | 
								if os.IsNotExist(err) {
 | 
				
			||||||
				return nil, nil
 | 
									return nil, nil
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -87,14 +81,16 @@ func (c *Client) Get(pathname string) (resource.Resource, error) {
 | 
				
			|||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pi := fi.(hugofs.FileMetaInfo).Meta().PathInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return c.rs.NewResource(resources.ResourceSourceDescriptor{
 | 
							return c.rs.NewResource(resources.ResourceSourceDescriptor{
 | 
				
			||||||
			LazyPublish: true,
 | 
								LazyPublish: true,
 | 
				
			||||||
			OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
 | 
								OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) {
 | 
				
			||||||
				return c.rs.BaseFs.Assets.Fs.Open(filename)
 | 
									return c.rs.BaseFs.Assets.Fs.Open(filename)
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			GroupIdentity:     identity.StringIdentity(key),
 | 
								Path:          pi,
 | 
				
			||||||
			DependencyManager: c.newDependencyManager(),
 | 
								GroupIdentity: pi,
 | 
				
			||||||
			TargetPath:        pathname,
 | 
								TargetPath:    pathname,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user