Fix concat with fingerprint regression

In Hugo 0.58 we optimized the transformers that only adjusted metadata, e.g. the fingerprint.

This depended on the source readers implementing `io.ReadSeeker`.

The reader produced by `concat` did that, but the implementation was buggy.

This commit fixes that.

Fixes #6309
This commit is contained in:
Bjørn Erik Pedersen 2019-09-05 18:03:00 +02:00
parent 5e66094775
commit 3be2c25351
3 changed files with 79 additions and 13 deletions

View file

@ -284,7 +284,7 @@ Edited content.
} }
} }
func TestResourceChain(t *testing.T) { func TestResourceChains(t *testing.T) {
t.Parallel() t.Parallel()
c := qt.New(t) c := qt.New(t)
@ -389,6 +389,23 @@ T3: Content: {{ $combinedJs.Content }}|{{ $combinedJs.RelPermalink }}
; ;
(function F {})()`) (function F {})()`)
}}, }},
{"concat and fingerprint", func() bool { return true }, func(b *sitesBuilder) {
b.WithTemplates("home.html", `
{{ $a := "A" | resources.FromString "a.txt"}}
{{ $b := "B" | resources.FromString "b.txt"}}
{{ $c := "C" | resources.FromString "c.txt"}}
{{ $combined := slice $a $b $c | resources.Concat "bundle/concat.txt" }}
{{ $fingerprinted := $combined | fingerprint }}
Fingerprinted: {{ $fingerprinted.RelPermalink }}
`)
}, func(b *sitesBuilder) {
b.AssertFileContent("public/index.html", "Fingerprinted: /bundle/concat.b5d4045c3f466fa91fe2cc6abe79232a1a57cdf104f7a26e716e0a1e2789df78.txt")
b.AssertFileContent("public/bundle/concat.b5d4045c3f466fa91fe2cc6abe79232a1a57cdf104f7a26e716e0a1e2789df78.txt", "ABC")
}},
{"fromstring", func() bool { return true }, func(b *sitesBuilder) { {"fromstring", func() bool { return true }, func(b *sitesBuilder) {
b.WithTemplates("home.html", ` b.WithTemplates("home.html", `
{{ $r := "Hugo Rocks!" | resources.FromString "rocks/hugo.txt" }} {{ $r := "Hugo Rocks!" | resources.FromString "rocks/hugo.txt" }}

View file

@ -15,7 +15,6 @@
package bundler package bundler
import ( import (
"bytes"
"fmt" "fmt"
"io" "io"
"path" "path"
@ -43,6 +42,19 @@ type multiReadSeekCloser struct {
sources []hugio.ReadSeekCloser sources []hugio.ReadSeekCloser
} }
func toReaders(sources []hugio.ReadSeekCloser) []io.Reader {
readers := make([]io.Reader, len(sources))
for i, r := range sources {
readers[i] = r
}
return readers
}
func newMultiReadSeekCloser(sources ...hugio.ReadSeekCloser) *multiReadSeekCloser {
mr := io.MultiReader(toReaders(sources)...)
return &multiReadSeekCloser{mr, sources}
}
func (r *multiReadSeekCloser) Read(p []byte) (n int, err error) { func (r *multiReadSeekCloser) Read(p []byte) (n int, err error) {
return r.mr.Read(p) return r.mr.Read(p)
} }
@ -54,6 +66,9 @@ func (r *multiReadSeekCloser) Seek(offset int64, whence int) (newOffset int64, e
return return
} }
} }
r.mr = io.MultiReader(toReaders(r.sources)...)
return return
} }
@ -98,31 +113,24 @@ func (c *Client) Concat(targetPath string, r resource.Resources) (resource.Resou
rcsources = append(rcsources, rc) rcsources = append(rcsources, rc)
} }
var readers []io.Reader
// Arbitrary JavaScript files require a barrier between them to be safely concatenated together. // Arbitrary JavaScript files require a barrier between them to be safely concatenated together.
// Without this, the last line of one file can affect the first line of the next file and change how both files are interpreted. // Without this, the last line of one file can affect the first line of the next file and change how both files are interpreted.
if resolvedm.MainType == media.JavascriptType.MainType && resolvedm.SubType == media.JavascriptType.SubType { if resolvedm.MainType == media.JavascriptType.MainType && resolvedm.SubType == media.JavascriptType.SubType {
readers = make([]io.Reader, 2*len(rcsources)-1) readers := make([]hugio.ReadSeekCloser, 2*len(rcsources)-1)
j := 0 j := 0
for i := 0; i < len(rcsources); i++ { for i := 0; i < len(rcsources); i++ {
if i > 0 { if i > 0 {
readers[j] = bytes.NewBufferString("\n;\n") readers[j] = hugio.NewReadSeekerNoOpCloserFromString("\n;\n")
j++ j++
} }
readers[j] = rcsources[i] readers[j] = rcsources[i]
j++ j++
} }
} else { return newMultiReadSeekCloser(readers...), nil
readers = make([]io.Reader, len(rcsources))
for i := 0; i < len(rcsources); i++ {
readers[i] = rcsources[i]
}
} }
mr := io.MultiReader(readers...) return newMultiReadSeekCloser(rcsources...), nil
return &multiReadSeekCloser{mr: mr, sources: rcsources}, nil
} }
composite, err := c.rs.New( composite, err := c.rs.New(

View file

@ -0,0 +1,41 @@
// Copyright 2019 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 bundler
import (
"testing"
"github.com/gohugoio/hugo/helpers"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/common/hugio"
)
func TestMultiReadSeekCloser(t *testing.T) {
c := qt.New(t)
rc := newMultiReadSeekCloser(
hugio.NewReadSeekerNoOpCloserFromString("A"),
hugio.NewReadSeekerNoOpCloserFromString("B"),
hugio.NewReadSeekerNoOpCloserFromString("C"),
)
for i := 0; i < 3; i++ {
s1 := helpers.ReaderToString(rc)
c.Assert(s1, qt.Equals, "ABC")
_, err := rc.Seek(0, 0)
c.Assert(err, qt.IsNil)
}
}