Fix class collector when running with --minify

Also add a related stresstest.

Fixes #7161
This commit is contained in:
Bjørn Erik Pedersen 2020-04-21 12:57:45 +02:00
parent 27af5a339a
commit f37e77f2d3
3 changed files with 116 additions and 15 deletions

View file

@ -14,11 +14,16 @@
package hugolib package hugolib
import ( import (
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"os"
"path/filepath" "path/filepath"
"strings" "strings"
"testing" "testing"
"github.com/gohugoio/hugo/publisher"
"github.com/spf13/viper" "github.com/spf13/viper"
"github.com/markbates/inflect" "github.com/markbates/inflect"
@ -982,13 +987,22 @@ func TestRefIssues(t *testing.T) {
} }
func TestClassCollector(t *testing.T) { func TestClassCollector(t *testing.T) {
for _, minify := range []bool{false, true} {
t.Run(fmt.Sprintf("minify-%t", minify), func(t *testing.T) {
statsFilename := "hugo_stats.json"
defer os.Remove(statsFilename)
b := newTestSitesBuilder(t) b := newTestSitesBuilder(t)
b.WithConfigFile("toml", ` b.WithConfigFile("toml", fmt.Sprintf(`
minify = %t
[build] [build]
writeStats = true writeStats = true
`) `, minify))
b.WithTemplates("index.html", ` b.WithTemplates("index.html", `
@ -997,6 +1011,12 @@ func TestClassCollector(t *testing.T) {
Some text. Some text.
<div class="c d e" id="el2">Foo</div> <div class="c d e" id="el2">Foo</div>
<span class=z>FOO</span>
<a class="text-base hover:text-gradient inline-block px-3 pb-1 rounded lowercase" href="{{ .RelPermalink }}">{{ .Title }}</a>
`) `)
b.WithContent("p1.md", "") b.WithContent("p1.md", "")
@ -1007,14 +1027,24 @@ Some text.
{ {
"htmlElements": { "htmlElements": {
"tags": [ "tags": [
"div" "a",
"div",
"span"
], ],
"classes": [ "classes": [
"a", "a",
"b", "b",
"c", "c",
"d", "d",
"e" "e",
"hover:text-gradient",
"inline-block",
"lowercase",
"pb-1",
"px-3",
"rounded",
"text-base",
"z"
], ],
"ids": [ "ids": [
"el1", "el1",
@ -1023,4 +1053,78 @@ Some text.
} }
} }
`) `)
})
}
}
func TestClassCollectorStress(t *testing.T) {
statsFilename := "hugo_stats.json"
defer os.Remove(statsFilename)
b := newTestSitesBuilder(t)
b.WithConfigFile("toml", `
disableKinds = ["home", "section", "taxonomy", "taxonomyTerm" ]
[languages]
[languages.en]
[languages.nb]
[languages.no]
[languages.sv]
[build]
writeStats = true
`)
b.WithTemplates("_default/single.html", `
<div class="c d e" id="el2">Foo</div>
Some text.
{{ $n := index (shuffle (seq 1 20)) 0 }}
{{ "<span class=_a>Foo</span>" | strings.Repeat $n | safeHTML }}
<div class="{{ .Title }}">
ABC.
</div>
<div class="f"></div>
{{ $n := index (shuffle (seq 1 5)) 0 }}
{{ "<hr class=p-3>" | safeHTML }}
`)
for _, lang := range []string{"en", "nb", "no", "sv"} {
for i := 100; i <= 999; i++ {
b.WithContent(fmt.Sprintf("p%d.%s.md", i, lang), fmt.Sprintf("---\ntitle: p%s%d\n---", lang, i))
}
}
b.Build(BuildCfg{})
contentMem := b.FileContent(statsFilename)
cb, err := ioutil.ReadFile(statsFilename)
b.Assert(err, qt.IsNil)
contentFile := string(cb)
for _, content := range []string{contentMem, contentFile} {
stats := &publisher.PublishStats{}
b.Assert(json.Unmarshal([]byte(content), stats), qt.IsNil)
els := stats.HTMLElements
b.Assert(els.Classes, qt.HasLen, 3606) // (4 * 900) + 4 +2
b.Assert(els.Tags, qt.HasLen, 8)
b.Assert(els.IDs, qt.HasLen, 1)
}
} }

View file

@ -87,11 +87,6 @@ func (w *cssClassCollectorWriter) Write(p []byte) (n int, err error) {
if w.isCollecting { if w.isCollecting {
for ; i < len(p); i++ { for ; i < len(p); i++ {
b := p[i] b := p[i]
if !w.inQuote && b == '/' {
// End element, we don't care about those.
w.endCollecting(true)
break
}
w.toggleIfQuote(b) w.toggleIfQuote(b)
if !w.inQuote && b == '>' { if !w.inQuote && b == '>' {
w.endCollecting(false) w.endCollecting(false)

View file

@ -51,6 +51,8 @@ func TestClassCollector(t *testing.T) {
{"duplicates", `<div class="b a b"></div>`, f("div", "a b", "")}, {"duplicates", `<div class="b a b"></div>`, f("div", "a b", "")},
{"single quote", `<body class='b a'></body>`, f("body", "a b", "")}, {"single quote", `<body class='b a'></body>`, f("body", "a b", "")},
{"no quote", `<body class=b id=myelement></body>`, f("body", "b", "myelement")}, {"no quote", `<body class=b id=myelement></body>`, f("body", "b", "myelement")},
// https://github.com/gohugoio/hugo/issues/7161
{"minified a href", `<a class="b a" href=/></a>`, f("a", "a b", "")},
{"AlpineJS bind 1", `<body> {"AlpineJS bind 1", `<body>
<div x-bind:class="{ <div x-bind:class="{