Add resources.GetRemote

In Hugo 0.89 we added remote support to `resources.Get`.

In hindsight that was not a great idea, as a poll from many Hugo users showed. See Issue #9285 for more details.

After this commit `resources.Get` only supports local resource lookups. If you want to support both, you need to use a construct similar to:

Also improve some option case handling.

```
{{ resource := "" }}
{{ if (urls.Parse $url).IsAbs }}
{{ $resource = resources.GetRemote $url }}
{{ else }}
{{ $resource = resources.Get $url }}
{{ end }}
```

Fixes #9285
Fixes #9296
This commit is contained in:
Bjørn Erik Pedersen
2021-12-16 11:09:21 +01:00
parent 5758c370ea
commit 22ef5da20d
7 changed files with 375 additions and 253 deletions

View File

@@ -16,7 +16,6 @@ package resources
import (
"fmt"
"net/url"
"path/filepath"
"sync"
@@ -108,42 +107,61 @@ func (ns *Namespace) getscssClientDartSass() (*dartsass.Client, error) {
return ns.scssClientDartSass, err
}
// Get locates the filename given in Hugo's assets filesystem or downloads
// a file from an URL and creates a Resource object that can be used for
// Get locates the filename given in Hugo's assets filesystem and
// creates a Resource object that can be used for
// further transformations.
func (ns *Namespace) Get(filename interface{}) resource.Resource {
get := func(args ...interface{}) (resource.Resource, error) {
filenamestr, err := cast.ToStringE(filename)
if err != nil {
return nil, err
}
return ns.createClient.Get(filepath.Clean(filenamestr))
}
r, err := get(filename)
if err != nil {
// This allows the client to reason about the .Err in the template.
// This is not as relevant for local resources as remotes, but
// it makes this method work the same way as resources.GetRemote.
return resources.NewErrorResource(errors.Wrap(err, "error calling resources.Get"))
}
return r
}
// GetRemote gets the URL (via HTTP(s)) in the first argument in args and creates Resource object that can be used for
// further transformations.
//
// For URLs an additional argument with options can be provided.
func (ns *Namespace) Get(args ...interface{}) resource.Resource {
// A second argument may be provided with an option map.
func (ns *Namespace) GetRemote(args ...interface{}) resource.Resource {
get := func(args ...interface{}) (resource.Resource, error) {
if len(args) != 1 && len(args) != 2 {
return nil, errors.New("must provide a filename or URL")
if len(args) < 1 {
return nil, errors.New("must provide an URL")
}
filenamestr, err := cast.ToStringE(args[0])
urlstr, err := cast.ToStringE(args[0])
if err != nil {
return nil, err
}
if u, err := url.Parse(filenamestr); err == nil && u.Scheme != "" {
if len(args) == 2 {
options, err := maps.ToStringMapE(args[1])
if err != nil {
return nil, err
}
return ns.createClient.FromRemote(filenamestr, options)
var options map[string]interface{}
if len(args) > 1 {
options, err = maps.ToStringMapE(args[1])
if err != nil {
return nil, err
}
return ns.createClient.FromRemote(filenamestr, nil)
}
filenamestr = filepath.Clean(filenamestr)
return ns.createClient.FromRemote(urlstr, options)
return ns.createClient.Get(filenamestr)
}
r, err := get(args...)
if err != nil {
// This allows the client to reason about the .Err in the template.
return resources.NewErrorResource(errors.Wrap(err, "error calling resources.Get"))
return resources.NewErrorResource(errors.Wrap(err, "error calling resources.GetRemote"))
}
return r