diff --git a/config/commonConfig.go b/config/commonConfig.go index 09f81c1ba05..0374994654e 100644 --- a/config/commonConfig.go +++ b/config/commonConfig.go @@ -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) diff --git a/hugolib/hugo_sites_build.go b/hugolib/hugo_sites_build.go index 9a645f3a56f..2c8ca0aae90 100644 --- a/hugolib/hugo_sites_build.go +++ b/hugolib/hugo_sites_build.go @@ -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 } diff --git a/hugolib/integrationtest_builder.go b/hugolib/integrationtest_builder.go index 3910e2b978b..ada01b6eefc 100644 --- a/hugolib/integrationtest_builder.go +++ b/hugolib/integrationtest_builder.go @@ -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)) } } diff --git a/hugolib/site_test.go b/hugolib/site_test.go index be59b17a7f2..8265c06d073 100644 --- a/hugolib/site_test.go +++ b/hugolib/site_test.go @@ -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 -- +
Foo
+ +` + 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) diff --git a/publisher/htmlElementsCollector.go b/publisher/htmlElementsCollector.go index c3b88c4cc14..0805023528a 100644 --- a/publisher/htmlElementsCollector.go +++ b/publisher/htmlElementsCollector.go @@ -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 { diff --git a/publisher/htmlElementsCollector_test.go b/publisher/htmlElementsCollector_test.go index 7aeda0dafb0..51b34a3d629 100644 --- a/publisher/htmlElementsCollector_test.go +++ b/publisher/htmlElementsCollector_test.go @@ -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) { ` 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) {
` - 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) diff --git a/publisher/publisher.go b/publisher/publisher.go index 970c93e6c88..37d8b36e408 100644 --- a/publisher/publisher.go +++ b/publisher/publisher.go @@ -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)