From ce8a09a4c0661dece931ab1173e4f09e8e04aa38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Wed, 2 Jan 2019 11:58:32 +0100 Subject: [PATCH] resources: Move resource interfaces into its own package --- deps/deps.go | 8 +- hugolib/collections.go | 2 +- hugolib/page.go | 2 +- hugolib/page_output.go | 2 +- hugolib/page_resource.go | 2 +- hugolib/pagebundler_handlers.go | 7 +- hugolib/pages_language_merge_test.go | 2 +- hugolib/resource_chain_test.go | 2 +- hugolib/site.go | 4 +- {resource => resources}/image.go | 14 +- {resource => resources}/image_cache.go | 4 +- {resource => resources}/image_test.go | 4 +- resources/internal/glob.go | 48 ++++ {resource => resources}/resource.go | 266 ++---------------- resources/resource/resources.go | 123 ++++++++ resources/resource/resourcetypes.go | 106 +++++++ {resource => resources}/resource_cache.go | 18 +- .../resource_factories/bundler/bundler.go | 19 +- .../resource_factories/create/create.go | 17 +- {resource => resources}/resource_metadata.go | 11 +- .../resource_metadata_test.go | 11 +- {resource => resources}/resource_test.go | 22 +- .../integrity/integrity.go | 13 +- .../resource_transformers/minifier/minify.go | 15 +- .../resource_transformers/postcss/postcss.go | 15 +- .../templates/execute_as_template.go | 15 +- .../tocss/scss/client.go | 12 +- .../resource_transformers/tocss/scss/tocss.go | 4 +- .../tocss/scss/tocss_notavailable.go | 4 +- {resource => resources}/smartcrop.go | 4 +- ...opasdfghjklzxcvbnm5to6eeeeee7via8eleph.jpg | Bin {resource => resources}/testdata/circle.svg | 0 {resource => resources}/testdata/gohugoio.png | Bin .../testdata/sub/gohugoio2.png | Bin {resource => resources}/testdata/sunset.jpg | Bin {resource => resources}/testhelpers_test.go | 7 +- {resource => resources}/transform.go | 25 +- {resource => resources}/transform_test.go | 4 +- tpl/resources/resources.go | 18 +- tpl/transform/unmarshal.go | 4 +- tpl/transform/unmarshal_test.go | 4 +- 41 files changed, 461 insertions(+), 377 deletions(-) rename {resource => resources}/image.go (98%) rename {resource => resources}/image_cache.go (98%) rename {resource => resources}/image_test.go (99%) create mode 100644 resources/internal/glob.go rename {resource => resources}/resource.go (69%) create mode 100644 resources/resource/resources.go create mode 100644 resources/resource/resourcetypes.go rename {resource => resources}/resource_cache.go (90%) rename {resource => resources}/resource_factories/bundler/bundler.go (85%) rename {resource => resources}/resource_factories/create/create.go (76%) rename {resource => resources}/resource_metadata.go (93%) rename {resource => resources}/resource_metadata_test.go (95%) rename {resource => resources}/resource_test.go (94%) rename {resource => resources}/resource_transformers/integrity/integrity.go (86%) rename {resource => resources}/resource_transformers/minifier/minify.go (78%) rename {resource => resources}/resource_transformers/postcss/postcss.go (91%) rename {resource => resources}/resource_transformers/templates/execute_as_template.go (80%) rename {resource => resources}/resource_transformers/tocss/scss/client.go (89%) rename {resource => resources}/resource_transformers/tocss/scss/tocss.go (97%) rename {resource => resources}/resource_transformers/tocss/scss/tocss_notavailable.go (86%) rename {resource => resources}/smartcrop.go (96%) rename {resource => resources}/testdata/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph.jpg (100%) rename {resource => resources}/testdata/circle.svg (100%) rename {resource => resources}/testdata/gohugoio.png (100%) rename {resource => resources}/testdata/sub/gohugoio2.png (100%) rename {resource => resources}/testdata/sunset.jpg (100%) rename {resource => resources}/testhelpers_test.go (96%) rename {resource => resources}/transform.go (94%) rename {resource => resources}/transform_test.go (93%) diff --git a/deps/deps.go b/deps/deps.go index 7fba0e153..628019961 100644 --- a/deps/deps.go +++ b/deps/deps.go @@ -16,7 +16,7 @@ import ( "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/metrics" "github.com/gohugoio/hugo/output" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/source" "github.com/gohugoio/hugo/tpl" jww "github.com/spf13/jwalterweatherman" @@ -52,7 +52,7 @@ type Deps struct { SourceSpec *source.SourceSpec `json:"-"` // The Resource Spec to use - ResourceSpec *resource.Spec + ResourceSpec *resources.Spec // The configuration to use Cfg config.Provider `json:"-"` @@ -214,7 +214,7 @@ func New(cfg DepsCfg) (*Deps, error) { return nil, errors.WithMessage(err, "failed to create file caches from configuration") } - resourceSpec, err := resource.NewSpec(ps, fileCaches, logger, cfg.OutputFormats, cfg.MediaTypes) + resourceSpec, err := resources.NewSpec(ps, fileCaches, logger, cfg.OutputFormats, cfg.MediaTypes) if err != nil { return nil, err } @@ -281,7 +281,7 @@ func (d Deps) ForLanguage(cfg DepsCfg, onCreated func(d *Deps) error) (*Deps, er // The resource cache is global so reuse. // TODO(bep) clean up these inits. resourceCache := d.ResourceSpec.ResourceCache - d.ResourceSpec, err = resource.NewSpec(d.PathSpec, d.ResourceSpec.FileCaches, d.Log, cfg.OutputFormats, cfg.MediaTypes) + d.ResourceSpec, err = resources.NewSpec(d.PathSpec, d.ResourceSpec.FileCaches, d.Log, cfg.OutputFormats, cfg.MediaTypes) if err != nil { return nil, err } diff --git a/hugolib/collections.go b/hugolib/collections.go index b9992c425..cf75d3732 100644 --- a/hugolib/collections.go +++ b/hugolib/collections.go @@ -16,7 +16,7 @@ package hugolib import ( "fmt" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/common/collections" ) diff --git a/hugolib/page.go b/hugolib/page.go index 9f09dc9bd..71070d1e8 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -35,7 +35,7 @@ import ( "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/hugolib/pagemeta" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/output" "github.com/mitchellh/mapstructure" diff --git a/hugolib/page_output.go b/hugolib/page_output.go index 9a6531207..0a3eef9a6 100644 --- a/hugolib/page_output.go +++ b/hugolib/page_output.go @@ -24,7 +24,7 @@ import ( "github.com/gohugoio/hugo/tpl" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/media" diff --git a/hugolib/page_resource.go b/hugolib/page_resource.go index 808a692da..201076e8b 100644 --- a/hugolib/page_resource.go +++ b/hugolib/page_resource.go @@ -14,7 +14,7 @@ package hugolib import ( - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources/resource" ) var ( diff --git a/hugolib/pagebundler_handlers.go b/hugolib/pagebundler_handlers.go index fdff76e24..2df1f8765 100644 --- a/hugolib/pagebundler_handlers.go +++ b/hugolib/pagebundler_handlers.go @@ -22,7 +22,8 @@ import ( "strings" "github.com/gohugoio/hugo/helpers" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" ) var ( @@ -255,7 +256,7 @@ func (c *contentHandlers) parsePage(h contentHandler) contentHandler { // Assign metadata from front matter if set if len(p.resourcesMetadata) > 0 { - resource.AssignMetadata(p.resourcesMetadata, p.Resources...) + resources.AssignMetadata(p.resourcesMetadata, p.Resources...) } } @@ -309,7 +310,7 @@ func (c *contentHandlers) createResource() contentHandler { } resource, err := c.s.ResourceSpec.New( - resource.ResourceSourceDescriptor{ + resources.ResourceSourceDescriptor{ TargetPathBuilder: ctx.parentPage.subResourceTargetPathFactory, SourceFile: ctx.source, RelTargetFilename: ctx.target, diff --git a/hugolib/pages_language_merge_test.go b/hugolib/pages_language_merge_test.go index 6706af3bc..efcfbf04b 100644 --- a/hugolib/pages_language_merge_test.go +++ b/hugolib/pages_language_merge_test.go @@ -17,7 +17,7 @@ import ( "fmt" "testing" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources/resource" "github.com/stretchr/testify/require" ) diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go index 0fe9c70c1..f53ab4966 100644 --- a/hugolib/resource_chain_test.go +++ b/hugolib/resource_chain_test.go @@ -25,7 +25,7 @@ import ( "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/common/loggers" - "github.com/gohugoio/hugo/resource/resource_transformers/tocss/scss" + "github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss" ) func TestSCSSWithIncludePaths(t *testing.T) { diff --git a/hugolib/site.go b/hugolib/site.go index 9c225b332..43b398b70 100644 --- a/hugolib/site.go +++ b/hugolib/site.go @@ -39,7 +39,6 @@ import ( "github.com/gohugoio/hugo/common/hugo" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/publisher" - "github.com/gohugoio/hugo/resource" _errors "github.com/pkg/errors" "github.com/gohugoio/hugo/langs" @@ -62,6 +61,7 @@ import ( "github.com/gohugoio/hugo/hugolib/pagemeta" "github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/related" + "github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/source" "github.com/gohugoio/hugo/tpl" "github.com/spf13/afero" @@ -760,7 +760,7 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) { cachePartitions := make([]string, len(events)) for i, ev := range events { - cachePartitions[i] = resource.ResourceKeyPartition(ev.Name) + cachePartitions[i] = resources.ResourceKeyPartition(ev.Name) if s.isContentDirEvent(ev) { logger.Println("Source changed", ev) diff --git a/resource/image.go b/resources/image.go similarity index 98% rename from resource/image.go rename to resources/image.go index 6dea4cbb8..d46facac5 100644 --- a/resource/image.go +++ b/resources/image.go @@ -1,4 +1,4 @@ -// Copyright 2017-present The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "errors" @@ -27,6 +27,8 @@ import ( "strings" "sync" + "github.com/gohugoio/hugo/resources/resource" + _errors "github.com/pkg/errors" "github.com/disintegration/imaging" @@ -43,9 +45,9 @@ import ( ) var ( - _ Resource = (*Image)(nil) - _ Source = (*Image)(nil) - _ Cloner = (*Image)(nil) + _ resource.Resource = (*Image)(nil) + _ resource.Source = (*Image)(nil) + _ resource.Cloner = (*Image)(nil) ) // Imaging contains default image processing configuration. This will be fetched @@ -146,7 +148,7 @@ func (i *Image) Height() int { } // WithNewBase implements the Cloner interface. -func (i *Image) WithNewBase(base string) Resource { +func (i *Image) WithNewBase(base string) resource.Resource { return &Image{ imaging: i.imaging, format: i.format, diff --git a/resource/image_cache.go b/resources/image_cache.go similarity index 98% rename from resource/image_cache.go rename to resources/image_cache.go index 8cc626caf..58be839b3 100644 --- a/resource/image_cache.go +++ b/resources/image_cache.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "fmt" diff --git a/resource/image_test.go b/resources/image_test.go similarity index 99% rename from resource/image_test.go rename to resources/image_test.go index 07eab5d56..ffa482296 100644 --- a/resource/image_test.go +++ b/resources/image_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "fmt" diff --git a/resources/internal/glob.go b/resources/internal/glob.go new file mode 100644 index 000000000..a87a23f13 --- /dev/null +++ b/resources/internal/glob.go @@ -0,0 +1,48 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package internal + +import ( + "strings" + "sync" + + "github.com/gobwas/glob" +) + +var ( + globCache = make(map[string]glob.Glob) + globMu sync.RWMutex +) + +func GetGlob(pattern string) (glob.Glob, error) { + var g glob.Glob + + globMu.RLock() + g, found := globCache[pattern] + globMu.RUnlock() + if !found { + var err error + g, err = glob.Compile(strings.ToLower(pattern), '/') + if err != nil { + return nil, err + } + + globMu.Lock() + globCache[pattern] = g + globMu.Unlock() + } + + return g, nil + +} diff --git a/resource/resource.go b/resources/resource.go similarity index 69% rename from resource/resource.go rename to resources/resource.go index 0f5a43648..742903e80 100644 --- a/resource/resource.go +++ b/resources/resource.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "fmt" @@ -24,6 +24,8 @@ import ( "strings" "sync" + "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/output" "github.com/gohugoio/hugo/tpl" "github.com/pkg/errors" @@ -32,35 +34,28 @@ import ( "github.com/gohugoio/hugo/common/collections" "github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/common/loggers" + "github.com/gohugoio/hugo/resources/resource" "github.com/spf13/afero" - "github.com/gobwas/glob" "github.com/gohugoio/hugo/helpers" - "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/source" ) var ( - _ ContentResource = (*genericResource)(nil) - _ ReadSeekCloserResource = (*genericResource)(nil) - _ Resource = (*genericResource)(nil) - _ Source = (*genericResource)(nil) - _ Cloner = (*genericResource)(nil) - _ ResourcesLanguageMerger = (*Resources)(nil) - _ permalinker = (*genericResource)(nil) - _ collections.Slicer = (*genericResource)(nil) - _ Identifier = (*genericResource)(nil) + _ resource.ContentResource = (*genericResource)(nil) + _ resource.ReadSeekCloserResource = (*genericResource)(nil) + _ resource.Resource = (*genericResource)(nil) + _ resource.Source = (*genericResource)(nil) + _ resource.Cloner = (*genericResource)(nil) + _ resource.ResourcesLanguageMerger = (*resource.Resources)(nil) + _ permalinker = (*genericResource)(nil) + _ collections.Slicer = (*genericResource)(nil) + _ resource.Identifier = (*genericResource)(nil) ) var noData = make(map[string]interface{}) -// Source is an internal template and not meant for use in the templates. It -// may change without notice. -type Source interface { - Publish() error -} - type permalinker interface { relPermalinkFor(target string) string permalinkFor(target string) string @@ -69,215 +64,6 @@ type permalinker interface { targetPath() string } -// Cloner is an internal template and not meant for use in the templates. It -// may change without notice. -type Cloner interface { - WithNewBase(base string) Resource -} - -// Resource represents a linkable resource, i.e. a content page, image etc. -type Resource interface { - resourceBase - - // Permalink represents the absolute link to this resource. - Permalink() string - - // RelPermalink represents the host relative link to this resource. - RelPermalink() string - - // ResourceType is the resource type. For most file types, this is the main - // part of the MIME type, e.g. "image", "application", "text" etc. - // For content pages, this value is "page". - ResourceType() string - - // Name is the logical name of this resource. This can be set in the front matter - // metadata for this resource. If not set, Hugo will assign a value. - // This will in most cases be the base filename. - // So, for the image "/some/path/sunset.jpg" this will be "sunset.jpg". - // The value returned by this method will be used in the GetByPrefix and ByPrefix methods - // on Resources. - Name() string - - // Title returns the title if set in front matter. For content pages, this will be the expected value. - Title() string - - // Resource specific data set by Hugo. - // One example would be.Data.Digest for fingerprinted resources. - Data() interface{} - - // Params set in front matter for this resource. - Params() map[string]interface{} -} - -// resourceBase pulls out the minimal set of operations to define a Resource, -// to simplify testing etc. -type resourceBase interface { - // MediaType is this resource's MIME type. - MediaType() media.Type -} - -// ResourcesLanguageMerger describes an interface for merging resources from a -// different language. -type ResourcesLanguageMerger interface { - MergeByLanguage(other Resources) Resources - // Needed for integration with the tpl package. - MergeByLanguageInterface(other interface{}) (interface{}, error) -} - -type translatedResource interface { - TranslationKey() string -} - -// Identifier identifies a resource. -type Identifier interface { - Key() string -} - -// ContentResource represents a Resource that provides a way to get to its content. -// Most Resource types in Hugo implements this interface, including Page. -// This should be used with care, as it will read the file content into memory, but it -// should be cached as effectively as possible by the implementation. -type ContentResource interface { - resourceBase - - // Content returns this resource's content. It will be equivalent to reading the content - // that RelPermalink points to in the published folder. - // The return type will be contextual, and should be what you would expect: - // * Page: template.HTML - // * JSON: String - // * Etc. - Content() (interface{}, error) -} - -// OpenReadSeekCloser allows setting some other way (than reading from a filesystem) -// to open or create a ReadSeekCloser. -type OpenReadSeekCloser func() (hugio.ReadSeekCloser, error) - -// ReadSeekCloserResource is a Resource that supports loading its content. -type ReadSeekCloserResource interface { - resourceBase - ReadSeekCloser() (hugio.ReadSeekCloser, error) -} - -// Resources represents a slice of resources, which can be a mix of different types. -// I.e. both pages and images etc. -type Resources []Resource - -// ResourcesConverter converts a given slice of Resource objects to Resources. -type ResourcesConverter interface { - ToResources() Resources -} - -// ByType returns resources of a given resource type (ie. "image"). -func (r Resources) ByType(tp string) Resources { - var filtered Resources - - for _, resource := range r { - if resource.ResourceType() == tp { - filtered = append(filtered, resource) - } - } - return filtered -} - -// GetMatch finds the first Resource matching the given pattern, or nil if none found. -// See Match for a more complete explanation about the rules used. -func (r Resources) GetMatch(pattern string) Resource { - g, err := getGlob(pattern) - if err != nil { - return nil - } - - for _, resource := range r { - if g.Match(strings.ToLower(resource.Name())) { - return resource - } - } - - return nil -} - -// Match gets all resources matching the given base filename prefix, e.g -// "*.png" will match all png files. The "*" does not match path delimiters (/), -// so if you organize your resources in sub-folders, you need to be explicit about it, e.g.: -// "images/*.png". To match any PNG image anywhere in the bundle you can do "**.png", and -// to match all PNG images below the images folder, use "images/**.jpg". -// The matching is case insensitive. -// Match matches by using the value of Resource.Name, which, by default, is a filename with -// path relative to the bundle root with Unix style slashes (/) and no leading slash, e.g. "images/logo.png". -// See https://github.com/gobwas/glob for the full rules set. -func (r Resources) Match(pattern string) Resources { - g, err := getGlob(pattern) - if err != nil { - return nil - } - - var matches Resources - for _, resource := range r { - if g.Match(strings.ToLower(resource.Name())) { - matches = append(matches, resource) - } - } - return matches -} - -var ( - globCache = make(map[string]glob.Glob) - globMu sync.RWMutex -) - -func getGlob(pattern string) (glob.Glob, error) { - var g glob.Glob - - globMu.RLock() - g, found := globCache[pattern] - globMu.RUnlock() - if !found { - var err error - g, err = glob.Compile(strings.ToLower(pattern), '/') - if err != nil { - return nil, err - } - - globMu.Lock() - globCache[pattern] = g - globMu.Unlock() - } - - return g, nil - -} - -// MergeByLanguage adds missing translations in r1 from r2. -func (r Resources) MergeByLanguage(r2 Resources) Resources { - result := append(Resources(nil), r...) - m := make(map[string]bool) - for _, rr := range r { - if translated, ok := rr.(translatedResource); ok { - m[translated.TranslationKey()] = true - } - } - - for _, rr := range r2 { - if translated, ok := rr.(translatedResource); ok { - if _, found := m[translated.TranslationKey()]; !found { - result = append(result, rr) - } - } - } - return result -} - -// MergeByLanguageInterface is the generic version of MergeByLanguage. It -// is here just so it can be called from the tpl package. -func (r Resources) MergeByLanguageInterface(in interface{}) (interface{}, error) { - r2, ok := in.(Resources) - if !ok { - return nil, fmt.Errorf("%T cannot be merged by language", in) - } - return r.MergeByLanguage(r2), nil -} - type Spec struct { *helpers.PathSpec @@ -336,7 +122,7 @@ type ResourceSourceDescriptor struct { // Need one of these to load the resource content. SourceFile source.File - OpenReadSeekCloser OpenReadSeekCloser + OpenReadSeekCloser resource.OpenReadSeekCloser // If OpenReadSeekerCloser is not set, we use this to open the file. SourceFilename string @@ -370,15 +156,15 @@ func (r *Spec) sourceFs() afero.Fs { return r.PathSpec.BaseFs.Content.Fs } -func (r *Spec) New(fd ResourceSourceDescriptor) (Resource, error) { +func (r *Spec) New(fd ResourceSourceDescriptor) (resource.Resource, error) { return r.newResourceForFs(r.sourceFs(), fd) } -func (r *Spec) NewForFs(sourceFs afero.Fs, fd ResourceSourceDescriptor) (Resource, error) { +func (r *Spec) NewForFs(sourceFs afero.Fs, fd ResourceSourceDescriptor) (resource.Resource, error) { return r.newResourceForFs(sourceFs, fd) } -func (r *Spec) newResourceForFs(sourceFs afero.Fs, fd ResourceSourceDescriptor) (Resource, error) { +func (r *Spec) newResourceForFs(sourceFs afero.Fs, fd ResourceSourceDescriptor) (resource.Resource, error) { if fd.OpenReadSeekCloser == nil { if fd.SourceFile != nil && fd.SourceFilename != "" { return nil, errors.New("both SourceFile and AbsSourceFilename provided") @@ -399,7 +185,7 @@ func (r *Spec) newResourceForFs(sourceFs afero.Fs, fd ResourceSourceDescriptor) return r.newResource(sourceFs, fd) } -func (r *Spec) newResource(sourceFs afero.Fs, fd ResourceSourceDescriptor) (Resource, error) { +func (r *Spec) newResource(sourceFs afero.Fs, fd ResourceSourceDescriptor) (resource.Resource, error) { var fi os.FileInfo var sourceFilename string @@ -551,7 +337,7 @@ type publishOnce struct { logger *loggers.Logger } -func (l *publishOnce) publish(s Source) error { +func (l *publishOnce) publish(s resource.Source) error { l.publisherInit.Do(func() { l.publisherErr = s.Publish() if l.publisherErr != nil { @@ -577,7 +363,7 @@ type genericResource struct { sourceFilename string // Will be set if this resource is backed by something other than a file. - openReadSeekerCloser OpenReadSeekCloser + openReadSeekerCloser resource.OpenReadSeekCloser // A hash of the source content. Is only calculated in caching situations. *resourceHash @@ -632,7 +418,7 @@ func (l *genericResource) MediaType() media.Type { } // Implement the Cloner interface. -func (l genericResource) WithNewBase(base string) Resource { +func (l genericResource) WithNewBase(base string) resource.Resource { l.baseOffset = base l.resourceContent = &resourceContent{} return &l @@ -642,12 +428,12 @@ func (l genericResource) WithNewBase(base string) Resource { // for the template functions. See collections.Slice. func (commonResource) Slice(in interface{}) (interface{}, error) { switch items := in.(type) { - case Resources: + case resource.Resources: return items, nil case []interface{}: - groups := make(Resources, len(items)) + groups := make(resource.Resources, len(items)) for i, v := range items { - g, ok := v.(Resource) + g, ok := v.(resource.Resource) if !ok { return nil, fmt.Errorf("type %T is not a Resource", v) } @@ -903,7 +689,7 @@ func (r *Spec) newGenericResource(sourceFs afero.Fs, func (r *Spec) newGenericResourceWithBase( sourceFs afero.Fs, lazyPublish bool, - openReadSeekerCloser OpenReadSeekCloser, + openReadSeekerCloser resource.OpenReadSeekCloser, urlBaseDir string, targetPathBaseDirs []string, targetPathBuilder func(base string) string, diff --git a/resources/resource/resources.go b/resources/resource/resources.go new file mode 100644 index 000000000..5c661c24e --- /dev/null +++ b/resources/resource/resources.go @@ -0,0 +1,123 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package resource + +import ( + "fmt" + "strings" + + "github.com/gohugoio/hugo/resources/internal" +) + +// Resources represents a slice of resources, which can be a mix of different types. +// I.e. both pages and images etc. +type Resources []Resource + +// ResourcesConverter converts a given slice of Resource objects to Resources. +type ResourcesConverter interface { + ToResources() Resources +} + +// ByType returns resources of a given resource type (ie. "image"). +func (r Resources) ByType(tp string) Resources { + var filtered Resources + + for _, resource := range r { + if resource.ResourceType() == tp { + filtered = append(filtered, resource) + } + } + return filtered +} + +// GetMatch finds the first Resource matching the given pattern, or nil if none found. +// See Match for a more complete explanation about the rules used. +func (r Resources) GetMatch(pattern string) Resource { + g, err := internal.GetGlob(pattern) + if err != nil { + return nil + } + + for _, resource := range r { + if g.Match(strings.ToLower(resource.Name())) { + return resource + } + } + + return nil +} + +// Match gets all resources matching the given base filename prefix, e.g +// "*.png" will match all png files. The "*" does not match path delimiters (/), +// so if you organize your resources in sub-folders, you need to be explicit about it, e.g.: +// "images/*.png". To match any PNG image anywhere in the bundle you can do "**.png", and +// to match all PNG images below the images folder, use "images/**.jpg". +// The matching is case insensitive. +// Match matches by using the value of Resource.Name, which, by default, is a filename with +// path relative to the bundle root with Unix style slashes (/) and no leading slash, e.g. "images/logo.png". +// See https://github.com/gobwas/glob for the full rules set. +func (r Resources) Match(pattern string) Resources { + g, err := internal.GetGlob(pattern) + if err != nil { + return nil + } + + var matches Resources + for _, resource := range r { + if g.Match(strings.ToLower(resource.Name())) { + matches = append(matches, resource) + } + } + return matches +} + +type translatedResource interface { + TranslationKey() string +} + +// MergeByLanguage adds missing translations in r1 from r2. +func (r Resources) MergeByLanguage(r2 Resources) Resources { + result := append(Resources(nil), r...) + m := make(map[string]bool) + for _, rr := range r { + if translated, ok := rr.(translatedResource); ok { + m[translated.TranslationKey()] = true + } + } + + for _, rr := range r2 { + if translated, ok := rr.(translatedResource); ok { + if _, found := m[translated.TranslationKey()]; !found { + result = append(result, rr) + } + } + } + return result +} + +// MergeByLanguageInterface is the generic version of MergeByLanguage. It +// is here just so it can be called from the tpl package. +func (r Resources) MergeByLanguageInterface(in interface{}) (interface{}, error) { + r2, ok := in.(Resources) + if !ok { + return nil, fmt.Errorf("%T cannot be merged by language", in) + } + return r.MergeByLanguage(r2), nil +} + +// Source is an internal template and not meant for use in the templates. It +// may change without notice. +type Source interface { + Publish() error +} diff --git a/resources/resource/resourcetypes.go b/resources/resource/resourcetypes.go new file mode 100644 index 000000000..120d753e4 --- /dev/null +++ b/resources/resource/resourcetypes.go @@ -0,0 +1,106 @@ +// Copyright 2019 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package resource + +import ( + "github.com/gohugoio/hugo/media" + + "github.com/gohugoio/hugo/common/hugio" +) + +// Cloner is an internal template and not meant for use in the templates. It +// may change without notice. +type Cloner interface { + WithNewBase(base string) Resource +} + +// Resource represents a linkable resource, i.e. a content page, image etc. +type Resource interface { + resourceBase + + // Permalink represents the absolute link to this resource. + Permalink() string + + // RelPermalink represents the host relative link to this resource. + RelPermalink() string + + // ResourceType is the resource type. For most file types, this is the main + // part of the MIME type, e.g. "image", "application", "text" etc. + // For content pages, this value is "page". + ResourceType() string + + // Name is the logical name of this resource. This can be set in the front matter + // metadata for this resource. If not set, Hugo will assign a value. + // This will in most cases be the base filename. + // So, for the image "/some/path/sunset.jpg" this will be "sunset.jpg". + // The value returned by this method will be used in the GetByPrefix and ByPrefix methods + // on Resources. + Name() string + + // Title returns the title if set in front matter. For content pages, this will be the expected value. + Title() string + + // Resource specific data set by Hugo. + // One example would be.Data.Digest for fingerprinted resources. + Data() interface{} + + // Params set in front matter for this resource. + Params() map[string]interface{} +} + +// resourceBase pulls out the minimal set of operations to define a Resource, +// to simplify testing etc. +type resourceBase interface { + // MediaType is this resource's MIME type. + MediaType() media.Type +} + +// ResourcesLanguageMerger describes an interface for merging resources from a +// different language. +type ResourcesLanguageMerger interface { + MergeByLanguage(other Resources) Resources + // Needed for integration with the tpl package. + MergeByLanguageInterface(other interface{}) (interface{}, error) +} + +// Identifier identifies a resource. +type Identifier interface { + Key() string +} + +// ContentResource represents a Resource that provides a way to get to its content. +// Most Resource types in Hugo implements this interface, including Page. +// This should be used with care, as it will read the file content into memory, but it +// should be cached as effectively as possible by the implementation. +type ContentResource interface { + resourceBase + + // Content returns this resource's content. It will be equivalent to reading the content + // that RelPermalink points to in the published folder. + // The return type will be contextual, and should be what you would expect: + // * Page: template.HTML + // * JSON: String + // * Etc. + Content() (interface{}, error) +} + +// OpenReadSeekCloser allows setting some other way (than reading from a filesystem) +// to open or create a ReadSeekCloser. +type OpenReadSeekCloser func() (hugio.ReadSeekCloser, error) + +// ReadSeekCloserResource is a Resource that supports loading its content. +type ReadSeekCloserResource interface { + resourceBase + ReadSeekCloser() (hugio.ReadSeekCloser, error) +} diff --git a/resource/resource_cache.go b/resources/resource_cache.go similarity index 90% rename from resource/resource_cache.go rename to resources/resource_cache.go index e7c6ab6d0..8ff63beb0 100644 --- a/resource/resource_cache.go +++ b/resources/resource_cache.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "encoding/json" @@ -21,6 +21,8 @@ import ( "strings" "sync" + "github.com/gohugoio/hugo/resources/resource" + "github.com/gohugoio/hugo/cache/filecache" "github.com/BurntSushi/locker" @@ -35,7 +37,7 @@ type ResourceCache struct { rs *Spec sync.RWMutex - cache map[string]Resource + cache map[string]resource.Resource fileCache *filecache.Cache @@ -59,7 +61,7 @@ func newResourceCache(rs *Spec) *ResourceCache { return &ResourceCache{ rs: rs, fileCache: rs.FileCaches.AssetsCache(), - cache: make(map[string]Resource), + cache: make(map[string]resource.Resource), nlocker: locker.NewLocker(), } } @@ -68,7 +70,7 @@ func (c *ResourceCache) clear() { c.Lock() defer c.Unlock() - c.cache = make(map[string]Resource) + c.cache = make(map[string]resource.Resource) c.nlocker = locker.NewLocker() } @@ -82,14 +84,14 @@ func (c *ResourceCache) cleanKey(key string) string { return strings.TrimPrefix(path.Clean(key), "/") } -func (c *ResourceCache) get(key string) (Resource, bool) { +func (c *ResourceCache) get(key string) (resource.Resource, bool) { c.RLock() defer c.RUnlock() r, found := c.cache[key] return r, found } -func (c *ResourceCache) GetOrCreate(partition, key string, f func() (Resource, error)) (Resource, error) { +func (c *ResourceCache) GetOrCreate(partition, key string, f func() (resource.Resource, error)) (resource.Resource, error) { key = c.cleanKey(path.Join(partition, key)) // First check in-memory cache. r, found := c.get(key) @@ -172,7 +174,7 @@ func (c *ResourceCache) writeMeta(key string, meta transformedResourceMetadata) } -func (c *ResourceCache) set(key string, r Resource) { +func (c *ResourceCache) set(key string, r resource.Resource) { c.Lock() defer c.Unlock() c.cache[key] = r diff --git a/resource/resource_factories/bundler/bundler.go b/resources/resource_factories/bundler/bundler.go similarity index 85% rename from resource/resource_factories/bundler/bundler.go rename to resources/resource_factories/bundler/bundler.go index 70b8ee536..ca0ccf86e 100644 --- a/resource/resource_factories/bundler/bundler.go +++ b/resources/resource_factories/bundler/bundler.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,17 +21,18 @@ import ( "github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/media" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" ) // Client contains methods perform concatenation and other bundling related // tasks to Resource objects. type Client struct { - rs *resource.Spec + rs *resources.Spec } // New creates a new Client with the given specification. -func New(rs *resource.Spec) *Client { +func New(rs *resources.Spec) *Client { return &Client{rs: rs} } @@ -62,14 +63,14 @@ func (r *multiReadSeekCloser) Close() error { } // Concat concatenates the list of Resource objects. -func (c *Client) Concat(targetPath string, resources resource.Resources) (resource.Resource, error) { +func (c *Client) Concat(targetPath string, r resource.Resources) (resource.Resource, error) { // The CACHE_OTHER will make sure this will be re-created and published on rebuilds. - return c.rs.ResourceCache.GetOrCreate(resource.CACHE_OTHER, targetPath, func() (resource.Resource, error) { + return c.rs.ResourceCache.GetOrCreate(resources.CACHE_OTHER, targetPath, func() (resource.Resource, error) { var resolvedm 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. - for i, r := range resources { + for i, r := range r { if i > 0 && r.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()) } @@ -78,7 +79,7 @@ func (c *Client) Concat(targetPath string, resources resource.Resources) (resour concatr := func() (hugio.ReadSeekCloser, error) { var rcsources []hugio.ReadSeekCloser - for _, s := range resources { + for _, s := range r { rcr, ok := s.(resource.ReadSeekCloserResource) if !ok { return nil, fmt.Errorf("resource %T does not implement resource.ReadSeekerCloserResource", s) @@ -106,7 +107,7 @@ func (c *Client) Concat(targetPath string, resources resource.Resources) (resour composite, err := c.rs.NewForFs( c.rs.FileCaches.AssetsCache().Fs, - resource.ResourceSourceDescriptor{ + resources.ResourceSourceDescriptor{ LazyPublish: true, OpenReadSeekCloser: concatr, RelTargetFilename: filepath.Clean(targetPath)}) diff --git a/resource/resource_factories/create/create.go b/resources/resource_factories/create/create.go similarity index 76% rename from resource/resource_factories/create/create.go rename to resources/resource_factories/create/create.go index db23930e4..dc565056d 100644 --- a/resource/resource_factories/create/create.go +++ b/resources/resource_factories/create/create.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,26 +21,27 @@ import ( "github.com/spf13/afero" "github.com/gohugoio/hugo/common/hugio" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" ) // Client contains methods to create Resource objects. // tasks to Resource objects. type Client struct { - rs *resource.Spec + rs *resources.Spec } // New creates a new Client with the given specification. -func New(rs *resource.Spec) *Client { +func New(rs *resources.Spec) *Client { return &Client{rs: rs} } // Get creates a new Resource by opening the given filename in the given filesystem. func (c *Client) Get(fs afero.Fs, filename string) (resource.Resource, error) { filename = filepath.Clean(filename) - return c.rs.ResourceCache.GetOrCreate(resource.ResourceKeyPartition(filename), filename, func() (resource.Resource, error) { + return c.rs.ResourceCache.GetOrCreate(resources.ResourceKeyPartition(filename), filename, func() (resource.Resource, error) { return c.rs.NewForFs(fs, - resource.ResourceSourceDescriptor{ + resources.ResourceSourceDescriptor{ LazyPublish: true, SourceFilename: filename}) }) @@ -49,10 +50,10 @@ func (c *Client) Get(fs afero.Fs, filename string) (resource.Resource, error) { // FromString creates a new Resource from a string with the given relative target path. func (c *Client) FromString(targetPath, content string) (resource.Resource, error) { - return c.rs.ResourceCache.GetOrCreate(resource.CACHE_OTHER, targetPath, func() (resource.Resource, error) { + return c.rs.ResourceCache.GetOrCreate(resources.CACHE_OTHER, targetPath, func() (resource.Resource, error) { return c.rs.NewForFs( c.rs.FileCaches.AssetsCache().Fs, - resource.ResourceSourceDescriptor{ + resources.ResourceSourceDescriptor{ LazyPublish: true, OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { return hugio.NewReadSeekerNoOpCloserFromString(content), nil diff --git a/resource/resource_metadata.go b/resources/resource_metadata.go similarity index 93% rename from resource/resource_metadata.go rename to resources/resource_metadata.go index 20c4f130b..0830dfc59 100644 --- a/resource/resource_metadata.go +++ b/resources/resource_metadata.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,12 +11,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "fmt" "strconv" + "github.com/gohugoio/hugo/resources/internal" + "github.com/gohugoio/hugo/resources/resource" + "github.com/pkg/errors" "github.com/spf13/cast" @@ -43,7 +46,7 @@ const counterPlaceHolder = ":counter" // This assignment is additive, but the most specific match needs to be first. // The `name` and `title` metadata field support shell-matched collection it got a match in. // See https://golang.org/pkg/path/#Match -func AssignMetadata(metadata []map[string]interface{}, resources ...Resource) error { +func AssignMetadata(metadata []map[string]interface{}, resources ...resource.Resource) error { counters := make(map[string]int) @@ -68,7 +71,7 @@ func AssignMetadata(metadata []map[string]interface{}, resources ...Resource) er srcKey := strings.ToLower(cast.ToString(src)) - glob, err := getGlob(srcKey) + glob, err := internal.GetGlob(srcKey) if err != nil { return errors.Wrap(err, "failed to match resource with metadata") } diff --git a/resource/resource_metadata_test.go b/resources/resource_metadata_test.go similarity index 95% rename from resource/resource_metadata_test.go rename to resources/resource_metadata_test.go index 85fb25b57..a1a2a738c 100644 --- a/resource/resource_metadata_test.go +++ b/resources/resource_metadata_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,12 +11,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "testing" "github.com/gohugoio/hugo/media" + "github.com/gohugoio/hugo/resources/resource" "github.com/stretchr/testify/require" ) @@ -25,8 +26,8 @@ func TestAssignMetadata(t *testing.T) { assert := require.New(t) spec := newTestResourceSpec(assert) - var foo1, foo2, foo3, logo1, logo2, logo3 Resource - var resources Resources + var foo1, foo2, foo3, logo1, logo2, logo3 resource.Resource + var resources resource.Resources for _, this := range []struct { metaData []map[string]interface{} @@ -215,7 +216,7 @@ func TestAssignMetadata(t *testing.T) { foo3 = spec.newGenericResource(nil, nil, nil, "/b/foo3.css", "foo3.css", media.CSSType) logo3 = spec.newGenericResource(nil, nil, nil, "/b/logo3.png", "logo3.png", pngType) - resources = Resources{ + resources = resource.Resources{ foo2, logo2, foo1, diff --git a/resource/resource_test.go b/resources/resource_test.go similarity index 94% rename from resource/resource_test.go rename to resources/resource_test.go index b3f6035b6..be2706e45 100644 --- a/resource/resource_test.go +++ b/resources/resource_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "fmt" @@ -22,6 +22,8 @@ import ( "testing" "time" + "github.com/gohugoio/hugo/resources/resource" + "github.com/gohugoio/hugo/media" "github.com/stretchr/testify/require" @@ -75,7 +77,7 @@ func TestNewResourceFromFilename(t *testing.T) { assert.NotNil(r) assert.Equal("json", r.ResourceType()) - cloned := r.(Cloner).WithNewBase("aceof") + cloned := r.(resource.Cloner).WithNewBase("aceof") assert.Equal(r.ResourceType(), cloned.ResourceType()) assert.Equal("/aceof/a/b/data.json", cloned.RelPermalink()) } @@ -103,7 +105,7 @@ var pngType, _ = media.FromStringAndExt("image/png", "png") func TestResourcesByType(t *testing.T) { assert := require.New(t) spec := newTestResourceSpec(assert) - resources := Resources{ + resources := resource.Resources{ spec.newGenericResource(nil, nil, nil, "/a/foo1.css", "foo1.css", media.CSSType), spec.newGenericResource(nil, nil, nil, "/a/logo.png", "logo.css", pngType), spec.newGenericResource(nil, nil, nil, "/a/foo2.css", "foo2.css", media.CSSType), @@ -117,7 +119,7 @@ func TestResourcesByType(t *testing.T) { func TestResourcesGetByPrefix(t *testing.T) { assert := require.New(t) spec := newTestResourceSpec(assert) - resources := Resources{ + resources := resource.Resources{ spec.newGenericResource(nil, nil, nil, "/a/foo1.css", "foo1.css", media.CSSType), spec.newGenericResource(nil, nil, nil, "/a/logo1.png", "logo1.png", pngType), spec.newGenericResource(nil, nil, nil, "/b/Logo2.png", "Logo2.png", pngType), @@ -146,7 +148,7 @@ func TestResourcesGetByPrefix(t *testing.T) { func TestResourcesGetMatch(t *testing.T) { assert := require.New(t) spec := newTestResourceSpec(assert) - resources := Resources{ + resources := resource.Resources{ spec.newGenericResource(nil, nil, nil, "/a/foo1.css", "foo1.css", media.CSSType), spec.newGenericResource(nil, nil, nil, "/a/logo1.png", "logo1.png", pngType), spec.newGenericResource(nil, nil, nil, "/b/Logo2.png", "Logo2.png", pngType), @@ -211,7 +213,7 @@ func BenchmarkResourcesMatchA100(b *testing.B) { a100 := strings.Repeat("a", 100) pattern := "a*a*a*a*a*a*a*a*b" - resources := Resources{spec.newGenericResource(nil, nil, nil, "/a/"+a100, a100, media.CSSType)} + resources := resource.Resources{spec.newGenericResource(nil, nil, nil, "/a/"+a100, a100, media.CSSType)} b.ResetTimer() for i := 0; i < b.N; i++ { @@ -220,10 +222,10 @@ func BenchmarkResourcesMatchA100(b *testing.B) { } -func benchResources(b *testing.B) Resources { +func benchResources(b *testing.B) resource.Resources { assert := require.New(b) spec := newTestResourceSpec(assert) - var resources Resources + var resources resource.Resources for i := 0; i < 30; i++ { name := fmt.Sprintf("abcde%d_%d.css", i%5, i) @@ -250,7 +252,7 @@ func BenchmarkAssignMetadata(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() - var resources Resources + var resources resource.Resources var meta = []map[string]interface{}{ { "title": "Foo #:counter", diff --git a/resource/resource_transformers/integrity/integrity.go b/resources/resource_transformers/integrity/integrity.go similarity index 86% rename from resource/resource_transformers/integrity/integrity.go rename to resources/resource_transformers/integrity/integrity.go index 535c06a32..90afafb88 100644 --- a/resource/resource_transformers/integrity/integrity.go +++ b/resources/resource_transformers/integrity/integrity.go @@ -24,7 +24,8 @@ import ( "html/template" "io" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" ) const defaultHashAlgo = "sha256" @@ -32,11 +33,11 @@ const defaultHashAlgo = "sha256" // Client contains methods to fingerprint (cachebusting) and other integrity-related // methods. type Client struct { - rs *resource.Spec + rs *resources.Spec } // New creates a new Client with the given specification. -func New(rs *resource.Spec) *Client { +func New(rs *resources.Spec) *Client { return &Client{rs: rs} } @@ -44,13 +45,13 @@ type fingerprintTransformation struct { algo string } -func (t *fingerprintTransformation) Key() resource.ResourceTransformationKey { - return resource.NewResourceTransformationKey("fingerprint", t.algo) +func (t *fingerprintTransformation) Key() resources.ResourceTransformationKey { + return resources.NewResourceTransformationKey("fingerprint", t.algo) } // Transform creates a MD5 hash of the Resource content and inserts that hash before // the extension in the filename. -func (t *fingerprintTransformation) Transform(ctx *resource.ResourceTransformationCtx) error { +func (t *fingerprintTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { algo := t.algo var h hash.Hash diff --git a/resource/resource_transformers/minifier/minify.go b/resources/resource_transformers/minifier/minify.go similarity index 78% rename from resource/resource_transformers/minifier/minify.go rename to resources/resource_transformers/minifier/minify.go index cef22efc0..952c6a99c 100644 --- a/resource/resource_transformers/minifier/minify.go +++ b/resources/resource_transformers/minifier/minify.go @@ -15,32 +15,33 @@ package minifier import ( "github.com/gohugoio/hugo/minifiers" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" ) // Client for minification of Resource objects. Supported minfiers are: // css, html, js, json, svg and xml. type Client struct { - rs *resource.Spec + rs *resources.Spec m minifiers.Client } // New creates a new Client given a specification. Note that it is the media types // configured for the site that is used to match files to the correct minifier. -func New(rs *resource.Spec) *Client { +func New(rs *resources.Spec) *Client { return &Client{rs: rs, m: minifiers.New(rs.MediaTypes, rs.OutputFormats)} } type minifyTransformation struct { - rs *resource.Spec + rs *resources.Spec m minifiers.Client } -func (t *minifyTransformation) Key() resource.ResourceTransformationKey { - return resource.NewResourceTransformationKey("minify") +func (t *minifyTransformation) Key() resources.ResourceTransformationKey { + return resources.NewResourceTransformationKey("minify") } -func (t *minifyTransformation) Transform(ctx *resource.ResourceTransformationCtx) error { +func (t *minifyTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { if err := t.m.Minify(ctx.InMediaType, ctx.To, ctx.From); err != nil { return err } diff --git a/resource/resource_transformers/postcss/postcss.go b/resources/resource_transformers/postcss/postcss.go similarity index 91% rename from resource/resource_transformers/postcss/postcss.go rename to resources/resource_transformers/postcss/postcss.go index ec73543dd..5350eebc5 100644 --- a/resource/resource_transformers/postcss/postcss.go +++ b/resources/resource_transformers/postcss/postcss.go @@ -26,7 +26,8 @@ import ( "github.com/mitchellh/mapstructure" "github.com/gohugoio/hugo/common/herrors" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" ) // Some of the options from https://github.com/postcss/postcss-cli @@ -74,28 +75,28 @@ func (opts Options) toArgs() []string { // Client is the client used to do PostCSS transformations. type Client struct { - rs *resource.Spec + rs *resources.Spec } // New creates a new Client with the given specification. -func New(rs *resource.Spec) *Client { +func New(rs *resources.Spec) *Client { return &Client{rs: rs} } type postcssTransformation struct { options Options - rs *resource.Spec + rs *resources.Spec } -func (t *postcssTransformation) Key() resource.ResourceTransformationKey { - return resource.NewResourceTransformationKey("postcss", t.options) +func (t *postcssTransformation) Key() resources.ResourceTransformationKey { + return resources.NewResourceTransformationKey("postcss", t.options) } // Transform shells out to postcss-cli to do the heavy lifting. // For this to work, you need some additional tools. To install them globally: // npm install -g postcss-cli // npm install -g autoprefixer -func (t *postcssTransformation) Transform(ctx *resource.ResourceTransformationCtx) error { +func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { const localPostCSSPath = "node_modules/postcss-cli/bin/" const binaryName = "postcss" diff --git a/resource/resource_transformers/templates/execute_as_template.go b/resources/resource_transformers/templates/execute_as_template.go similarity index 80% rename from resource/resource_transformers/templates/execute_as_template.go rename to resources/resource_transformers/templates/execute_as_template.go index a126b26c9..b3ec3cf43 100644 --- a/resource/resource_transformers/templates/execute_as_template.go +++ b/resources/resource_transformers/templates/execute_as_template.go @@ -16,20 +16,21 @@ package templates import ( "github.com/gohugoio/hugo/helpers" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" "github.com/gohugoio/hugo/tpl" "github.com/pkg/errors" ) // Client contains methods to perform template processing of Resource objects. type Client struct { - rs *resource.Spec + rs *resources.Spec textTemplate tpl.TemplateParseFinder } // New creates a new Client with the given specification. -func New(rs *resource.Spec, textTemplate tpl.TemplateParseFinder) *Client { +func New(rs *resources.Spec, textTemplate tpl.TemplateParseFinder) *Client { if rs == nil { panic("must provice a resource Spec") } @@ -40,17 +41,17 @@ func New(rs *resource.Spec, textTemplate tpl.TemplateParseFinder) *Client { } type executeAsTemplateTransform struct { - rs *resource.Spec + rs *resources.Spec textTemplate tpl.TemplateParseFinder targetPath string data interface{} } -func (t *executeAsTemplateTransform) Key() resource.ResourceTransformationKey { - return resource.NewResourceTransformationKey("execute-as-template", t.targetPath) +func (t *executeAsTemplateTransform) Key() resources.ResourceTransformationKey { + return resources.NewResourceTransformationKey("execute-as-template", t.targetPath) } -func (t *executeAsTemplateTransform) Transform(ctx *resource.ResourceTransformationCtx) error { +func (t *executeAsTemplateTransform) Transform(ctx *resources.ResourceTransformationCtx) error { tplStr := helpers.ReaderToString(ctx.From) templ, err := t.textTemplate.Parse(ctx.InPath, tplStr) if err != nil { diff --git a/resource/resource_transformers/tocss/scss/client.go b/resources/resource_transformers/tocss/scss/client.go similarity index 89% rename from resource/resource_transformers/tocss/scss/client.go rename to resources/resource_transformers/tocss/scss/client.go index 126727e06..41ff67433 100644 --- a/resource/resource_transformers/tocss/scss/client.go +++ b/resources/resource_transformers/tocss/scss/client.go @@ -17,17 +17,19 @@ import ( "github.com/bep/go-tocss/scss" "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/hugolib/filesystems" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" + "github.com/gohugoio/hugo/resources/resource" + "github.com/mitchellh/mapstructure" ) type Client struct { - rs *resource.Spec + rs *resources.Spec sfs *filesystems.SourceFilesystem workFs *filesystems.SourceFilesystem } -func New(fs *filesystems.SourceFilesystem, rs *resource.Spec) (*Client, error) { +func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error) { return &Client{sfs: fs, workFs: rs.BaseFs.Work, rs: rs}, nil } @@ -91,8 +93,8 @@ type toCSSTransformation struct { options options } -func (t *toCSSTransformation) Key() resource.ResourceTransformationKey { - return resource.NewResourceTransformationKey("tocss", t.options.from) +func (t *toCSSTransformation) Key() resources.ResourceTransformationKey { + return resources.NewResourceTransformationKey("tocss", t.options.from) } func DecodeOptions(m map[string]interface{}) (opts Options, err error) { diff --git a/resource/resource_transformers/tocss/scss/tocss.go b/resources/resource_transformers/tocss/scss/tocss.go similarity index 97% rename from resource/resource_transformers/tocss/scss/tocss.go rename to resources/resource_transformers/tocss/scss/tocss.go index 984e14fc2..17c32ea8e 100644 --- a/resource/resource_transformers/tocss/scss/tocss.go +++ b/resources/resource_transformers/tocss/scss/tocss.go @@ -28,7 +28,7 @@ import ( "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/media" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" "github.com/pkg/errors" ) @@ -37,7 +37,7 @@ func Supports() bool { return true } -func (t *toCSSTransformation) Transform(ctx *resource.ResourceTransformationCtx) error { +func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { ctx.OutMediaType = media.CSSType var outName string diff --git a/resource/resource_transformers/tocss/scss/tocss_notavailable.go b/resources/resource_transformers/tocss/scss/tocss_notavailable.go similarity index 86% rename from resource/resource_transformers/tocss/scss/tocss_notavailable.go rename to resources/resource_transformers/tocss/scss/tocss_notavailable.go index df918b368..ad6b42b98 100644 --- a/resource/resource_transformers/tocss/scss/tocss_notavailable.go +++ b/resources/resource_transformers/tocss/scss/tocss_notavailable.go @@ -17,7 +17,7 @@ package scss import ( "github.com/gohugoio/hugo/common/herrors" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources" ) // Used in tests. @@ -25,6 +25,6 @@ func Supports() bool { return false } -func (t *toCSSTransformation) Transform(ctx *resource.ResourceTransformationCtx) error { +func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { return herrors.ErrFeatureNotAvailable } diff --git a/resource/smartcrop.go b/resources/smartcrop.go similarity index 96% rename from resource/smartcrop.go rename to resources/smartcrop.go index 201262436..05bc55cd7 100644 --- a/resource/smartcrop.go +++ b/resources/smartcrop.go @@ -1,4 +1,4 @@ -// Copyright 2017-present The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "image" diff --git a/resource/testdata/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph.jpg b/resources/testdata/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph.jpg similarity index 100% rename from resource/testdata/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph.jpg rename to resources/testdata/1234567890qwertyuiopasdfghjklzxcvbnm5to6eeeeee7via8eleph.jpg diff --git a/resource/testdata/circle.svg b/resources/testdata/circle.svg similarity index 100% rename from resource/testdata/circle.svg rename to resources/testdata/circle.svg diff --git a/resource/testdata/gohugoio.png b/resources/testdata/gohugoio.png similarity index 100% rename from resource/testdata/gohugoio.png rename to resources/testdata/gohugoio.png diff --git a/resource/testdata/sub/gohugoio2.png b/resources/testdata/sub/gohugoio2.png similarity index 100% rename from resource/testdata/sub/gohugoio2.png rename to resources/testdata/sub/gohugoio2.png diff --git a/resource/testdata/sunset.jpg b/resources/testdata/sunset.jpg similarity index 100% rename from resource/testdata/sunset.jpg rename to resources/testdata/sunset.jpg diff --git a/resource/testhelpers_test.go b/resources/testhelpers_test.go similarity index 96% rename from resource/testhelpers_test.go rename to resources/testhelpers_test.go index 55833bafa..d0fcb59e7 100644 --- a/resource/testhelpers_test.go +++ b/resources/testhelpers_test.go @@ -1,4 +1,4 @@ -package resource +package resources import ( "path/filepath" @@ -18,6 +18,7 @@ import ( "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/output" + "github.com/gohugoio/hugo/resources/resource" "github.com/spf13/afero" "github.com/spf13/viper" "github.com/stretchr/testify/require" @@ -112,7 +113,7 @@ func fetchImageForSpec(spec *Spec, assert *require.Assertions, name string) *Ima return r.(*Image) } -func fetchResourceForSpec(spec *Spec, assert *require.Assertions, name string) ContentResource { +func fetchResourceForSpec(spec *Spec, assert *require.Assertions, name string) resource.ContentResource { src, err := os.Open(filepath.FromSlash("testdata/" + name)) assert.NoError(err) @@ -130,7 +131,7 @@ func fetchResourceForSpec(spec *Spec, assert *require.Assertions, name string) C r, err := spec.New(ResourceSourceDescriptor{TargetPathBuilder: factory, SourceFilename: name}) assert.NoError(err) - return r.(ContentResource) + return r.(resource.ContentResource) } func assertImageFile(assert *require.Assertions, fs afero.Fs, filename string, width, height int) { diff --git a/resource/transform.go b/resources/transform.go similarity index 94% rename from resource/transform.go rename to resources/transform.go index bd59d0658..fd3ae1ae6 100644 --- a/resource/transform.go +++ b/resources/transform.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "bytes" @@ -23,6 +23,7 @@ import ( "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/hugio" "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/resources/resource" "github.com/mitchellh/hashstructure" "fmt" @@ -35,13 +36,13 @@ import ( ) var ( - _ ContentResource = (*transformedResource)(nil) - _ ReadSeekCloserResource = (*transformedResource)(nil) - _ collections.Slicer = (*transformedResource)(nil) - _ Identifier = (*transformedResource)(nil) + _ resource.ContentResource = (*transformedResource)(nil) + _ resource.ReadSeekCloserResource = (*transformedResource)(nil) + _ collections.Slicer = (*transformedResource)(nil) + _ resource.Identifier = (*transformedResource)(nil) ) -func (s *Spec) Transform(r Resource, t ResourceTransformation) (Resource, error) { +func (s *Spec) Transform(r resource.Resource, t ResourceTransformation) (resource.Resource, error) { return &transformedResource{ Resource: r, transformation: t, @@ -195,7 +196,7 @@ type transformedResource struct { transformedResourceMetadata // The source - Resource + resource.Resource } func (r *transformedResource) ReadSeekCloser() (hugio.ReadSeekCloser, error) { @@ -292,11 +293,11 @@ func (r *transformedResource) transform(setContent, publish bool) (err error) { // This can be the last resource in a chain. // Rewind and create a processing chain. - var chain []Resource + var chain []resource.Resource current := r for { rr := current.Resource - chain = append(chain[:0], append([]Resource{rr}, chain[0:]...)...) + chain = append(chain[:0], append([]resource.Resource{rr}, chain[0:]...)...) if tr, ok := rr.(*transformedResource); ok { current = tr } else { @@ -538,9 +539,9 @@ func (r *transformedResource) initTransform(setContent, publish bool) error { } // contentReadSeekerCloser returns a ReadSeekerCloser if possible for a given Resource. -func contentReadSeekerCloser(r Resource) (hugio.ReadSeekCloser, error) { +func contentReadSeekerCloser(r resource.Resource) (hugio.ReadSeekCloser, error) { switch rr := r.(type) { - case ReadSeekCloserResource: + case resource.ReadSeekCloserResource: rc, err := rr.ReadSeekCloser() if err != nil { return nil, err diff --git a/resource/transform_test.go b/resources/transform_test.go similarity index 93% rename from resource/transform_test.go rename to resources/transform_test.go index df68e780d..ed462cd2a 100644 --- a/resource/transform_test.go +++ b/resources/transform_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package resource +package resources import ( "testing" diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go index 497611b75..d32e12a05 100644 --- a/tpl/resources/resources.go +++ b/tpl/resources/resources.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -22,14 +22,14 @@ import ( _errors "github.com/pkg/errors" "github.com/gohugoio/hugo/deps" - "github.com/gohugoio/hugo/resource" - "github.com/gohugoio/hugo/resource/resource_factories/bundler" - "github.com/gohugoio/hugo/resource/resource_factories/create" - "github.com/gohugoio/hugo/resource/resource_transformers/integrity" - "github.com/gohugoio/hugo/resource/resource_transformers/minifier" - "github.com/gohugoio/hugo/resource/resource_transformers/postcss" - "github.com/gohugoio/hugo/resource/resource_transformers/templates" - "github.com/gohugoio/hugo/resource/resource_transformers/tocss/scss" + "github.com/gohugoio/hugo/resources/resource" + "github.com/gohugoio/hugo/resources/resource_factories/bundler" + "github.com/gohugoio/hugo/resources/resource_factories/create" + "github.com/gohugoio/hugo/resources/resource_transformers/integrity" + "github.com/gohugoio/hugo/resources/resource_transformers/minifier" + "github.com/gohugoio/hugo/resources/resource_transformers/postcss" + "github.com/gohugoio/hugo/resources/resource_transformers/templates" + "github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss" "github.com/spf13/cast" ) diff --git a/tpl/transform/unmarshal.go b/tpl/transform/unmarshal.go index c27a21a9e..da06b6aa1 100644 --- a/tpl/transform/unmarshal.go +++ b/tpl/transform/unmarshal.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -21,7 +21,7 @@ import ( "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/parser/metadecoders" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources/resource" "github.com/pkg/errors" "github.com/spf13/cast" diff --git a/tpl/transform/unmarshal_test.go b/tpl/transform/unmarshal_test.go index d9ebd1f89..1defe8c9c 100644 --- a/tpl/transform/unmarshal_test.go +++ b/tpl/transform/unmarshal_test.go @@ -1,4 +1,4 @@ -// Copyright 2018 The Hugo Authors. All rights reserved. +// Copyright 2019 The Hugo Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,7 +23,7 @@ import ( "github.com/gohugoio/hugo/media" - "github.com/gohugoio/hugo/resource" + "github.com/gohugoio/hugo/resources/resource" "github.com/spf13/viper" "github.com/stretchr/testify/require" )