mirror of
https://github.com/gohugoio/hugo.git
synced 2024-12-23 18:51:02 +00:00
Make build.writeStats a struct
So you can do ```toml [build.writeStats] tags = true classes = true ids = false ``` Fixes #11191
This commit is contained in:
parent
da98724bc8
commit
11ecea6106
7 changed files with 166 additions and 14 deletions
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue