]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Add resources.Copy
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Tue, 24 May 2022 07:34:36 +0000 (09:34 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 25 May 2022 08:35:31 +0000 (10:35 +0200)
Implemented by most Resource objects, but not Page (for now).

Fixes #9313

helpers/general.go
resources/image.go
resources/resource.go
resources/resource/resourcetypes.go
resources/resource_factories/create/create.go
resources/transform.go
tpl/resources/integration_test.go [new file with mode: 0644]
tpl/resources/resources.go

index e31bbfc9daf9a96f4fb48a2c15b0c8766651e832..462ec773da070b53a9310612f3ba4877b32c094c 100644 (file)
@@ -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
        })
 }
index f431784b4a7a531f17d9652ceaf21f2580cd729a..3eda709d9b1e0d5f33b3b1b120a9c7d339b96f7d 100644 (file)
@@ -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 {
index ead483d65a189034c8b50ed873bcc54741c81beb..e38090dbe738aff8f35e597c411f159721fa5e34 100644 (file)
@@ -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
index e3251aabe2234e18fef89741bea1cfff7fa39bfd..4ba95c170534df11cc382f5b9bdaea91a205a1db 100644 (file)
@@ -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
 }
index 3827411a9f96414e2e90331d8910c626c6c39cea..075d257362bd6eeaa39dc3115581fc0e6f833c81 100644 (file)
@@ -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)
index a470c94daf4421c6d1bb08fa8fab70f9c5bf959c..bb1608cbd54ef35ebabf26f84296dbbdd094fde5 100644 (file)
@@ -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 {
diff --git a/tpl/resources/integration_test.go b/tpl/resources/integration_test.go
new file mode 100644 (file)
index 0000000..06f98ee
--- /dev/null
@@ -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)
+
+}
index 2adec358ce81b36c5dba29bce4f858ae95a783ba..165152c78a97e2590c525129b336c13c8d9567b2 100644 (file)
@@ -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 {