diff --git a/hugofs/rootmapping_fs.go b/hugofs/rootmapping_fs.go index 02e541a05..388493174 100644 --- a/hugofs/rootmapping_fs.go +++ b/hugofs/rootmapping_fs.go @@ -311,12 +311,13 @@ func (fs *RootMappingFs) Open(name string) (afero.File, error) { // Stat returns the os.FileInfo structure describing a given file. If there is // an error, it will be of type *os.PathError. +// If multiple roots are found, the last one will be used. func (fs *RootMappingFs) Stat(name string) (os.FileInfo, error) { fis, err := fs.doStat(name) if err != nil { return nil, err } - return fis[0], nil + return fis[len(fis)-1], nil } type ComponentPath struct { diff --git a/resources/resource_cache.go b/resources/resource_cache.go index a3ba9aa26..898cd4c31 100644 --- a/resources/resource_cache.go +++ b/resources/resource_cache.go @@ -36,6 +36,11 @@ func newResourceCache(rs *Spec, memCache *dynacache.Cache) *ResourceCache { "/res1", dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnChange, Weight: 40}, ), + cacheResourceFile: dynacache.GetOrCreatePartition[string, resource.Resource]( + memCache, + "/res2", + dynacache.OptionsPartition{ClearWhen: dynacache.ClearOnChange, Weight: 40}, + ), CacheResourceRemote: dynacache.GetOrCreatePartition[string, resource.Resource]( memCache, "/resr", @@ -58,6 +63,7 @@ type ResourceCache struct { sync.RWMutex cacheResource *dynacache.Partition[string, resource.Resource] + cacheResourceFile *dynacache.Partition[string, resource.Resource] CacheResourceRemote *dynacache.Partition[string, resource.Resource] cacheResources *dynacache.Partition[string, resource.Resources] cacheResourceTransformation *dynacache.Partition[string, *resourceAdapterInner] @@ -79,6 +85,12 @@ func (c *ResourceCache) GetOrCreate(key string, f func() (resource.Resource, err }) } +func (c *ResourceCache) GetOrCreateFile(key string, f func() (resource.Resource, error)) (resource.Resource, error) { + return c.cacheResourceFile.GetOrCreate(key, func(key string) (resource.Resource, error) { + return f() + }) +} + func (c *ResourceCache) GetOrCreateResources(key string, f func() (resource.Resources, error)) (resource.Resources, error) { return c.cacheResources.GetOrCreate(key, func(key string) (resource.Resources, error) { return f() diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go index 7dd26f4c0..581c0a854 100644 --- a/resources/resource_factories/create/create.go +++ b/resources/resource_factories/create/create.go @@ -143,19 +143,7 @@ func (c *Client) Get(pathname string) (resource.Resource, error) { return nil, err } - meta := fi.(hugofs.FileMetaInfo).Meta() - pi := meta.PathInfo - - return c.rs.NewResource(resources.ResourceSourceDescriptor{ - LazyPublish: true, - OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { - return c.rs.BaseFs.Assets.Fs.Open(filename) - }, - Path: pi, - GroupIdentity: pi, - TargetPath: pathname, - SourceFilenameOrPath: meta.Filename, - }) + return c.getOrCreateFileResource(fi.(hugofs.FileMetaInfo)) }) } @@ -181,6 +169,23 @@ func (c *Client) GetMatch(pattern string) (resource.Resource, error) { return res[0], err } +func (c *Client) getOrCreateFileResource(info hugofs.FileMetaInfo) (resource.Resource, error) { + meta := info.Meta() + return c.rs.ResourceCache.GetOrCreateFile(filepath.ToSlash(meta.Filename), func() (resource.Resource, error) { + return c.rs.NewResource(resources.ResourceSourceDescriptor{ + LazyPublish: true, + OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { + return meta.Open() + }, + NameNormalized: meta.PathInfo.Path(), + NameOriginal: meta.PathInfo.Unnormalized().Path(), + GroupIdentity: meta.PathInfo, + TargetPath: meta.PathInfo.Unnormalized().Path(), + SourceFilenameOrPath: meta.Filename, + }) + }) +} + func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource) bool, firstOnly bool) (resource.Resources, error) { pattern = glob.NormalizePath(pattern) partitions := glob.FilterGlobParts(strings.Split(pattern, "/")) @@ -191,19 +196,7 @@ func (c *Client) match(name, pattern string, matchFunc func(r resource.Resource) var res resource.Resources handle := func(info hugofs.FileMetaInfo) (bool, error) { - meta := info.Meta() - - r, err := c.rs.NewResource(resources.ResourceSourceDescriptor{ - LazyPublish: true, - OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { - return meta.Open() - }, - NameNormalized: meta.PathInfo.Path(), - NameOriginal: meta.PathInfo.Unnormalized().Path(), - GroupIdentity: meta.PathInfo, - TargetPath: meta.PathInfo.Unnormalized().Path(), - SourceFilenameOrPath: meta.Filename, - }) + r, err := c.getOrCreateFileResource(info) if err != nil { return true, err } diff --git a/testscripts/commands/hugo__path-warnings_issue13164.txt b/testscripts/commands/hugo__path-warnings_issue13164.txt new file mode 100644 index 000000000..1342c287a --- /dev/null +++ b/testscripts/commands/hugo__path-warnings_issue13164.txt @@ -0,0 +1,15 @@ +hugo --printPathWarnings + +! stderr 'Duplicate target paths' + +-- hugo.toml -- +disableKinds = ['page','rss','section','sitemap','taxonomy','term'] +-- assets/foo.txt -- +foo +-- layouts/index.html -- +A: {{ (resources.Get "foo.txt").RelPermalink }} +B: {{ (resources.GetMatch "foo.txt").RelPermalink }} +C: {{ (index (resources.Match "foo.txt") 0).RelPermalink }} +D: {{ (index (resources.ByType "text") 0).RelPermalink }} +-- layouts/unused/single.html -- +{{ .Title }}