shithub: hugo

Download patch

ref: 43f9df0194d229805d80b13c9e38a7a0fec12cf4
parent: 1021714449a05ef85b2fdfaf65b354cbdee44f23
author: Bjørn Erik Pedersen <[email protected]>
date: Fri Dec 21 05:05:57 EST 2018

Prevent resource publishing for transformed inline resources

That is, if only `.Content` is accessed.

This means that, for a transformed resource to be published to `/public`, you need to access either `.RelPermalink` or `Permalink`.

Fixes #4944

--- a/hugolib/resource_chain_test.go
+++ b/hugolib/resource_chain_test.go
@@ -168,6 +168,8 @@
 func TestResourceChain(t *testing.T) {
 	t.Parallel()
 
+	assert := require.New(t)
+
 	tests := []struct {
 		name      string
 		shouldRun func() bool
@@ -199,7 +201,7 @@
 			b.AssertFileContent("public/index.html", `T5 RelPermalink: /sass/styles3.css|`)
 			b.AssertFileContent("public/index.html", `T6: http://example.com/styles/bundle1.css`)
 
-			b.AssertFileContent("public/styles/templ.min.css", `.home{color:blue}`)
+			assert.False(b.CheckExists("public/styles/templ.min.css"))
 			b.AssertFileContent("public/styles/bundle1.css", `.home{color:blue}body{color:#333}`)
 
 		}},
@@ -311,6 +313,30 @@
 		}, func(b *sitesBuilder) {
 			b.AssertFileContent("public/index.html", `T1: https://example.com/hugo/rocks/hugo.txt|/hugo/rocks/hugo.txt`)
 
+		}},
+
+		// https://github.com/gohugoio/hugo/issues/4944
+		{"Prevent resource publish on .Content only", func() bool { return true }, func(b *sitesBuilder) {
+			b.WithTemplates("home.html", `
+{{ $cssInline := "body { color: green; }" | resources.FromString "inline.css" | minify }}
+{{ $cssPublish1 := "body { color: blue; }" | resources.FromString "external1.css" | minify }}
+{{ $cssPublish2 := "body { color: orange; }" | resources.FromString "external2.css" | minify }}
+
+Inline: {{ $cssInline.Content }}
+Publish 1: {{ $cssPublish1.Content }} {{ $cssPublish1.RelPermalink }}
+Publish 2: {{ $cssPublish2.Permalink }}
+`)
+
+		}, func(b *sitesBuilder) {
+			b.AssertFileContent("public/index.html",
+				`Inline: body{color:green}`,
+				"Publish 1: body{color:blue} /external1.min.css",
+				"Publish 2: http://example.com/external2.min.css",
+			)
+			assert.True(b.CheckExists("public/external2.min.css"), "Referenced content should be copied to /public")
+			assert.True(b.CheckExists("public/external1.min.css"), "Referenced content should be copied to /public")
+
+			assert.False(b.CheckExists("public/inline.min.css"), "Inline content should not be copied to /public")
 		}},
 
 		{"template", func() bool { return true }, func(b *sitesBuilder) {}, func(b *sitesBuilder) {
--- a/resource/transform.go
+++ b/resource/transform.go
@@ -183,6 +183,11 @@
 	transformInit sync.Once
 	transformErr  error
 
+	// We delay publishing until either .RelPermalink or .Permalink
+	// is invoked.
+	publishInit sync.Once
+	published   bool
+
 	// The transformed values
 	content     string
 	contentInit sync.Once
@@ -220,7 +225,7 @@
 }
 
 func (r *transformedResource) Content() (interface{}, error) {
-	if err := r.initTransform(true); err != nil {
+	if err := r.initTransform(true, false); err != nil {
 		return nil, err
 	}
 	if err := r.initContent(); err != nil {
@@ -230,7 +235,7 @@
 }
 
 func (r *transformedResource) Data() interface{} {
-	if err := r.initTransform(false); err != nil {
+	if err := r.initTransform(false, false); err != nil {
 		return noData
 	}
 	return r.MetaData
@@ -237,7 +242,7 @@
 }
 
 func (r *transformedResource) MediaType() media.Type {
-	if err := r.initTransform(false); err != nil {
+	if err := r.initTransform(false, false); err != nil {
 		return media.Type{}
 	}
 	m, _ := r.cache.rs.MediaTypes.GetByType(r.MediaTypeV)
@@ -245,7 +250,7 @@
 }
 
 func (r *transformedResource) Permalink() string {
-	if err := r.initTransform(false); err != nil {
+	if err := r.initTransform(false, true); err != nil {
 		return ""
 	}
 	return r.linker.permalinkFor(r.Target)
@@ -252,7 +257,7 @@
 }
 
 func (r *transformedResource) RelPermalink() string {
-	if err := r.initTransform(false); err != nil {
+	if err := r.initTransform(false, true); err != nil {
 		return ""
 	}
 	return r.linker.relPermalinkFor(r.Target)
@@ -271,11 +276,11 @@
 	return err
 }
 
-func (r *transformedResource) transform(setContent bool) (err error) {
+func (r *transformedResource) openPublishFileForWriting(relTargetPath string) (io.WriteCloser, error) {
+	return helpers.OpenFilesForWriting(r.cache.rs.PublishFs, r.linker.relTargetPathsFor(relTargetPath)...)
+}
 
-	openPublishFileForWriting := func(relTargetPath string) (io.WriteCloser, error) {
-		return helpers.OpenFilesForWriting(r.cache.rs.PublishFs, r.linker.relTargetPathsFor(relTargetPath)...)
-	}
+func (r *transformedResource) transform(setContent, publish bool) (err error) {
 
 	// This can be the last resource in a chain.
 	// Rewind and create a processing chain.
@@ -345,7 +350,7 @@
 
 	tctx := &ResourceTransformationCtx{
 		Data:                  r.transformedResourceMetadata.MetaData,
-		OpenResourcePublisher: openPublishFileForWriting,
+		OpenResourcePublisher: r.openPublishFileForWriting,
 	}
 
 	tctx.InMediaType = first.MediaType()
@@ -426,15 +431,19 @@
 		r.MediaTypeV = tctx.OutMediaType.Type()
 	}
 
-	publicw, err := openPublishFileForWriting(r.Target)
-	if err != nil {
-		r.transformErr = err
-		return
-	}
-	defer publicw.Close()
+	var publishwriters []io.WriteCloser
 
-	publishwriters := []io.WriteCloser{publicw}
+	if publish {
+		publicw, err := r.openPublishFileForWriting(r.Target)
+		if err != nil {
+			r.transformErr = err
+			return err
+		}
+		defer publicw.Close()
 
+		publishwriters = append(publishwriters, publicw)
+	}
+
 	if transformedContentr == nil {
 		// Also write it to the cache
 		fi, metaw, err := r.cache.writeMeta(key, r.transformedResourceMetadata)
@@ -474,13 +483,49 @@
 	return nil
 
 }
-func (r *transformedResource) initTransform(setContent bool) error {
+func (r *transformedResource) initTransform(setContent, publish bool) error {
 	r.transformInit.Do(func() {
-		if err := r.transform(setContent); err != nil {
+		r.published = publish
+		if err := r.transform(setContent, publish); err != nil {
 			r.transformErr = err
 			r.cache.rs.Logger.ERROR.Println("error: failed to transform resource:", err)
 		}
+
 	})
+
+	if !publish {
+		return r.transformErr
+	}
+
+	r.publishInit.Do(func() {
+		if r.published {
+			return
+		}
+
+		r.published = true
+
+		// Copy the file from cache to /public
+		_, src, err := r.cache.fileCache.Get(r.sourceFilename)
+
+		if err == nil {
+			defer src.Close()
+
+			var dst io.WriteCloser
+			dst, err = r.openPublishFileForWriting(r.Target)
+			if err == nil {
+				defer dst.Close()
+				io.Copy(dst, src)
+			}
+		}
+
+		if err != nil {
+			r.transformErr = err
+			r.cache.rs.Logger.ERROR.Println("error: failed to publish resource:", err)
+			return
+		}
+
+	})
+
 	return r.transformErr
 }