URI: 
       Add resources.Copy - hugo - [fork] hugo port for 9front
  HTML git clone git@git.drkhsh.at/hugo.git
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
   DIR README
   DIR LICENSE
       ---
   DIR commit cd0112a05a9ddb7043c9808284f93d8099c48473
   DIR parent 6f7fbe03b1a70733f00da6556a89250a29e53ec8
  HTML Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
       Date:   Tue, 24 May 2022 09:34:36 +0200
       
       Add resources.Copy
       
       Implemented by most Resource objects, but not Page (for now).
       
       Fixes #9313
       
       Diffstat:
         M helpers/general.go                  |      10 +---------
         M resources/image.go                  |      11 ++++++++++-
         M resources/resource.go               |      27 ++++++++++++++++++++++++++-
         M resources/resource/resourcetypes.go |       3 +--
         M resources/resource_factories/creat… |       7 +++++++
         M resources/transform.go              |      15 +++++++++++++++
         A tpl/resources/integration_test.go   |     100 +++++++++++++++++++++++++++++++
         M tpl/resources/resources.go          |       9 +++++++++
       
       8 files changed, 169 insertions(+), 13 deletions(-)
       ---
   DIR diff --git a/helpers/general.go b/helpers/general.go
       @@ -33,8 +33,6 @@ import (
        
                "github.com/mitchellh/hashstructure"
        
       -        "github.com/gohugoio/hugo/hugofs"
       -
                "github.com/gohugoio/hugo/common/hugo"
        
                "github.com/spf13/afero"
       @@ -521,13 +519,7 @@ func PrintFs(fs afero.Fs, path string, w io.Writer) {
                }
        
                afero.Walk(fs, path, func(path string, info os.FileInfo, err error) error {
       -                var filename string
       -                var meta any
       -                if fim, ok := info.(hugofs.FileMetaInfo); ok {
       -                        filename = fim.Meta().Filename
       -                        meta = fim.Meta()
       -                }
       -                fmt.Fprintf(w, "    %q %q\t\t%v\n", path, filename, meta)
       +                fmt.Println(path)
                        return nil
                })
        }
   DIR diff --git a/resources/image.go b/resources/image.go
       @@ -134,7 +134,7 @@ func (i *imageResource) getExif() *exif.ExifInfo {
                return i.meta.Exif
        }
        
       -// Cloneis for internal use.
       +// Clone is for internal use.
        func (i *imageResource) Clone() resource.Resource {
                gr := i.baseResource.Clone().(baseResource)
                return &imageResource{
       @@ -144,6 +144,15 @@ func (i *imageResource) Clone() resource.Resource {
                }
        }
        
       +func (i *imageResource) cloneTo(targetPath string) resource.Resource {
       +        gr := i.baseResource.cloneTo(targetPath).(baseResource)
       +        return &imageResource{
       +                root:         i.root,
       +                Image:        i.WithSpec(gr),
       +                baseResource: gr,
       +        }
       +}
       +
        func (i *imageResource) cloneWithUpdates(u *transformationUpdate) (baseResource, error) {
                base, err := i.baseResource.cloneWithUpdates(u)
                if err != nil {
   DIR diff --git a/resources/resource.go b/resources/resource.go
       @@ -1,4 +1,4 @@
       -// Copyright 2019 The Hugo Authors. All rights reserved.
       +// Copyright 2022 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.
       @@ -120,8 +120,19 @@ func (t transformerNotAvailable) Key() internal.ResourceTransformationKey {
                return t.key
        }
        
       +// resourceCopier is for internal use.
       +type resourceCopier interface {
       +        cloneTo(targetPath string) resource.Resource
       +}
       +
       +// Copy copies r to the targetPath given.
       +func Copy(r resource.Resource, targetPath string) resource.Resource {
       +        return r.(resourceCopier).cloneTo(targetPath)
       +}
       +
        type baseResourceResource interface {
                resource.Cloner
       +        resourceCopier
                resource.ContentProvider
                resource.Resource
                resource.Identifier
       @@ -225,6 +236,20 @@ func (l *genericResource) Clone() resource.Resource {
                return l.clone()
        }
        
       +func (l *genericResource) cloneTo(targetPath string) resource.Resource {
       +        c := l.clone()
       +
       +        targetPath = helpers.ToSlashTrimLeading(targetPath)
       +        dir, file := path.Split(targetPath)
       +
       +        c.resourcePathDescriptor = &resourcePathDescriptor{
       +                relTargetDirFile: dirFile{dir: dir, file: file},
       +        }
       +
       +        return c
       +
       +}
       +
        func (l *genericResource) Content() (any, error) {
                if err := l.initContent(); err != nil {
                        return nil, err
   DIR diff --git a/resources/resource/resourcetypes.go b/resources/resource/resourcetypes.go
       @@ -26,8 +26,7 @@ var (
                _ ResourceError        = (*resourceError)(nil)
        )
        
       -// Cloner is an internal template and not meant for use in the templates. It
       -// may change without notice.
       +// Cloner is for internal use.
        type Cloner interface {
                Clone() Resource
        }
   DIR diff --git a/resources/resource_factories/create/create.go b/resources/resource_factories/create/create.go
       @@ -51,6 +51,13 @@ func New(rs *resources.Spec) *Client {
                }
        }
        
       +// Copy copies r to the new targetPath.
       +func (c *Client) Copy(r resource.Resource, targetPath string) (resource.Resource, error) {
       +        return c.rs.ResourceCache.GetOrCreate(resources.ResourceCacheKey(targetPath), func() (resource.Resource, error) {
       +                return resources.Copy(r, targetPath), nil
       +        })
       +}
       +
        // Get creates a new Resource by opening the given filename in the assets filesystem.
        func (c *Client) Get(filename string) (resource.Resource, error) {
                filename = filepath.Clean(filename)
   DIR diff --git a/resources/transform.go b/resources/transform.go
       @@ -42,6 +42,7 @@ import (
        
        var (
                _ resource.ContentResource        = (*resourceAdapter)(nil)
       +        _ resourceCopier                  = (*resourceAdapter)(nil)
                _ resource.ReadSeekCloserResource = (*resourceAdapter)(nil)
                _ resource.Resource               = (*resourceAdapter)(nil)
                _ resource.Source                 = (*resourceAdapter)(nil)
       @@ -175,6 +176,19 @@ func (r *resourceAdapter) Data() any {
                return r.target.Data()
        }
        
       +func (r resourceAdapter) cloneTo(targetPath string) resource.Resource {
       +        newtTarget := r.target.cloneTo(targetPath)
       +        newInner := &resourceAdapterInner{
       +                spec:   r.spec,
       +                target: newtTarget.(transformableResource),
       +        }
       +        if r.resourceAdapterInner.publishOnce != nil {
       +                newInner.publishOnce = &publishOnce{}
       +        }
       +        r.resourceAdapterInner = newInner
       +        return &r
       +}
       +
        func (r *resourceAdapter) Crop(spec string) (images.ImageResource, error) {
                return r.getImageOps().Crop(spec)
        }
       @@ -596,6 +610,7 @@ type transformableResource interface {
                resource.ContentProvider
                resource.Resource
                resource.Identifier
       +        resourceCopier
        }
        
        type transformationUpdate struct {
   DIR diff --git a/tpl/resources/integration_test.go b/tpl/resources/integration_test.go
       @@ -0,0 +1,100 @@
       +// Copyright 2022s 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 resources_test
       +
       +import (
       +        "testing"
       +
       +        qt "github.com/frankban/quicktest"
       +        "github.com/gohugoio/hugo/hugolib"
       +)
       +
       +func TestCopy(t *testing.T) {
       +        t.Parallel()
       +
       +        files := `
       +-- config.toml --
       +baseURL = "http://example.com/blog"
       +-- assets/images/pixel.png --
       +iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
       +-- layouts/index.html --
       +{{/* Image resources */}}
       +{{ $img := resources.Get "images/pixel.png" }}
       +{{ $imgCopy1 := $img | resources.Copy "images/copy.png"  }}
       +{{ $imgCopy1 = $imgCopy1.Resize "3x4"}}
       +{{ $imgCopy2 := $imgCopy1 | resources.Copy "images/copy2.png" }}
       +{{ $imgCopy3 := $imgCopy1 | resources.Copy "images/copy3.png" }}
       +Image Orig:  {{ $img.RelPermalink}}|{{ $img.MediaType }}|{{ $img.Width }}|{{ $img.Height }}|
       +Image Copy1:  {{ $imgCopy1.RelPermalink}}|{{ $imgCopy1.MediaType }}|{{ $imgCopy1.Width }}|{{ $imgCopy1.Height }}|
       +Image Copy2:  {{ $imgCopy2.RelPermalink}}|{{ $imgCopy2.MediaType }}|{{ $imgCopy2.Width }}|{{ $imgCopy2.Height }}|
       +Image Copy3:  {{ $imgCopy3.MediaType }}|{{ $imgCopy3.Width }}|{{ $imgCopy3.Height }}|
       +
       +{{/* Generic resources */}}
       +{{ $targetPath := "js/vars.js" }}
       +{{ $orig := "let foo;" | resources.FromString "js/foo.js" }}
       +{{ $copy1 := $orig | resources.Copy "js/copies/bar.js" }}
       +{{ $copy2 := $orig | resources.Copy "js/copies/baz.js" | fingerprint "md5" }}
       +{{ $copy3 := $copy2 | resources.Copy "js/copies/moo.js" | minify }}
       +
       +Orig: {{ $orig.RelPermalink}}|{{ $orig.MediaType }}|{{ $orig.Content | safeJS }}|
       +Copy1: {{ $copy1.RelPermalink}}|{{ $copy1.MediaType }}|{{ $copy1.Content | safeJS }}|
       +Copy2: {{ $copy2.RelPermalink}}|{{ $copy2.MediaType }}|{{ $copy2.Content | safeJS }}|
       +Copy3: {{ $copy3.RelPermalink}}|{{ $copy3.MediaType }}|{{ $copy3.Content | safeJS }}|
       +
       +        `
       +
       +        b := hugolib.NewIntegrationTestBuilder(
       +                hugolib.IntegrationTestConfig{
       +                        T:           t,
       +                        TxtarString: files,
       +                }).Build()
       +
       +        b.AssertFileContent("public/index.html", `
       +Image Orig:  /blog/images/pixel.png|image/png|1|1|
       +Image Copy1:  /blog/images/copy_hu8aa3346827e49d756ff4e630147c42b5_70_3x4_resize_box_3.png|image/png|3|4|
       +Image Copy2:  /blog/images/copy2.png|image/png|3|4
       +Image Copy3:  image/png|3|4|
       +Orig: /blog/js/foo.js|application/javascript|let foo;|
       +Copy1: /blog/js/copies/bar.js|application/javascript|let foo;|
       +Copy2: /blog/js/copies/baz.a677329fc6c4ad947e0c7116d91f37a2.js|application/javascript|let foo;|
       +Copy3: /blog/js/copies/moo.a677329fc6c4ad947e0c7116d91f37a2.min.js|application/javascript|let foo|
       +
       +                `)
       +
       +        b.AssertDestinationExists("images/copy2.png", true)
       +        // No permalink used.
       +        b.AssertDestinationExists("images/copy3.png", false)
       +
       +}
       +
       +func TestCopyPageShouldFail(t *testing.T) {
       +        t.Parallel()
       +
       +        files := `
       +-- config.toml --
       +-- layouts/index.html --
       +{{/* This is currently not supported. */}}
       +{{ $copy := .Copy "copy.md" }}
       +
       +        `
       +
       +        b, err := hugolib.NewIntegrationTestBuilder(
       +                hugolib.IntegrationTestConfig{
       +                        T:           t,
       +                        TxtarString: files,
       +                }).BuildE()
       +
       +        b.Assert(err, qt.IsNotNil)
       +
       +}
   DIR diff --git a/tpl/resources/resources.go b/tpl/resources/resources.go
       @@ -111,6 +111,15 @@ func (ns *Namespace) getscssClientDartSass() (*dartsass.Client, error) {
                return ns.scssClientDartSass, err
        }
        
       +// Copy copies r to the new targetPath in s.
       +func (ns *Namespace) Copy(s any, r resource.Resource) (resource.Resource, error) {
       +        targetPath, err := cast.ToStringE(s)
       +        if err != nil {
       +                panic(err)
       +        }
       +        return ns.createClient.Copy(r, targetPath)
       +}
       +
        // 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 any) resource.Resource {