diff --git a/hugolib/resource_chain_test.go b/hugolib/resource_chain_test.go index 0335339a3c8..66a0a7ce6b5 100644 --- a/hugolib/resource_chain_test.go +++ b/hugolib/resource_chain_test.go @@ -168,6 +168,8 @@ T1: {{ $r.Content }} func TestResourceChain(t *testing.T) { t.Parallel() + assert := require.New(t) + tests := []struct { name string shouldRun func() bool @@ -199,7 +201,7 @@ T6: {{ $bundle1.Permalink }} 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}`) }}, @@ -313,6 +315,30 @@ T1: {{ $r1.Permalink }}|{{ $r1.RelPermalink }} }}, + // 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) { }}, } diff --git a/resource/transform.go b/resource/transform.go index 0b5772dde3b..796c7ee2330 100644 --- a/resource/transform.go +++ b/resource/transform.go @@ -183,6 +183,11 @@ type transformedResource struct { 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) tryTransformedFileCache(key string) io.ReadCloser } 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,14 +235,14 @@ func (r *transformedResource) Content() (interface{}, error) { } 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 } 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,14 +250,14 @@ func (r *transformedResource) MediaType() media.Type { } 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) } 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 @@ func (r *transformedResource) initContent() error { 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 @@ func (r *transformedResource) transform(setContent bool) (err error) { tctx := &ResourceTransformationCtx{ Data: r.transformedResourceMetadata.MetaData, - OpenResourcePublisher: openPublishFileForWriting, + OpenResourcePublisher: r.openPublishFileForWriting, } tctx.InMediaType = first.MediaType() @@ -426,14 +431,18 @@ func (r *transformedResource) transform(setContent bool) (err error) { r.MediaTypeV = tctx.OutMediaType.Type() } - publicw, err := openPublishFileForWriting(r.Target) - if err != nil { - r.transformErr = err - return - } - defer publicw.Close() + var publishwriters []io.WriteCloser + + if publish { + publicw, err := r.openPublishFileForWriting(r.Target) + if err != nil { + r.transformErr = err + return err + } + defer publicw.Close() - publishwriters := []io.WriteCloser{publicw} + publishwriters = append(publishwriters, publicw) + } if transformedContentr == nil { // Also write it to the cache @@ -474,13 +483,49 @@ func (r *transformedResource) transform(setContent bool) (err error) { 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 }