resources: Move resource interfaces into its own package
							
								
								
									
										8
									
								
								deps/deps.go
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -16,7 +16,7 @@ import ( | |||||||
| 	"github.com/gohugoio/hugo/media" | 	"github.com/gohugoio/hugo/media" | ||||||
| 	"github.com/gohugoio/hugo/metrics" | 	"github.com/gohugoio/hugo/metrics" | ||||||
| 	"github.com/gohugoio/hugo/output" | 	"github.com/gohugoio/hugo/output" | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources" | ||||||
| 	"github.com/gohugoio/hugo/source" | 	"github.com/gohugoio/hugo/source" | ||||||
| 	"github.com/gohugoio/hugo/tpl" | 	"github.com/gohugoio/hugo/tpl" | ||||||
| 	jww "github.com/spf13/jwalterweatherman" | 	jww "github.com/spf13/jwalterweatherman" | ||||||
| @@ -52,7 +52,7 @@ type Deps struct { | |||||||
| 	SourceSpec *source.SourceSpec `json:"-"` | 	SourceSpec *source.SourceSpec `json:"-"` | ||||||
|  |  | ||||||
| 	// The Resource Spec to use | 	// The Resource Spec to use | ||||||
| 	ResourceSpec *resource.Spec | 	ResourceSpec *resources.Spec | ||||||
|  |  | ||||||
| 	// The configuration to use | 	// The configuration to use | ||||||
| 	Cfg config.Provider `json:"-"` | 	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") | 		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 { | 	if err != nil { | ||||||
| 		return nil, err | 		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. | 	// The resource cache is global so reuse. | ||||||
| 	// TODO(bep) clean up these inits. | 	// TODO(bep) clean up these inits. | ||||||
| 	resourceCache := d.ResourceSpec.ResourceCache | 	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 { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ package hugolib | |||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/common/collections" | 	"github.com/gohugoio/hugo/common/collections" | ||||||
| ) | ) | ||||||
|   | |||||||
| @@ -35,7 +35,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/helpers" | 	"github.com/gohugoio/hugo/helpers" | ||||||
| 	"github.com/gohugoio/hugo/hugolib/pagemeta" | 	"github.com/gohugoio/hugo/hugolib/pagemeta" | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/output" | 	"github.com/gohugoio/hugo/output" | ||||||
| 	"github.com/mitchellh/mapstructure" | 	"github.com/mitchellh/mapstructure" | ||||||
|   | |||||||
| @@ -24,7 +24,7 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/tpl" | 	"github.com/gohugoio/hugo/tpl" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/media" | 	"github.com/gohugoio/hugo/media" | ||||||
|  |  | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ | |||||||
| package hugolib | package hugolib | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
|   | |||||||
| @@ -22,7 +22,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/helpers" | 	"github.com/gohugoio/hugo/helpers" | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources" | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| @@ -255,7 +256,7 @@ func (c *contentHandlers) parsePage(h contentHandler) contentHandler { | |||||||
|  |  | ||||||
| 			// Assign metadata from front matter if set | 			// Assign metadata from front matter if set | ||||||
| 			if len(p.resourcesMetadata) > 0 { | 			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, err := c.s.ResourceSpec.New( | ||||||
| 			resource.ResourceSourceDescriptor{ | 			resources.ResourceSourceDescriptor{ | ||||||
| 				TargetPathBuilder: ctx.parentPage.subResourceTargetPathFactory, | 				TargetPathBuilder: ctx.parentPage.subResourceTargetPathFactory, | ||||||
| 				SourceFile:        ctx.source, | 				SourceFile:        ctx.source, | ||||||
| 				RelTargetFilename: ctx.target, | 				RelTargetFilename: ctx.target, | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"testing" | 	"testing" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| 	"github.com/stretchr/testify/require" | 	"github.com/stretchr/testify/require" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ import ( | |||||||
| 	"github.com/gohugoio/hugo/hugofs" | 	"github.com/gohugoio/hugo/hugofs" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/common/loggers" | 	"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) { | func TestSCSSWithIncludePaths(t *testing.T) { | ||||||
|   | |||||||
| @@ -39,7 +39,6 @@ import ( | |||||||
| 	"github.com/gohugoio/hugo/common/hugo" | 	"github.com/gohugoio/hugo/common/hugo" | ||||||
| 	"github.com/gohugoio/hugo/common/maps" | 	"github.com/gohugoio/hugo/common/maps" | ||||||
| 	"github.com/gohugoio/hugo/publisher" | 	"github.com/gohugoio/hugo/publisher" | ||||||
| 	"github.com/gohugoio/hugo/resource" |  | ||||||
| 	_errors "github.com/pkg/errors" | 	_errors "github.com/pkg/errors" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/langs" | 	"github.com/gohugoio/hugo/langs" | ||||||
| @@ -62,6 +61,7 @@ import ( | |||||||
| 	"github.com/gohugoio/hugo/hugolib/pagemeta" | 	"github.com/gohugoio/hugo/hugolib/pagemeta" | ||||||
| 	"github.com/gohugoio/hugo/output" | 	"github.com/gohugoio/hugo/output" | ||||||
| 	"github.com/gohugoio/hugo/related" | 	"github.com/gohugoio/hugo/related" | ||||||
|  | 	"github.com/gohugoio/hugo/resources" | ||||||
| 	"github.com/gohugoio/hugo/source" | 	"github.com/gohugoio/hugo/source" | ||||||
| 	"github.com/gohugoio/hugo/tpl" | 	"github.com/gohugoio/hugo/tpl" | ||||||
| 	"github.com/spf13/afero" | 	"github.com/spf13/afero" | ||||||
| @@ -760,7 +760,7 @@ func (s *Site) processPartial(events []fsnotify.Event) (whatChanged, error) { | |||||||
| 	cachePartitions := make([]string, len(events)) | 	cachePartitions := make([]string, len(events)) | ||||||
|  |  | ||||||
| 	for i, ev := range events { | 	for i, ev := range events { | ||||||
| 		cachePartitions[i] = resource.ResourceKeyPartition(ev.Name) | 		cachePartitions[i] = resources.ResourceKeyPartition(ev.Name) | ||||||
|  |  | ||||||
| 		if s.isContentDirEvent(ev) { | 		if s.isContentDirEvent(ev) { | ||||||
| 			logger.Println("Source changed", ev) | 			logger.Println("Source changed", ev) | ||||||
|   | |||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| @@ -27,6 +27,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
|  | 
 | ||||||
| 	_errors "github.com/pkg/errors" | 	_errors "github.com/pkg/errors" | ||||||
| 
 | 
 | ||||||
| 	"github.com/disintegration/imaging" | 	"github.com/disintegration/imaging" | ||||||
| @@ -43,9 +45,9 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	_ Resource = (*Image)(nil) | 	_ resource.Resource = (*Image)(nil) | ||||||
| 	_ Source   = (*Image)(nil) | 	_ resource.Source   = (*Image)(nil) | ||||||
| 	_ Cloner   = (*Image)(nil) | 	_ resource.Cloner   = (*Image)(nil) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Imaging contains default image processing configuration. This will be fetched | // Imaging contains default image processing configuration. This will be fetched | ||||||
| @@ -146,7 +148,7 @@ func (i *Image) Height() int { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // WithNewBase implements the Cloner interface. | // WithNewBase implements the Cloner interface. | ||||||
| func (i *Image) WithNewBase(base string) Resource { | func (i *Image) WithNewBase(base string) resource.Resource { | ||||||
| 	return &Image{ | 	return &Image{ | ||||||
| 		imaging:         i.imaging, | 		imaging:         i.imaging, | ||||||
| 		format:          i.format, | 		format:          i.format, | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
							
								
								
									
										48
									
								
								resources/internal/glob.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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 | ||||||
|  |  | ||||||
|  | } | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -24,6 +24,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/gohugoio/hugo/media" | ||||||
|  | 
 | ||||||
| 	"github.com/gohugoio/hugo/output" | 	"github.com/gohugoio/hugo/output" | ||||||
| 	"github.com/gohugoio/hugo/tpl" | 	"github.com/gohugoio/hugo/tpl" | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
| @@ -32,35 +34,28 @@ import ( | |||||||
| 	"github.com/gohugoio/hugo/common/collections" | 	"github.com/gohugoio/hugo/common/collections" | ||||||
| 	"github.com/gohugoio/hugo/common/hugio" | 	"github.com/gohugoio/hugo/common/hugio" | ||||||
| 	"github.com/gohugoio/hugo/common/loggers" | 	"github.com/gohugoio/hugo/common/loggers" | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| 
 | 
 | ||||||
| 	"github.com/spf13/afero" | 	"github.com/spf13/afero" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gobwas/glob" |  | ||||||
| 	"github.com/gohugoio/hugo/helpers" | 	"github.com/gohugoio/hugo/helpers" | ||||||
| 	"github.com/gohugoio/hugo/media" |  | ||||||
| 	"github.com/gohugoio/hugo/source" | 	"github.com/gohugoio/hugo/source" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	_ ContentResource         = (*genericResource)(nil) | 	_ resource.ContentResource         = (*genericResource)(nil) | ||||||
| 	_ ReadSeekCloserResource  = (*genericResource)(nil) | 	_ resource.ReadSeekCloserResource  = (*genericResource)(nil) | ||||||
| 	_ Resource                = (*genericResource)(nil) | 	_ resource.Resource                = (*genericResource)(nil) | ||||||
| 	_ Source                  = (*genericResource)(nil) | 	_ resource.Source                  = (*genericResource)(nil) | ||||||
| 	_ Cloner                  = (*genericResource)(nil) | 	_ resource.Cloner                  = (*genericResource)(nil) | ||||||
| 	_ ResourcesLanguageMerger = (*Resources)(nil) | 	_ resource.ResourcesLanguageMerger = (*resource.Resources)(nil) | ||||||
| 	_ permalinker                      = (*genericResource)(nil) | 	_ permalinker                      = (*genericResource)(nil) | ||||||
| 	_ collections.Slicer               = (*genericResource)(nil) | 	_ collections.Slicer               = (*genericResource)(nil) | ||||||
| 	_ Identifier              = (*genericResource)(nil) | 	_ resource.Identifier              = (*genericResource)(nil) | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var noData = make(map[string]interface{}) | 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 { | type permalinker interface { | ||||||
| 	relPermalinkFor(target string) string | 	relPermalinkFor(target string) string | ||||||
| 	permalinkFor(target string) string | 	permalinkFor(target string) string | ||||||
| @@ -69,215 +64,6 @@ type permalinker interface { | |||||||
| 	targetPath() string | 	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 { | type Spec struct { | ||||||
| 	*helpers.PathSpec | 	*helpers.PathSpec | ||||||
| 
 | 
 | ||||||
| @@ -336,7 +122,7 @@ type ResourceSourceDescriptor struct { | |||||||
| 
 | 
 | ||||||
| 	// Need one of these to load the resource content. | 	// Need one of these to load the resource content. | ||||||
| 	SourceFile         source.File | 	SourceFile         source.File | ||||||
| 	OpenReadSeekCloser OpenReadSeekCloser | 	OpenReadSeekCloser resource.OpenReadSeekCloser | ||||||
| 
 | 
 | ||||||
| 	// If OpenReadSeekerCloser is not set, we use this to open the file. | 	// If OpenReadSeekerCloser is not set, we use this to open the file. | ||||||
| 	SourceFilename string | 	SourceFilename string | ||||||
| @@ -370,15 +156,15 @@ func (r *Spec) sourceFs() afero.Fs { | |||||||
| 	return r.PathSpec.BaseFs.Content.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) | 	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) | 	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.OpenReadSeekCloser == nil { | ||||||
| 		if fd.SourceFile != nil && fd.SourceFilename != "" { | 		if fd.SourceFile != nil && fd.SourceFilename != "" { | ||||||
| 			return nil, errors.New("both SourceFile and AbsSourceFilename provided") | 			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) | 	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 fi os.FileInfo | ||||||
| 	var sourceFilename string | 	var sourceFilename string | ||||||
| 
 | 
 | ||||||
| @@ -551,7 +337,7 @@ type publishOnce struct { | |||||||
| 	logger        *loggers.Logger | 	logger        *loggers.Logger | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (l *publishOnce) publish(s Source) error { | func (l *publishOnce) publish(s resource.Source) error { | ||||||
| 	l.publisherInit.Do(func() { | 	l.publisherInit.Do(func() { | ||||||
| 		l.publisherErr = s.Publish() | 		l.publisherErr = s.Publish() | ||||||
| 		if l.publisherErr != nil { | 		if l.publisherErr != nil { | ||||||
| @@ -577,7 +363,7 @@ type genericResource struct { | |||||||
| 	sourceFilename string | 	sourceFilename string | ||||||
| 
 | 
 | ||||||
| 	// Will be set if this resource is backed by something other than a file. | 	// 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. | 	// A hash of the source content. Is only calculated in caching situations. | ||||||
| 	*resourceHash | 	*resourceHash | ||||||
| @@ -632,7 +418,7 @@ func (l *genericResource) MediaType() media.Type { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Implement the Cloner interface. | // Implement the Cloner interface. | ||||||
| func (l genericResource) WithNewBase(base string) Resource { | func (l genericResource) WithNewBase(base string) resource.Resource { | ||||||
| 	l.baseOffset = base | 	l.baseOffset = base | ||||||
| 	l.resourceContent = &resourceContent{} | 	l.resourceContent = &resourceContent{} | ||||||
| 	return &l | 	return &l | ||||||
| @@ -642,12 +428,12 @@ func (l genericResource) WithNewBase(base string) Resource { | |||||||
| // for the template functions. See collections.Slice. | // for the template functions. See collections.Slice. | ||||||
| func (commonResource) Slice(in interface{}) (interface{}, error) { | func (commonResource) Slice(in interface{}) (interface{}, error) { | ||||||
| 	switch items := in.(type) { | 	switch items := in.(type) { | ||||||
| 	case Resources: | 	case resource.Resources: | ||||||
| 		return items, nil | 		return items, nil | ||||||
| 	case []interface{}: | 	case []interface{}: | ||||||
| 		groups := make(Resources, len(items)) | 		groups := make(resource.Resources, len(items)) | ||||||
| 		for i, v := range items { | 		for i, v := range items { | ||||||
| 			g, ok := v.(Resource) | 			g, ok := v.(resource.Resource) | ||||||
| 			if !ok { | 			if !ok { | ||||||
| 				return nil, fmt.Errorf("type %T is not a Resource", v) | 				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( | func (r *Spec) newGenericResourceWithBase( | ||||||
| 	sourceFs afero.Fs, | 	sourceFs afero.Fs, | ||||||
| 	lazyPublish bool, | 	lazyPublish bool, | ||||||
| 	openReadSeekerCloser OpenReadSeekCloser, | 	openReadSeekerCloser resource.OpenReadSeekCloser, | ||||||
| 	urlBaseDir string, | 	urlBaseDir string, | ||||||
| 	targetPathBaseDirs []string, | 	targetPathBaseDirs []string, | ||||||
| 	targetPathBuilder func(base string) string, | 	targetPathBuilder func(base string) string, | ||||||
							
								
								
									
										123
									
								
								resources/resource/resources.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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 | ||||||
|  | } | ||||||
							
								
								
									
										106
									
								
								resources/resource/resourcetypes.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -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) | ||||||
|  | } | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| @@ -21,6 +21,8 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
|  | 
 | ||||||
| 	"github.com/gohugoio/hugo/cache/filecache" | 	"github.com/gohugoio/hugo/cache/filecache" | ||||||
| 
 | 
 | ||||||
| 	"github.com/BurntSushi/locker" | 	"github.com/BurntSushi/locker" | ||||||
| @@ -35,7 +37,7 @@ type ResourceCache struct { | |||||||
| 	rs *Spec | 	rs *Spec | ||||||
| 
 | 
 | ||||||
| 	sync.RWMutex | 	sync.RWMutex | ||||||
| 	cache map[string]Resource | 	cache map[string]resource.Resource | ||||||
| 
 | 
 | ||||||
| 	fileCache *filecache.Cache | 	fileCache *filecache.Cache | ||||||
| 
 | 
 | ||||||
| @@ -59,7 +61,7 @@ func newResourceCache(rs *Spec) *ResourceCache { | |||||||
| 	return &ResourceCache{ | 	return &ResourceCache{ | ||||||
| 		rs:        rs, | 		rs:        rs, | ||||||
| 		fileCache: rs.FileCaches.AssetsCache(), | 		fileCache: rs.FileCaches.AssetsCache(), | ||||||
| 		cache:     make(map[string]Resource), | 		cache:     make(map[string]resource.Resource), | ||||||
| 		nlocker:   locker.NewLocker(), | 		nlocker:   locker.NewLocker(), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -68,7 +70,7 @@ func (c *ResourceCache) clear() { | |||||||
| 	c.Lock() | 	c.Lock() | ||||||
| 	defer c.Unlock() | 	defer c.Unlock() | ||||||
| 
 | 
 | ||||||
| 	c.cache = make(map[string]Resource) | 	c.cache = make(map[string]resource.Resource) | ||||||
| 	c.nlocker = locker.NewLocker() | 	c.nlocker = locker.NewLocker() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -82,14 +84,14 @@ func (c *ResourceCache) cleanKey(key string) string { | |||||||
| 	return strings.TrimPrefix(path.Clean(key), "/") | 	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() | 	c.RLock() | ||||||
| 	defer c.RUnlock() | 	defer c.RUnlock() | ||||||
| 	r, found := c.cache[key] | 	r, found := c.cache[key] | ||||||
| 	return r, found | 	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)) | 	key = c.cleanKey(path.Join(partition, key)) | ||||||
| 	// First check in-memory cache. | 	// First check in-memory cache. | ||||||
| 	r, found := c.get(key) | 	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() | 	c.Lock() | ||||||
| 	defer c.Unlock() | 	defer c.Unlock() | ||||||
| 	c.cache[key] = r | 	c.cache[key] = r | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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/common/hugio" | ||||||
| 	"github.com/gohugoio/hugo/media" | 	"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 | // Client contains methods perform concatenation and other bundling related | ||||||
| // tasks to Resource objects. | // tasks to Resource objects. | ||||||
| type Client struct { | type Client struct { | ||||||
| 	rs *resource.Spec | 	rs *resources.Spec | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // New creates a new Client with the given specification. | // 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} | 	return &Client{rs: rs} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -62,14 +63,14 @@ func (r *multiReadSeekCloser) Close() error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Concat concatenates the list of Resource objects. | // 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. | 	// 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 | 		var resolvedm media.Type | ||||||
| 
 | 
 | ||||||
| 		// 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 resources { | 		for i, r := range r { | ||||||
| 			if i > 0 && r.MediaType().Type() != resolvedm.Type() { | 			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()) | 				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) { | 		concatr := func() (hugio.ReadSeekCloser, error) { | ||||||
| 			var rcsources []hugio.ReadSeekCloser | 			var rcsources []hugio.ReadSeekCloser | ||||||
| 			for _, s := range resources { | 			for _, s := range r { | ||||||
| 				rcr, ok := s.(resource.ReadSeekCloserResource) | 				rcr, ok := s.(resource.ReadSeekCloserResource) | ||||||
| 				if !ok { | 				if !ok { | ||||||
| 					return nil, fmt.Errorf("resource %T does not implement resource.ReadSeekerCloserResource", s) | 					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( | 		composite, err := c.rs.NewForFs( | ||||||
| 			c.rs.FileCaches.AssetsCache().Fs, | 			c.rs.FileCaches.AssetsCache().Fs, | ||||||
| 			resource.ResourceSourceDescriptor{ | 			resources.ResourceSourceDescriptor{ | ||||||
| 				LazyPublish:        true, | 				LazyPublish:        true, | ||||||
| 				OpenReadSeekCloser: concatr, | 				OpenReadSeekCloser: concatr, | ||||||
| 				RelTargetFilename:  filepath.Clean(targetPath)}) | 				RelTargetFilename:  filepath.Clean(targetPath)}) | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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/spf13/afero" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gohugoio/hugo/common/hugio" | 	"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. | // Client contains methods to create Resource objects. | ||||||
| // tasks to Resource objects. | // tasks to Resource objects. | ||||||
| type Client struct { | type Client struct { | ||||||
| 	rs *resource.Spec | 	rs *resources.Spec | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // New creates a new Client with the given specification. | // 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} | 	return &Client{rs: rs} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Get creates a new Resource by opening the given filename in the given filesystem. | // 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) { | func (c *Client) Get(fs afero.Fs, filename string) (resource.Resource, error) { | ||||||
| 	filename = filepath.Clean(filename) | 	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, | 		return c.rs.NewForFs(fs, | ||||||
| 			resource.ResourceSourceDescriptor{ | 			resources.ResourceSourceDescriptor{ | ||||||
| 				LazyPublish:    true, | 				LazyPublish:    true, | ||||||
| 				SourceFilename: filename}) | 				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. | // FromString creates a new Resource from a string with the given relative target path. | ||||||
| func (c *Client) FromString(targetPath, content string) (resource.Resource, error) { | 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( | 		return c.rs.NewForFs( | ||||||
| 			c.rs.FileCaches.AssetsCache().Fs, | 			c.rs.FileCaches.AssetsCache().Fs, | ||||||
| 			resource.ResourceSourceDescriptor{ | 			resources.ResourceSourceDescriptor{ | ||||||
| 				LazyPublish: true, | 				LazyPublish: true, | ||||||
| 				OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { | 				OpenReadSeekCloser: func() (hugio.ReadSeekCloser, error) { | ||||||
| 					return hugio.NewReadSeekerNoOpCloserFromString(content), nil | 					return hugio.NewReadSeekerNoOpCloserFromString(content), nil | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/gohugoio/hugo/resources/internal" | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
|  | 
 | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
| 	"github.com/spf13/cast" | 	"github.com/spf13/cast" | ||||||
| 
 | 
 | ||||||
| @@ -43,7 +46,7 @@ const counterPlaceHolder = ":counter" | |||||||
| // This assignment is additive, but the most specific match needs to be first. | // 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. | // The `name` and `title` metadata field support shell-matched collection it got a match in. | ||||||
| // See https://golang.org/pkg/path/#Match | // 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) | 	counters := make(map[string]int) | ||||||
| 
 | 
 | ||||||
| @@ -68,7 +71,7 @@ func AssignMetadata(metadata []map[string]interface{}, resources ...Resource) er | |||||||
| 
 | 
 | ||||||
| 			srcKey := strings.ToLower(cast.ToString(src)) | 			srcKey := strings.ToLower(cast.ToString(src)) | ||||||
| 
 | 
 | ||||||
| 			glob, err := getGlob(srcKey) | 			glob, err := internal.GetGlob(srcKey) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return errors.Wrap(err, "failed to match resource with metadata") | 				return errors.Wrap(err, "failed to match resource with metadata") | ||||||
| 			} | 			} | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gohugoio/hugo/media" | 	"github.com/gohugoio/hugo/media" | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| 
 | 
 | ||||||
| 	"github.com/stretchr/testify/require" | 	"github.com/stretchr/testify/require" | ||||||
| ) | ) | ||||||
| @@ -25,8 +26,8 @@ func TestAssignMetadata(t *testing.T) { | |||||||
| 	assert := require.New(t) | 	assert := require.New(t) | ||||||
| 	spec := newTestResourceSpec(assert) | 	spec := newTestResourceSpec(assert) | ||||||
| 
 | 
 | ||||||
| 	var foo1, foo2, foo3, logo1, logo2, logo3 Resource | 	var foo1, foo2, foo3, logo1, logo2, logo3 resource.Resource | ||||||
| 	var resources Resources | 	var resources resource.Resources | ||||||
| 
 | 
 | ||||||
| 	for _, this := range []struct { | 	for _, this := range []struct { | ||||||
| 		metaData   []map[string]interface{} | 		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) | 		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) | 		logo3 = spec.newGenericResource(nil, nil, nil, "/b/logo3.png", "logo3.png", pngType) | ||||||
| 
 | 
 | ||||||
| 		resources = Resources{ | 		resources = resource.Resources{ | ||||||
| 			foo2, | 			foo2, | ||||||
| 			logo2, | 			logo2, | ||||||
| 			foo1, | 			foo1, | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -22,6 +22,8 @@ import ( | |||||||
| 	"testing" | 	"testing" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
|  | 
 | ||||||
| 	"github.com/gohugoio/hugo/media" | 	"github.com/gohugoio/hugo/media" | ||||||
| 
 | 
 | ||||||
| 	"github.com/stretchr/testify/require" | 	"github.com/stretchr/testify/require" | ||||||
| @@ -75,7 +77,7 @@ func TestNewResourceFromFilename(t *testing.T) { | |||||||
| 	assert.NotNil(r) | 	assert.NotNil(r) | ||||||
| 	assert.Equal("json", r.ResourceType()) | 	assert.Equal("json", r.ResourceType()) | ||||||
| 
 | 
 | ||||||
| 	cloned := r.(Cloner).WithNewBase("aceof") | 	cloned := r.(resource.Cloner).WithNewBase("aceof") | ||||||
| 	assert.Equal(r.ResourceType(), cloned.ResourceType()) | 	assert.Equal(r.ResourceType(), cloned.ResourceType()) | ||||||
| 	assert.Equal("/aceof/a/b/data.json", cloned.RelPermalink()) | 	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) { | func TestResourcesByType(t *testing.T) { | ||||||
| 	assert := require.New(t) | 	assert := require.New(t) | ||||||
| 	spec := newTestResourceSpec(assert) | 	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/foo1.css", "foo1.css", media.CSSType), | ||||||
| 		spec.newGenericResource(nil, nil, nil, "/a/logo.png", "logo.css", pngType), | 		spec.newGenericResource(nil, nil, nil, "/a/logo.png", "logo.css", pngType), | ||||||
| 		spec.newGenericResource(nil, nil, nil, "/a/foo2.css", "foo2.css", media.CSSType), | 		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) { | func TestResourcesGetByPrefix(t *testing.T) { | ||||||
| 	assert := require.New(t) | 	assert := require.New(t) | ||||||
| 	spec := newTestResourceSpec(assert) | 	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/foo1.css", "foo1.css", media.CSSType), | ||||||
| 		spec.newGenericResource(nil, nil, nil, "/a/logo1.png", "logo1.png", pngType), | 		spec.newGenericResource(nil, nil, nil, "/a/logo1.png", "logo1.png", pngType), | ||||||
| 		spec.newGenericResource(nil, nil, nil, "/b/Logo2.png", "Logo2.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) { | func TestResourcesGetMatch(t *testing.T) { | ||||||
| 	assert := require.New(t) | 	assert := require.New(t) | ||||||
| 	spec := newTestResourceSpec(assert) | 	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/foo1.css", "foo1.css", media.CSSType), | ||||||
| 		spec.newGenericResource(nil, nil, nil, "/a/logo1.png", "logo1.png", pngType), | 		spec.newGenericResource(nil, nil, nil, "/a/logo1.png", "logo1.png", pngType), | ||||||
| 		spec.newGenericResource(nil, nil, nil, "/b/Logo2.png", "Logo2.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) | 	a100 := strings.Repeat("a", 100) | ||||||
| 	pattern := "a*a*a*a*a*a*a*a*b" | 	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() | 	b.ResetTimer() | ||||||
| 	for i := 0; i < b.N; i++ { | 	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) | 	assert := require.New(b) | ||||||
| 	spec := newTestResourceSpec(assert) | 	spec := newTestResourceSpec(assert) | ||||||
| 	var resources Resources | 	var resources resource.Resources | ||||||
| 
 | 
 | ||||||
| 	for i := 0; i < 30; i++ { | 	for i := 0; i < 30; i++ { | ||||||
| 		name := fmt.Sprintf("abcde%d_%d.css", i%5, 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++ { | 	for i := 0; i < b.N; i++ { | ||||||
| 		b.StopTimer() | 		b.StopTimer() | ||||||
| 		var resources Resources | 		var resources resource.Resources | ||||||
| 		var meta = []map[string]interface{}{ | 		var meta = []map[string]interface{}{ | ||||||
| 			{ | 			{ | ||||||
| 				"title": "Foo #:counter", | 				"title": "Foo #:counter", | ||||||
| @@ -24,7 +24,8 @@ import ( | |||||||
| 	"html/template" | 	"html/template" | ||||||
| 	"io" | 	"io" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources" | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const defaultHashAlgo = "sha256" | const defaultHashAlgo = "sha256" | ||||||
| @@ -32,11 +33,11 @@ const defaultHashAlgo = "sha256" | |||||||
| // Client contains methods to fingerprint (cachebusting) and other integrity-related | // Client contains methods to fingerprint (cachebusting) and other integrity-related | ||||||
| // methods. | // methods. | ||||||
| type Client struct { | type Client struct { | ||||||
| 	rs *resource.Spec | 	rs *resources.Spec | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // New creates a new Client with the given specification. | // 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} | 	return &Client{rs: rs} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -44,13 +45,13 @@ type fingerprintTransformation struct { | |||||||
| 	algo string | 	algo string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *fingerprintTransformation) Key() resource.ResourceTransformationKey { | func (t *fingerprintTransformation) Key() resources.ResourceTransformationKey { | ||||||
| 	return resource.NewResourceTransformationKey("fingerprint", t.algo) | 	return resources.NewResourceTransformationKey("fingerprint", t.algo) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Transform creates a MD5 hash of the Resource content and inserts that hash before | // Transform creates a MD5 hash of the Resource content and inserts that hash before | ||||||
| // the extension in the filename. | // the extension in the filename. | ||||||
| func (t *fingerprintTransformation) Transform(ctx *resource.ResourceTransformationCtx) error { | func (t *fingerprintTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { | ||||||
| 	algo := t.algo | 	algo := t.algo | ||||||
| 
 | 
 | ||||||
| 	var h hash.Hash | 	var h hash.Hash | ||||||
| @@ -15,32 +15,33 @@ package minifier | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/gohugoio/hugo/minifiers" | 	"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: | // Client for minification of Resource objects. Supported minfiers are: | ||||||
| // css, html, js, json, svg and xml. | // css, html, js, json, svg and xml. | ||||||
| type Client struct { | type Client struct { | ||||||
| 	rs *resource.Spec | 	rs *resources.Spec | ||||||
| 	m  minifiers.Client | 	m  minifiers.Client | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // New creates a new Client given a specification. Note that it is the media types | // 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. | // 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)} | 	return &Client{rs: rs, m: minifiers.New(rs.MediaTypes, rs.OutputFormats)} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type minifyTransformation struct { | type minifyTransformation struct { | ||||||
| 	rs *resource.Spec | 	rs *resources.Spec | ||||||
| 	m  minifiers.Client | 	m  minifiers.Client | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *minifyTransformation) Key() resource.ResourceTransformationKey { | func (t *minifyTransformation) Key() resources.ResourceTransformationKey { | ||||||
| 	return resource.NewResourceTransformationKey("minify") | 	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 { | 	if err := t.m.Minify(ctx.InMediaType, ctx.To, ctx.From); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| @@ -26,7 +26,8 @@ import ( | |||||||
| 	"github.com/mitchellh/mapstructure" | 	"github.com/mitchellh/mapstructure" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gohugoio/hugo/common/herrors" | 	"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 | // 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. | // Client is the client used to do PostCSS transformations. | ||||||
| type Client struct { | type Client struct { | ||||||
| 	rs *resource.Spec | 	rs *resources.Spec | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // New creates a new Client with the given specification. | // 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} | 	return &Client{rs: rs} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type postcssTransformation struct { | type postcssTransformation struct { | ||||||
| 	options Options | 	options Options | ||||||
| 	rs      *resource.Spec | 	rs      *resources.Spec | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *postcssTransformation) Key() resource.ResourceTransformationKey { | func (t *postcssTransformation) Key() resources.ResourceTransformationKey { | ||||||
| 	return resource.NewResourceTransformationKey("postcss", t.options) | 	return resources.NewResourceTransformationKey("postcss", t.options) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Transform shells out to postcss-cli to do the heavy lifting. | // Transform shells out to postcss-cli to do the heavy lifting. | ||||||
| // For this to work, you need some additional tools. To install them globally: | // For this to work, you need some additional tools. To install them globally: | ||||||
| // npm install -g postcss-cli | // npm install -g postcss-cli | ||||||
| // npm install -g autoprefixer | // 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 localPostCSSPath = "node_modules/postcss-cli/bin/" | ||||||
| 	const binaryName = "postcss" | 	const binaryName = "postcss" | ||||||
| @@ -16,20 +16,21 @@ package templates | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/gohugoio/hugo/helpers" | 	"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/gohugoio/hugo/tpl" | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Client contains methods to perform template processing of Resource objects. | // Client contains methods to perform template processing of Resource objects. | ||||||
| type Client struct { | type Client struct { | ||||||
| 	rs *resource.Spec | 	rs *resources.Spec | ||||||
| 
 | 
 | ||||||
| 	textTemplate tpl.TemplateParseFinder | 	textTemplate tpl.TemplateParseFinder | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // New creates a new Client with the given specification. | // 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 { | 	if rs == nil { | ||||||
| 		panic("must provice a resource Spec") | 		panic("must provice a resource Spec") | ||||||
| 	} | 	} | ||||||
| @@ -40,17 +41,17 @@ func New(rs *resource.Spec, textTemplate tpl.TemplateParseFinder) *Client { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type executeAsTemplateTransform struct { | type executeAsTemplateTransform struct { | ||||||
| 	rs           *resource.Spec | 	rs           *resources.Spec | ||||||
| 	textTemplate tpl.TemplateParseFinder | 	textTemplate tpl.TemplateParseFinder | ||||||
| 	targetPath   string | 	targetPath   string | ||||||
| 	data         interface{} | 	data         interface{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *executeAsTemplateTransform) Key() resource.ResourceTransformationKey { | func (t *executeAsTemplateTransform) Key() resources.ResourceTransformationKey { | ||||||
| 	return resource.NewResourceTransformationKey("execute-as-template", t.targetPath) | 	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) | 	tplStr := helpers.ReaderToString(ctx.From) | ||||||
| 	templ, err := t.textTemplate.Parse(ctx.InPath, tplStr) | 	templ, err := t.textTemplate.Parse(ctx.InPath, tplStr) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -17,17 +17,19 @@ import ( | |||||||
| 	"github.com/bep/go-tocss/scss" | 	"github.com/bep/go-tocss/scss" | ||||||
| 	"github.com/gohugoio/hugo/helpers" | 	"github.com/gohugoio/hugo/helpers" | ||||||
| 	"github.com/gohugoio/hugo/hugolib/filesystems" | 	"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" | 	"github.com/mitchellh/mapstructure" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Client struct { | type Client struct { | ||||||
| 	rs     *resource.Spec | 	rs     *resources.Spec | ||||||
| 	sfs    *filesystems.SourceFilesystem | 	sfs    *filesystems.SourceFilesystem | ||||||
| 	workFs *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 | 	return &Client{sfs: fs, workFs: rs.BaseFs.Work, rs: rs}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @@ -91,8 +93,8 @@ type toCSSTransformation struct { | |||||||
| 	options options | 	options options | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *toCSSTransformation) Key() resource.ResourceTransformationKey { | func (t *toCSSTransformation) Key() resources.ResourceTransformationKey { | ||||||
| 	return resource.NewResourceTransformationKey("tocss", t.options.from) | 	return resources.NewResourceTransformationKey("tocss", t.options.from) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func DecodeOptions(m map[string]interface{}) (opts Options, err error) { | func DecodeOptions(m map[string]interface{}) (opts Options, err error) { | ||||||
| @@ -28,7 +28,7 @@ import ( | |||||||
| 	"github.com/gohugoio/hugo/helpers" | 	"github.com/gohugoio/hugo/helpers" | ||||||
| 	"github.com/gohugoio/hugo/hugofs" | 	"github.com/gohugoio/hugo/hugofs" | ||||||
| 	"github.com/gohugoio/hugo/media" | 	"github.com/gohugoio/hugo/media" | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources" | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| @@ -37,7 +37,7 @@ func Supports() bool { | |||||||
| 	return true | 	return true | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *toCSSTransformation) Transform(ctx *resource.ResourceTransformationCtx) error { | func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { | ||||||
| 	ctx.OutMediaType = media.CSSType | 	ctx.OutMediaType = media.CSSType | ||||||
| 
 | 
 | ||||||
| 	var outName string | 	var outName string | ||||||
| @@ -17,7 +17,7 @@ package scss | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"github.com/gohugoio/hugo/common/herrors" | 	"github.com/gohugoio/hugo/common/herrors" | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // Used in tests. | // Used in tests. | ||||||
| @@ -25,6 +25,6 @@ func Supports() bool { | |||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (t *toCSSTransformation) Transform(ctx *resource.ResourceTransformationCtx) error { | func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { | ||||||
| 	return herrors.ErrFeatureNotAvailable | 	return herrors.ErrFeatureNotAvailable | ||||||
| } | } | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"image" | 	"image" | ||||||
| Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB | 
| Before Width: | Height: | Size: 172 B After Width: | Height: | Size: 172 B | 
| Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB | 
| Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB | 
| Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB | 
| @@ -1,4 +1,4 @@ | |||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"path/filepath" | 	"path/filepath" | ||||||
| @@ -18,6 +18,7 @@ import ( | |||||||
| 	"github.com/gohugoio/hugo/hugofs" | 	"github.com/gohugoio/hugo/hugofs" | ||||||
| 	"github.com/gohugoio/hugo/media" | 	"github.com/gohugoio/hugo/media" | ||||||
| 	"github.com/gohugoio/hugo/output" | 	"github.com/gohugoio/hugo/output" | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| 	"github.com/spf13/afero" | 	"github.com/spf13/afero" | ||||||
| 	"github.com/spf13/viper" | 	"github.com/spf13/viper" | ||||||
| 	"github.com/stretchr/testify/require" | 	"github.com/stretchr/testify/require" | ||||||
| @@ -112,7 +113,7 @@ func fetchImageForSpec(spec *Spec, assert *require.Assertions, name string) *Ima | |||||||
| 	return r.(*Image) | 	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)) | 	src, err := os.Open(filepath.FromSlash("testdata/" + name)) | ||||||
| 	assert.NoError(err) | 	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}) | 	r, err := spec.New(ResourceSourceDescriptor{TargetPathBuilder: factory, SourceFilename: name}) | ||||||
| 	assert.NoError(err) | 	assert.NoError(err) | ||||||
| 
 | 
 | ||||||
| 	return r.(ContentResource) | 	return r.(resource.ContentResource) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func assertImageFile(assert *require.Assertions, fs afero.Fs, filename string, width, height int) { | func assertImageFile(assert *require.Assertions, fs afero.Fs, filename string, width, height int) { | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
| @@ -23,6 +23,7 @@ import ( | |||||||
| 	"github.com/gohugoio/hugo/common/herrors" | 	"github.com/gohugoio/hugo/common/herrors" | ||||||
| 	"github.com/gohugoio/hugo/common/hugio" | 	"github.com/gohugoio/hugo/common/hugio" | ||||||
| 	"github.com/gohugoio/hugo/helpers" | 	"github.com/gohugoio/hugo/helpers" | ||||||
|  | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| 	"github.com/mitchellh/hashstructure" | 	"github.com/mitchellh/hashstructure" | ||||||
| 
 | 
 | ||||||
| 	"fmt" | 	"fmt" | ||||||
| @@ -35,13 +36,13 @@ import ( | |||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	_ ContentResource        = (*transformedResource)(nil) | 	_ resource.ContentResource        = (*transformedResource)(nil) | ||||||
| 	_ ReadSeekCloserResource = (*transformedResource)(nil) | 	_ resource.ReadSeekCloserResource = (*transformedResource)(nil) | ||||||
| 	_ collections.Slicer              = (*transformedResource)(nil) | 	_ collections.Slicer              = (*transformedResource)(nil) | ||||||
| 	_ Identifier             = (*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{ | 	return &transformedResource{ | ||||||
| 		Resource:                    r, | 		Resource:                    r, | ||||||
| 		transformation:              t, | 		transformation:              t, | ||||||
| @@ -195,7 +196,7 @@ type transformedResource struct { | |||||||
| 	transformedResourceMetadata | 	transformedResourceMetadata | ||||||
| 
 | 
 | ||||||
| 	// The source | 	// The source | ||||||
| 	Resource | 	resource.Resource | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (r *transformedResource) ReadSeekCloser() (hugio.ReadSeekCloser, error) { | 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. | 	// This can be the last resource in a chain. | ||||||
| 	// Rewind and create a processing chain. | 	// Rewind and create a processing chain. | ||||||
| 	var chain []Resource | 	var chain []resource.Resource | ||||||
| 	current := r | 	current := r | ||||||
| 	for { | 	for { | ||||||
| 		rr := current.Resource | 		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 { | 		if tr, ok := rr.(*transformedResource); ok { | ||||||
| 			current = tr | 			current = tr | ||||||
| 		} else { | 		} else { | ||||||
| @@ -538,9 +539,9 @@ func (r *transformedResource) initTransform(setContent, publish bool) error { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // contentReadSeekerCloser returns a ReadSeekerCloser if possible for a given Resource. | // 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) { | 	switch rr := r.(type) { | ||||||
| 	case ReadSeekCloserResource: | 	case resource.ReadSeekCloserResource: | ||||||
| 		rc, err := rr.ReadSeekCloser() | 		rc, err := rr.ReadSeekCloser() | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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 | // See the License for the specific language governing permissions and | ||||||
| // limitations under the License. | // limitations under the License. | ||||||
| 
 | 
 | ||||||
| package resource | package resources | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"testing" | 	"testing" | ||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with the License. | // you may not use this file except in compliance with the License. | ||||||
| @@ -22,14 +22,14 @@ import ( | |||||||
| 	_errors "github.com/pkg/errors" | 	_errors "github.com/pkg/errors" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/deps" | 	"github.com/gohugoio/hugo/deps" | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| 	"github.com/gohugoio/hugo/resource/resource_factories/bundler" | 	"github.com/gohugoio/hugo/resources/resource_factories/bundler" | ||||||
| 	"github.com/gohugoio/hugo/resource/resource_factories/create" | 	"github.com/gohugoio/hugo/resources/resource_factories/create" | ||||||
| 	"github.com/gohugoio/hugo/resource/resource_transformers/integrity" | 	"github.com/gohugoio/hugo/resources/resource_transformers/integrity" | ||||||
| 	"github.com/gohugoio/hugo/resource/resource_transformers/minifier" | 	"github.com/gohugoio/hugo/resources/resource_transformers/minifier" | ||||||
| 	"github.com/gohugoio/hugo/resource/resource_transformers/postcss" | 	"github.com/gohugoio/hugo/resources/resource_transformers/postcss" | ||||||
| 	"github.com/gohugoio/hugo/resource/resource_transformers/templates" | 	"github.com/gohugoio/hugo/resources/resource_transformers/templates" | ||||||
| 	"github.com/gohugoio/hugo/resource/resource_transformers/tocss/scss" | 	"github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss" | ||||||
| 	"github.com/spf13/cast" | 	"github.com/spf13/cast" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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/helpers" | ||||||
| 	"github.com/gohugoio/hugo/parser/metadecoders" | 	"github.com/gohugoio/hugo/parser/metadecoders" | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| 	"github.com/pkg/errors" | 	"github.com/pkg/errors" | ||||||
|  |  | ||||||
| 	"github.com/spf13/cast" | 	"github.com/spf13/cast" | ||||||
|   | |||||||
| @@ -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"); | // Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
| // you may not use this file except in compliance with 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/media" | ||||||
|  |  | ||||||
| 	"github.com/gohugoio/hugo/resource" | 	"github.com/gohugoio/hugo/resources/resource" | ||||||
| 	"github.com/spf13/viper" | 	"github.com/spf13/viper" | ||||||
| 	"github.com/stretchr/testify/require" | 	"github.com/stretchr/testify/require" | ||||||
| ) | ) | ||||||
|   | |||||||