]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Support PostProcess for all file types
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 14 Sep 2022 09:58:45 +0000 (11:58 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Wed, 14 Sep 2022 17:09:20 +0000 (19:09 +0200)
Not just HTML.

Fixes #10269

commands/hugo.go
common/hugio/hasBytesWriter.go [new file with mode: 0644]
common/hugio/hasBytesWriter_test.go [new file with mode: 0644]
deps/deps.go
hugofs/hasbytes_fs.go [new file with mode: 0644]
hugolib/hugo_sites_build.go
hugolib/resource_chain_test.go
hugolib/testhelpers_test.go
resources/transform_test.go

index 5169d65a52ef14dc25c2a5bb9b9d8986ed4f7c7e..d127d37212f259531dbe39e5a55bf2dfe1b0429e 100644 (file)
@@ -511,12 +511,15 @@ func (c *commandeer) build() error {
                c.hugo().PrintProcessingStats(os.Stdout)
                fmt.Println()
 
-               if createCounter, ok := c.publishDirFs.(hugofs.DuplicatesReporter); ok {
-                       dupes := createCounter.ReportDuplicates()
-                       if dupes != "" {
-                               c.logger.Warnln("Duplicate target paths:", dupes)
+               hugofs.WalkFilesystems(c.publishDirFs, func(fs afero.Fs) bool {
+                       if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
+                               dupes := dfs.ReportDuplicates()
+                               if dupes != "" {
+                                       c.logger.Warnln("Duplicate target paths:", dupes)
+                               }
                        }
-               }
+                       return false
+               })
 
                unusedTemplates := c.hugo().Tmpl().(tpl.UnusedTemplatesProvider).UnusedTemplates()
                for _, unusedTemplate := range unusedTemplates {
diff --git a/common/hugio/hasBytesWriter.go b/common/hugio/hasBytesWriter.go
new file mode 100644 (file)
index 0000000..7b7d7a5
--- /dev/null
@@ -0,0 +1,57 @@
+// 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.
+// 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 hugio
+
+import (
+       "bytes"
+)
+
+// HasBytesWriter is a writer that will set Match to true if the given pattern
+// is found in the stream.
+type HasBytesWriter struct {
+       Match   bool
+       Pattern []byte
+
+       i    int
+       done bool
+       buff []byte
+}
+
+func (h *HasBytesWriter) Write(p []byte) (n int, err error) {
+       if h.done {
+               return len(p), nil
+       }
+
+       if len(h.buff) == 0 {
+               h.buff = make([]byte, len(h.Pattern)*2)
+       }
+
+       for i := range p {
+               h.buff[h.i] = p[i]
+               h.i++
+               if h.i == len(h.buff) {
+                       // Shift left.
+                       copy(h.buff, h.buff[len(h.buff)/2:])
+                       h.i = len(h.buff) / 2
+               }
+
+               if bytes.Contains(h.buff, h.Pattern) {
+                       h.Match = true
+                       h.done = true
+                       return len(p), nil
+               }
+       }
+
+       return len(p), nil
+}
diff --git a/common/hugio/hasBytesWriter_test.go b/common/hugio/hasBytesWriter_test.go
new file mode 100644 (file)
index 0000000..b1b8011
--- /dev/null
@@ -0,0 +1,64 @@
+// 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.
+// 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 hugio
+
+import (
+       "bytes"
+       "fmt"
+       "io"
+       "math/rand"
+       "strings"
+       "testing"
+       "time"
+
+       qt "github.com/frankban/quicktest"
+)
+
+func TestHasBytesWriter(t *testing.T) {
+       r := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+       c := qt.New((t))
+
+       neww := func() (*HasBytesWriter, io.Writer) {
+               var b bytes.Buffer
+
+               h := &HasBytesWriter{
+                       Pattern: []byte("__foo"),
+               }
+               return h, io.MultiWriter(&b, h)
+       }
+
+       rndStr := func() string {
+               return strings.Repeat("ab cfo", r.Intn(33))
+       }
+
+       for i := 0; i < 22; i++ {
+               h, w := neww()
+               fmt.Fprintf(w, rndStr()+"abc __foobar"+rndStr())
+               c.Assert(h.Match, qt.Equals, true)
+
+               h, w = neww()
+               fmt.Fprintf(w, rndStr()+"abc __f")
+               fmt.Fprintf(w, "oo bar"+rndStr())
+               c.Assert(h.Match, qt.Equals, true)
+
+               h, w = neww()
+               fmt.Fprintf(w, rndStr()+"abc __moo bar")
+               c.Assert(h.Match, qt.Equals, false)
+       }
+
+       h, w := neww()
+       fmt.Fprintf(w, "__foo")
+       c.Assert(h.Match, qt.Equals, true)
+}
index ece4203024f9af128a6c882d65bba92a65f0b0fb..e1cbfce069e35cc47a5d9c0df09928bc3406ae10 100644 (file)
@@ -16,6 +16,7 @@ import (
        "github.com/gohugoio/hugo/langs"
        "github.com/gohugoio/hugo/media"
        "github.com/gohugoio/hugo/resources/page"
+       "github.com/gohugoio/hugo/resources/postpub"
 
        "github.com/gohugoio/hugo/metrics"
        "github.com/gohugoio/hugo/output"
@@ -78,6 +79,10 @@ type Deps struct {
        // All the output formats available for the current site.
        OutputFormatsConfig output.Formats
 
+       // FilenameHasPostProcessPrefix is a set of filenames in /public that
+       // contains a post-processing prefix.
+       FilenameHasPostProcessPrefix []string
+
        templateProvider ResourceProvider
        WithTemplate     func(templ tpl.TemplateManager) error `json:"-"`
 
@@ -202,6 +207,7 @@ func New(cfg DepsCfg) (*Deps, error) {
        var (
                logger = cfg.Logger
                fs     = cfg.Fs
+               d      *Deps
        )
 
        if cfg.TemplateProvider == nil {
@@ -239,6 +245,18 @@ func New(cfg DepsCfg) (*Deps, error) {
        }
        execHelper := hexec.New(securityConfig)
 
+       var filenameHasPostProcessPrefixMu sync.Mutex
+       cb := func(name string, match bool) {
+               if !match {
+                       return
+               }
+               filenameHasPostProcessPrefixMu.Lock()
+               d.FilenameHasPostProcessPrefix = append(d.FilenameHasPostProcessPrefix, name)
+               filenameHasPostProcessPrefixMu.Unlock()
+
+       }
+       fs.PublishDir = hugofs.NewHasBytesReceiver(fs.PublishDir, cb, []byte(postpub.PostProcessPrefix))
+
        ps, err := helpers.NewPathSpec(fs, cfg.Language, logger)
        if err != nil {
                return nil, fmt.Errorf("create PathSpec: %w", err)
@@ -274,7 +292,7 @@ func New(cfg DepsCfg) (*Deps, error) {
 
        logDistinct := helpers.NewDistinctLogger(logger)
 
-       d := &Deps{
+       d = &Deps{
                Fs:                      fs,
                Log:                     ignorableLogger,
                LogDistinct:             logDistinct,
diff --git a/hugofs/hasbytes_fs.go b/hugofs/hasbytes_fs.go
new file mode 100644 (file)
index 0000000..b5f8287
--- /dev/null
@@ -0,0 +1,90 @@
+// 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.
+// 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 hugofs
+
+import (
+       "os"
+
+       "github.com/gohugoio/hugo/common/hugio"
+       "github.com/spf13/afero"
+)
+
+var (
+       _ afero.Fs            = (*hasBytesFs)(nil)
+       _ FilesystemUnwrapper = (*hasBytesFs)(nil)
+)
+
+type hasBytesFs struct {
+       afero.Fs
+       hasBytesCallback func(name string, match bool)
+       pattern          []byte
+}
+
+func NewHasBytesReceiver(delegate afero.Fs, hasBytesCallback func(name string, match bool), pattern []byte) afero.Fs {
+       return &hasBytesFs{Fs: delegate, hasBytesCallback: hasBytesCallback, pattern: pattern}
+}
+
+func (fs *hasBytesFs) UnwrapFilesystem() afero.Fs {
+       return fs.Fs
+}
+
+func (fs *hasBytesFs) Create(name string) (afero.File, error) {
+       f, err := fs.Fs.Create(name)
+       if err == nil {
+               f = fs.wrapFile(f)
+       }
+       return f, err
+}
+
+func (fs *hasBytesFs) OpenFile(name string, flag int, perm os.FileMode) (afero.File, error) {
+       f, err := fs.Fs.OpenFile(name, flag, perm)
+       if err == nil && isWrite(flag) {
+               f = fs.wrapFile(f)
+       }
+       return f, err
+}
+
+func (fs *hasBytesFs) wrapFile(f afero.File) afero.File {
+       return &hasBytesFile{
+               File: f,
+               hbw: &hugio.HasBytesWriter{
+                       Pattern: fs.pattern,
+               },
+               hasBytesCallback: fs.hasBytesCallback,
+       }
+
+}
+
+func (fs *hasBytesFs) Name() string {
+       return "hasBytesFs"
+}
+
+type hasBytesFile struct {
+       hasBytesCallback func(name string, match bool)
+       hbw              *hugio.HasBytesWriter
+       afero.File
+}
+
+func (h *hasBytesFile) Write(p []byte) (n int, err error) {
+       n, err = h.File.Write(p)
+       if err != nil {
+               return
+       }
+       return h.hbw.Write(p)
+}
+
+func (h *hasBytesFile) Close() error {
+       h.hasBytesCallback(h.Name(), h.hbw.Match)
+       return h.File.Close()
+}
index 1a191257c18295064db64af7e99981c92a6e32c1..3bebc5284e1f763160493d139eb3e7536cad78e7 100644 (file)
@@ -18,7 +18,6 @@ import (
        "context"
        "encoding/json"
        "fmt"
-       "os"
        "path/filepath"
        "runtime/trace"
        "strings"
@@ -439,23 +438,15 @@ func (h *HugoSites) postProcess() error {
                return nil
        }
 
-       _ = afero.Walk(h.BaseFs.PublishFs, "", func(path string, info os.FileInfo, err error) error {
-               if info == nil || info.IsDir() {
-                       return nil
-               }
-
-               if !strings.HasSuffix(path, "html") {
-                       return nil
-               }
-
+       for _, filename := range h.Deps.FilenameHasPostProcessPrefix {
+               filename := filename
                g.Run(func() error {
-                       return handleFile(path)
+                       return handleFile(filename)
                })
-
-               return nil
-       })
+       }
 
        // Prepare for a new build.
+       h.Deps.FilenameHasPostProcessPrefix = nil
        for _, s := range h.Sites {
                s.ResourceSpec.PostProcessResources = make(map[string]postpub.PostPublishedResource)
        }
index d94d389a75a780001f2ad8b0a55d14bc298c338a..4edc2cb31a3f94e211886c5243c1d3e39d65a4da 100644 (file)
@@ -168,6 +168,11 @@ HELLO: {{ $hello.RelPermalink }}
 HELLO: {{ $hello.RelPermalink }}|Integrity: {{ $hello.Data.Integrity }}|MediaType: {{ $hello.MediaType.Type }}
 HELLO2: Name: {{ $hello.Name }}|Content: {{ $hello.Content }}|Title: {{ $hello.Title }}|ResourceType: {{ $hello.ResourceType }}
 
+// Issue #10269
+{{ $m := dict "relPermalink"  $hello.RelPermalink "integrity" $hello.Data.Integrity "mediaType" $hello.MediaType.Type }}
+{{ $json := jsonify (dict "indent" "  ") $m | resources.FromString "hello.json" -}}
+JSON: {{ $json.RelPermalink }}
+
 // Issue #8884
 <a href="hugo.rocks">foo</a>
 <a href="{{ $hello.RelPermalink }}" integrity="{{ $hello.Data.Integrity}}">Hello</a>
@@ -188,6 +193,11 @@ End.`)
 
        b.AssertFileContent("public/page1/index.html", `HELLO: /hello.min.a2d1cb24f24b322a7dad520414c523e9.html`)
        b.AssertFileContent("public/page2/index.html", `HELLO: /hello.min.a2d1cb24f24b322a7dad520414c523e9.html`)
+       b.AssertFileContent("public/hello.json", `
+integrity": "md5-otHLJPJLMip9rVIEFMUj6Q==
+mediaType": "text/html
+relPermalink": "/hello.min.a2d1cb24f24b322a7dad520414c523e9.html"
+`)
 }
 
 func BenchmarkResourceChainPostProcess(b *testing.B) {
index 46fa35debffefe09c6b8443bbc8b1614b978d1c8..ca74e9340e29308640ef1502aede11ea36f7e6b3 100644 (file)
@@ -764,8 +764,12 @@ func (s *sitesBuilder) AssertImage(width, height int, filename string) {
 
 func (s *sitesBuilder) AssertNoDuplicateWrites() {
        s.Helper()
-       d := s.Fs.PublishDir.(hugofs.DuplicatesReporter)
-       s.Assert(d.ReportDuplicates(), qt.Equals, "")
+       hugofs.WalkFilesystems(s.Fs.PublishDir, func(fs afero.Fs) bool {
+               if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
+                       s.Assert(dfs.ReportDuplicates(), qt.Equals, "")
+               }
+               return false
+       })
 }
 
 func (s *sitesBuilder) FileContent(filename string) string {
index af8ccbc1fe1d5b0e05ed3c141f8ab6dadd65b975..1bd8302d29b30b36f4968ef5e203b24b6cfeb942 100644 (file)
@@ -71,8 +71,12 @@ func TestTransform(t *testing.T) {
        // Verify that we publish the same file once only.
        assertNoDuplicateWrites := func(c *qt.C, spec *Spec) {
                c.Helper()
-               d := spec.Fs.PublishDir.(hugofs.DuplicatesReporter)
-               c.Assert(d.ReportDuplicates(), qt.Equals, "")
+               hugofs.WalkFilesystems(spec.Fs.PublishDir, func(fs afero.Fs) bool {
+                       if dfs, ok := fs.(hugofs.DuplicatesReporter); ok {
+                               c.Assert(dfs.ReportDuplicates(), qt.Equals, "")
+                       }
+                       return false
+               })
        }
 
        assertShouldExist := func(c *qt.C, spec *Spec, filename string, should bool) {