]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
Make build.writeStats a struct
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 1 Jul 2023 08:37:38 +0000 (10:37 +0200)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Sat, 1 Jul 2023 13:38:32 +0000 (15:38 +0200)
So you can do

```toml
[build.writeStats]
  tags = true
  classes = true
  ids = false
```

Fixes #11191

config/commonConfig.go
hugolib/hugo_sites_build.go
hugolib/integrationtest_builder.go
hugolib/site_test.go
publisher/htmlElementsCollector.go
publisher/htmlElementsCollector_test.go
publisher/publisher.go

index 09f81c1ba051cad1c1792fdd9048fe61f8f7b982..0374994654e7e7fda99496cdf818b39e983ae334 100644 (file)
@@ -82,7 +82,7 @@ type LoadConfigResult struct {
 
 var defaultBuild = BuildConfig{
        UseResourceCacheWhen: "fallback",
-       WriteStats:           false,
+       WriteStats:           WriteStats{},
 
        CacheBusters: []CacheBuster{
                {
@@ -111,7 +111,8 @@ type BuildConfig struct {
 
        // When enabled, will collect and write a hugo_stats.json with some build
        // related aggregated data (e.g. CSS class names).
-       WriteStats bool
+       // Note that this was a bool <= v0.115.0.
+       WriteStats WriteStats
 
        // Can be used to toggle off writing of the IntelliSense /assets/jsconfig.js
        // file.
@@ -121,6 +122,17 @@ type BuildConfig struct {
        CacheBusters []CacheBuster
 }
 
+// WriteStats configures what to write to the hugo_stats.json file.
+type WriteStats struct {
+       Tags    bool
+       Classes bool
+       IDs     bool
+}
+
+func (w WriteStats) Enabled() bool {
+       return w.Tags || w.Classes || w.IDs
+}
+
 func (b BuildConfig) clone() BuildConfig {
        b.CacheBusters = append([]CacheBuster{}, b.CacheBusters...)
        return b
@@ -171,14 +183,26 @@ func (b *BuildConfig) CompileConfig(logger loggers.Logger) error {
 
 func DecodeBuildConfig(cfg Provider) BuildConfig {
        m := cfg.GetStringMap("build")
+
        b := defaultBuild.clone()
        if m == nil {
                return b
        }
 
+       // writeStats was a bool <= v0.115.0.
+       if writeStats, ok := m["writestats"]; ok {
+               if bb, ok := writeStats.(bool); ok {
+                       m["writestats"] = WriteStats{
+                               Tags:    bb,
+                               Classes: bb,
+                               IDs:     bb,
+                       }
+               }
+       }
+
        err := mapstructure.WeakDecode(m, &b)
        if err != nil {
-               return defaultBuild
+               return b
        }
 
        b.UseResourceCacheWhen = strings.ToLower(b.UseResourceCacheWhen)
index 9a645f3a56f8a9d4057e4c2b445334e4faf38322..2c8ca0aae903c182eccdbc6f138c7a40263eb892 100644 (file)
@@ -475,7 +475,7 @@ func (h *HugoSites) writeBuildStats() error {
        if h.ResourceSpec == nil {
                panic("h.ResourceSpec is nil")
        }
-       if !h.ResourceSpec.BuildConfig().WriteStats {
+       if !h.ResourceSpec.BuildConfig().WriteStats.Enabled() {
                return nil
        }
 
index 3910e2b978b49913af240b94dd3564eb06c46f0d..ada01b6eefc67010fe2a43b43f58e93638132392 100644 (file)
@@ -147,6 +147,15 @@ func (s *IntegrationTestBuilder) AssertFileContent(filename string, matches ...s
                        if match == "" || strings.HasPrefix(match, "#") {
                                continue
                        }
+                       var negate bool
+                       if strings.HasPrefix(match, "! ") {
+                               negate = true
+                               match = strings.TrimPrefix(match, "! ")
+                       }
+                       if negate {
+                               s.Assert(content, qt.Not(qt.Contains), match, qt.Commentf(m))
+                               continue
+                       }
                        s.Assert(content, qt.Contains, match, qt.Commentf(m))
                }
        }
index be59b17a7f23ff8a9645b34ad63d6b7d86eb7c1c..8265c06d07389e2033b5c56c38d0a169bb039a02 100644 (file)
@@ -1162,6 +1162,89 @@ Some text.
        }
 }
 
+func TestClassCollectorConfigWriteStats(t *testing.T) {
+       r := func(writeStatsConfig string) *IntegrationTestBuilder {
+               files := `
+-- hugo.toml --
+WRITE_STATS_CONFIG
+-- layouts/_default/list.html --
+<div id="myid" class="myclass">Foo</div>
+
+`
+               files = strings.Replace(files, "WRITE_STATS_CONFIG", writeStatsConfig, 1)
+
+               b := NewIntegrationTestBuilder(
+                       IntegrationTestConfig{
+                               T:           t,
+                               TxtarString: files,
+                               NeedsOsFS:   true,
+                       },
+               ).Build()
+
+               return b
+       }
+
+       // Legacy config.
+       b := r(`
+[build]
+writeStats = true
+`)
+
+       b.AssertFileContent("hugo_stats.json", "myclass", "div", "myid")
+
+       b = r(`
+[build]
+writeStats = false
+       `)
+
+       b.AssertDestinationExists("hugo_stats.json", false)
+
+       b = r(`
+[build.writeStats]
+tags = true
+classes = true
+ids = true
+       `)
+
+       b.AssertFileContent("hugo_stats.json", "myclass", "div", "myid")
+
+       b = r(`
+[build.writeStats]
+tags = true
+classes = true
+ids = false
+`)
+
+       b.AssertFileContent("hugo_stats.json", "myclass", "div", "! myid")
+
+       b = r(`
+[build.writeStats]
+tags = true
+classes = false
+ids = true
+`)
+
+       b.AssertFileContent("hugo_stats.json", "! myclass", "div", "myid")
+
+       b = r(`
+[build.writeStats]
+tags = false
+classes = true
+ids = true
+       `)
+
+       b.AssertFileContent("hugo_stats.json", "myclass", "! div", "myid")
+
+       b = r(`
+[build.writeStats]
+tags = false
+classes = false
+ids = false
+       `)
+       b.AssertDestinationExists("hugo_stats.json", false)
+
+}
+
 func TestClassCollectorStress(t *testing.T) {
        statsFilename := "hugo_stats.json"
        defer os.Remove(statsFilename)
index c3b88c4cc141b95b1fca8e9d34722fabb97db6b0..0805023528ad229f351c94a220cb0ae9525148f5 100644 (file)
@@ -24,6 +24,7 @@ import (
 
        "golang.org/x/net/html"
 
+       "github.com/gohugoio/hugo/config"
        "github.com/gohugoio/hugo/helpers"
 )
 
@@ -46,8 +47,9 @@ var (
        }
 )
 
-func newHTMLElementsCollector() *htmlElementsCollector {
+func newHTMLElementsCollector(conf config.WriteStats) *htmlElementsCollector {
        return &htmlElementsCollector{
+               conf:       conf,
                elementSet: make(map[string]bool),
        }
 }
@@ -93,6 +95,8 @@ type htmlElement struct {
 }
 
 type htmlElementsCollector struct {
+       conf config.WriteStats
+
        // Contains the raw HTML string. We will get the same element
        // several times, and want to avoid costly reparsing when this
        // is used for aggregated data only.
@@ -113,7 +117,9 @@ func (c *htmlElementsCollector) getHTMLElements() HTMLElements {
        for _, el := range c.elements {
                classes = append(classes, el.Classes...)
                ids = append(ids, el.IDs...)
-               tags = append(tags, el.Tag)
+               if c.conf.Tags {
+                       tags = append(tags, el.Tag)
+               }
        }
 
        classes = helpers.UniqueStringsSorted(classes)
@@ -246,7 +252,7 @@ func (w *htmlElementsCollectorWriter) lexElementInside(resolve htmlCollectorStat
                        }
 
                        // Parse each collected element.
-                       el, err := parseHTMLElement(s)
+                       el, err := w.parseHTMLElement(s)
                        if err != nil {
                                w.err = err
                                return resolve
@@ -363,7 +369,13 @@ func htmlLexToEndOfComment(w *htmlElementsCollectorWriter) htmlCollectorStateFun
        return htmlLexToEndOfComment
 }
 
-func parseHTMLElement(elStr string) (el htmlElement, err error) {
+func (w *htmlElementsCollectorWriter) parseHTMLElement(elStr string) (el htmlElement, err error) {
+       conf := w.collector.conf
+
+       if !conf.IDs && !conf.Classes {
+               // Nothing to do.
+               return
+       }
 
        tagName := parseStartTag(elStr)
 
@@ -390,8 +402,13 @@ func parseHTMLElement(elStr string) (el htmlElement, err error) {
                                switch {
                                case strings.EqualFold(a.Key, "id"):
                                        // There should be only one, but one never knows...
-                                       el.IDs = append(el.IDs, a.Val)
+                                       if conf.IDs {
+                                               el.IDs = append(el.IDs, a.Val)
+                                       }
                                default:
+                                       if !conf.Classes {
+                                               continue
+                                       }
                                        if classAttrRe.MatchString(a.Key) {
                                                el.Classes = append(el.Classes, strings.Fields(a.Val)...)
                                        } else {
index 7aeda0dafb030c7bc84e0254d956874f7f225a20..51b34a3d629023b4166912c585213c55d6efce04 100644 (file)
@@ -22,6 +22,7 @@ import (
        "testing"
        "time"
 
+       "github.com/gohugoio/hugo/config"
        "github.com/gohugoio/hugo/config/testconfig"
        "github.com/gohugoio/hugo/media"
        "github.com/gohugoio/hugo/minifiers"
@@ -136,7 +137,13 @@ func TestClassCollector(t *testing.T) {
                } {
 
                        c.Run(fmt.Sprintf("%s--minify-%t", test.name, variant.minify), func(c *qt.C) {
-                               w := newHTMLElementsCollectorWriter(newHTMLElementsCollector())
+                               w := newHTMLElementsCollectorWriter(newHTMLElementsCollector(
+                                       config.WriteStats{
+                                               Tags:    true,
+                                               Classes: true,
+                                               IDs:     true,
+                                       },
+                               ))
                                if variant.minify {
                                        if skipMinifyTest[test.name] {
                                                c.Skip("skip minify test")
@@ -240,7 +247,13 @@ func BenchmarkElementsCollectorWriter(b *testing.B) {
 </html>
 `
        for i := 0; i < b.N; i++ {
-               w := newHTMLElementsCollectorWriter(newHTMLElementsCollector())
+               w := newHTMLElementsCollectorWriter(newHTMLElementsCollector(
+                       config.WriteStats{
+                               Tags:    true,
+                               Classes: true,
+                               IDs:     true,
+                       },
+               ))
                fmt.Fprint(w, benchHTML)
 
        }
@@ -262,7 +275,13 @@ func BenchmarkElementsCollectorWriterPre(b *testing.B) {
 <div class="foo"></div>
 
 `
-       w := newHTMLElementsCollectorWriter(newHTMLElementsCollector())
+       w := newHTMLElementsCollectorWriter(newHTMLElementsCollector(
+               config.WriteStats{
+                       Tags:    true,
+                       Classes: true,
+                       IDs:     true,
+               },
+       ))
        for i := 0; i < b.N; i++ {
                fmt.Fprint(w, benchHTML)
 
index 970c93e6c88916b270498177a146f501e69755aa..37d8b36e40833bf07fd1b274e65d30cd79f050c3 100644 (file)
@@ -81,8 +81,8 @@ func NewDestinationPublisher(rs *resources.Spec, outputFormats output.Formats, m
        fs := rs.BaseFs.PublishFs
        cfg := rs.Cfg
        var classCollector *htmlElementsCollector
-       if rs.BuildConfig().WriteStats {
-               classCollector = newHTMLElementsCollector()
+       if rs.BuildConfig().WriteStats.Enabled() {
+               classCollector = newHTMLElementsCollector(rs.BuildConfig().WriteStats)
        }
        pub = DestinationPublisher{fs: fs, htmlElementsCollector: classCollector}
        pub.min, err = minifiers.New(mediaTypes, outputFormats, cfg)