summaryrefslogtreecommitdiffstats
path: root/resources/resource_spec.go
diff options
context:
space:
mode:
Diffstat (limited to 'resources/resource_spec.go')
-rw-r--r--resources/resource_spec.go285
1 files changed, 55 insertions, 230 deletions
diff --git a/resources/resource_spec.go b/resources/resource_spec.go
index 3e1b53205..66f56d147 100644
--- a/resources/resource_spec.go
+++ b/resources/resource_spec.go
@@ -14,54 +14,44 @@
package resources
import (
- "errors"
- "fmt"
- "mime"
- "os"
"path"
- "path/filepath"
- "strings"
"sync"
- "github.com/BurntSushi/locker"
"github.com/gohugoio/hugo/config"
"github.com/gohugoio/hugo/config/allconfig"
"github.com/gohugoio/hugo/output"
+ "github.com/gohugoio/hugo/resources/internal"
"github.com/gohugoio/hugo/resources/jsconfig"
"github.com/gohugoio/hugo/common/herrors"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
+ "github.com/gohugoio/hugo/common/paths"
"github.com/gohugoio/hugo/identity"
"github.com/gohugoio/hugo/helpers"
- "github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/resources/postpub"
+ "github.com/gohugoio/hugo/cache/dynacache"
"github.com/gohugoio/hugo/cache/filecache"
"github.com/gohugoio/hugo/media"
"github.com/gohugoio/hugo/resources/images"
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/resource"
"github.com/gohugoio/hugo/tpl"
- "github.com/spf13/afero"
)
func NewSpec(
s *helpers.PathSpec,
common *SpecCommon, // may be nil
- imageCache *ImageCache, // may be nil
+ fileCaches filecache.Caches,
+ memCache *dynacache.Cache,
incr identity.Incrementer,
logger loggers.Logger,
errorHandler herrors.ErrorSender,
- execHelper *hexec.Exec) (*Spec, error) {
-
- fileCaches, err := filecache.NewCaches(s)
- if err != nil {
- return nil, fmt.Errorf("failed to create file caches from configuration: %w", err)
- }
-
+ execHelper *hexec.Exec,
+) (*Spec, error) {
conf := s.Cfg.GetConfig().(*allconfig.Config)
imgConfig := conf.Imaging
@@ -91,37 +81,28 @@ func NewSpec(
PostProcessResources: make(map[string]postpub.PostPublishedResource),
JSConfigBuilder: jsconfig.NewBuilder(),
},
- ResourceCache: &ResourceCache{
- fileCache: fileCaches.AssetsCache(),
- cache: make(map[string]any),
- nlocker: locker.NewLocker(),
- },
}
}
- if imageCache == nil {
- imageCache = newImageCache(
- fileCaches.ImageCache(),
- s,
- )
- } else {
- imageCache = imageCache.WithPathSpec(s)
-
- }
-
rs := &Spec{
PathSpec: s,
Logger: logger,
ErrorSender: errorHandler,
imaging: imaging,
- ImageCache: imageCache,
- ExecHelper: execHelper,
+ ImageCache: newImageCache(
+ fileCaches.ImageCache(),
+ memCache,
+ s,
+ ),
+ ExecHelper: execHelper,
Permalinks: permalinks,
SpecCommon: common,
}
+ rs.ResourceCache = newResourceCache(rs, memCache)
+
return rs, nil
}
@@ -162,221 +143,65 @@ type PostBuildAssets struct {
JSConfigBuilder *jsconfig.Builder
}
-func (r *Spec) New(fd ResourceSourceDescriptor) (resource.Resource, error) {
- return r.newResourceFor(fd)
-}
-
-func (r *Spec) MediaTypes() media.Types {
- return r.Cfg.GetConfigSection("mediaTypes").(media.Types)
-}
-
-func (r *Spec) OutputFormats() output.Formats {
- return r.Cfg.GetConfigSection("outputFormats").(output.Formats)
-}
-
-func (r *Spec) BuildConfig() config.BuildConfig {
- return r.Cfg.GetConfigSection("build").(config.BuildConfig)
-}
-
-func (r *Spec) CacheStats() string {
- r.ImageCache.mu.RLock()
- defer r.ImageCache.mu.RUnlock()
-
- s := fmt.Sprintf("Cache entries: %d", len(r.ImageCache.store))
-
- count := 0
- for k := range r.ImageCache.store {
- if count > 5 {
- break
- }
- s += "\n" + k
- count++
- }
-
- return s
-}
-
-func (r *Spec) ClearCaches() {
- r.ImageCache.clear()
- r.ResourceCache.clear()
-}
-
-func (r *Spec) DeleteBySubstring(s string) {
- r.ImageCache.deleteIfContains(s)
-}
-
-func (s *Spec) String() string {
- return "spec"
-}
-
-// TODO(bep) clean up below
-func (r *Spec) newGenericResource(sourceFs afero.Fs,
- targetPathBuilder func() page.TargetPaths,
- osFileInfo os.FileInfo,
- sourceFilename,
- baseFilename string,
- mediaType media.Type) *genericResource {
- return r.newGenericResourceWithBase(
- sourceFs,
- nil,
- nil,
- targetPathBuilder,
- osFileInfo,
- sourceFilename,
- baseFilename,
- mediaType,
- nil,
- )
-}
-
-func (r *Spec) newGenericResourceWithBase(
- sourceFs afero.Fs,
- openReadSeekerCloser resource.OpenReadSeekCloser,
- targetPathBaseDirs []string,
- targetPathBuilder func() page.TargetPaths,
- osFileInfo os.FileInfo,
- sourceFilename,
- baseFilename string,
- mediaType media.Type,
- data map[string]any,
-) *genericResource {
- if osFileInfo != nil && osFileInfo.IsDir() {
- panic(fmt.Sprintf("dirs not supported resource types: %v", osFileInfo))
- }
-
- // This value is used both to construct URLs and file paths, but start
- // with a Unix-styled path.
- baseFilename = helpers.ToSlashTrimLeading(baseFilename)
- fpath, fname := path.Split(baseFilename)
-
- resourceType := mediaType.MainType
-
- pathDescriptor := &resourcePathDescriptor{
- baseTargetPathDirs: helpers.UniqueStringsReuse(targetPathBaseDirs),
- targetPathBuilder: targetPathBuilder,
- relTargetDirFile: dirFile{dir: fpath, file: fname},
- }
-
- var fim hugofs.FileMetaInfo
- if osFileInfo != nil {
- fim = osFileInfo.(hugofs.FileMetaInfo)
- }
-
- gfi := &resourceFileInfo{
- fi: fim,
- openReadSeekerCloser: openReadSeekerCloser,
- sourceFs: sourceFs,
- sourceFilename: sourceFilename,
- h: &resourceHash{},
- }
-
- g := &genericResource{
- resourceFileInfo: gfi,
- resourcePathDescriptor: pathDescriptor,
- mediaType: mediaType,
- resourceType: resourceType,
- spec: r,
- params: make(map[string]any),
- name: baseFilename,
- title: baseFilename,
- resourceContent: &resourceContent{},
- data: data,
+// NewResource creates a new Resource from the given ResourceSourceDescriptor.
+func (r *Spec) NewResource(rd ResourceSourceDescriptor) (resource.Resource, error) {
+ if err := rd.init(r); err != nil {
+ return nil, err
}
- return g
-}
-
-func (r *Spec) newResource(sourceFs afero.Fs, fd ResourceSourceDescriptor) (resource.Resource, error) {
- fi := fd.FileInfo
- var sourceFilename string
-
- if fd.OpenReadSeekCloser != nil {
- } else if fd.SourceFilename != "" {
- var err error
- fi, err = sourceFs.Stat(fd.SourceFilename)
- if err != nil {
- if herrors.IsNotExist(err) {
- return nil, nil
- }
- return nil, err
- }
- sourceFilename = fd.SourceFilename
- } else {
- sourceFilename = fd.SourceFile.Filename()
+ dir, name := path.Split(rd.TargetPath)
+ dir = paths.ToSlashPreserveLeading(dir)
+ if dir == "/" {
+ dir = ""
}
-
- if fd.RelTargetFilename == "" {
- fd.RelTargetFilename = sourceFilename
+ rp := internal.ResourcePaths{
+ File: name,
+ Dir: dir,
+ BaseDirTarget: rd.BasePathTargetPath,
+ BaseDirLink: rd.BasePathRelPermalink,
+ TargetBasePaths: rd.TargetBasePaths,
}
- mimeType := fd.MediaType
- if mimeType.IsZero() {
- ext := strings.ToLower(filepath.Ext(fd.RelTargetFilename))
- var (
- found bool
- suffixInfo media.SuffixInfo
- )
- mimeType, suffixInfo, found = r.MediaTypes().GetFirstBySuffix(strings.TrimPrefix(ext, "."))
- // TODO(bep) we need to handle these ambiguous types better, but in this context
- // we most likely want the application/xml type.
- if suffixInfo.Suffix == "xml" && mimeType.SubType == "rss" {
- mimeType, found = r.MediaTypes().GetByType("application/xml")
- }
-
- if !found {
- // A fallback. Note that mime.TypeByExtension is slow by Hugo standards,
- // so we should configure media types to avoid this lookup for most
- // situations.
- mimeStr := mime.TypeByExtension(ext)
- if mimeStr != "" {
- mimeType, _ = media.FromStringAndExt(mimeStr, ext)
- }
- }
+ gr := &genericResource{
+ Staler: &AtomicStaler{},
+ h: &resourceHash{},
+ paths: rp,
+ spec: r,
+ sd: rd,
+ params: make(map[string]any),
+ name: rd.Name,
+ title: rd.Name,
+ resourceContent: &resourceContent{},
}
- gr := r.newGenericResourceWithBase(
- sourceFs,
- fd.OpenReadSeekCloser,
- fd.TargetBasePaths,
- fd.TargetPaths,
- fi,
- sourceFilename,
- fd.RelTargetFilename,
- mimeType,
- fd.Data)
-
- if mimeType.MainType == "image" {
- imgFormat, ok := images.ImageFormatFromMediaSubType(mimeType.SubType)
+ if rd.MediaType.MainType == "image" {
+ imgFormat, ok := images.ImageFormatFromMediaSubType(rd.MediaType.SubType)
if ok {
ir := &imageResource{
Image: images.NewImage(imgFormat, r.imaging, nil, gr),
baseResource: gr,
}
ir.root = ir
- return newResourceAdapter(gr.spec, fd.LazyPublish, ir), nil
+ return newResourceAdapter(gr.spec, rd.LazyPublish, ir), nil
}
+
}
- return newResourceAdapter(gr.spec, fd.LazyPublish, gr), nil
+ return newResourceAdapter(gr.spec, rd.LazyPublish, gr), nil
}
-func (r *Spec) newResourceFor(fd ResourceSourceDescriptor) (resource.Resource, error) {
- if fd.OpenReadSeekCloser == nil {
- if fd.SourceFile != nil && fd.SourceFilename != "" {
- return nil, errors.New("both SourceFile and AbsSourceFilename provided")
- } else if fd.SourceFile == nil && fd.SourceFilename == "" {
- return nil, errors.New("either SourceFile or AbsSourceFilename must be provided")
- }
- }
+func (r *Spec) MediaTypes() media.Types {
+ return r.Cfg.GetConfigSection("mediaTypes").(media.Types)
+}
- if fd.RelTargetFilename == "" {
- fd.RelTargetFilename = fd.Filename()
- }
+func (r *Spec) OutputFormats() output.Formats {
+ return r.Cfg.GetConfigSection("outputFormats").(output.Formats)
+}
- if len(fd.TargetBasePaths) == 0 {
- // If not set, we publish the same resource to all hosts.
- fd.TargetBasePaths = r.MultihostTargetBasePaths
- }
+func (r *Spec) BuildConfig() config.BuildConfig {
+ return r.Cfg.GetConfigSection("build").(config.BuildConfig)
+}
- return r.newResource(fd.Fs, fd)
+func (s *Spec) String() string {
+ return "spec"
}