mirror of
https://github.com/gohugoio/hugo.git
synced 2024-05-11 05:54:58 +00:00
publisher: Improve class collector for dynamic classes
E.g. * AlpinesJS' :class="isTrue 'class1' : 'class2'" * And dynamic classes with colon in them, e.g. `hover:bg-white`
This commit is contained in:
@ -32,7 +32,7 @@ const eof = -1
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
htmlJsonFixer = strings.NewReplacer(", ", "\n")
|
htmlJsonFixer = strings.NewReplacer(", ", "\n")
|
||||||
jsonAttrRe = regexp.MustCompile(`'?(.*?)'?:.*`)
|
jsonAttrRe = regexp.MustCompile(`'?(.*?)'?:\s.*`)
|
||||||
classAttrRe = regexp.MustCompile(`(?i)^class$|transition`)
|
classAttrRe = regexp.MustCompile(`(?i)^class$|transition`)
|
||||||
|
|
||||||
skipInnerElementRe = regexp.MustCompile(`(?i)^(pre|textarea|script|style)`)
|
skipInnerElementRe = regexp.MustCompile(`(?i)^(pre|textarea|script|style)`)
|
||||||
@ -404,21 +404,31 @@ func (w *htmlElementsCollectorWriter) parseHTMLElement(elStr string) (el htmlEle
|
|||||||
if conf.DisableClasses {
|
if conf.DisableClasses {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if classAttrRe.MatchString(a.Key) {
|
if classAttrRe.MatchString(a.Key) {
|
||||||
el.Classes = append(el.Classes, strings.Fields(a.Val)...)
|
el.Classes = append(el.Classes, strings.Fields(a.Val)...)
|
||||||
} else {
|
} else {
|
||||||
key := strings.ToLower(a.Key)
|
key := strings.ToLower(a.Key)
|
||||||
val := strings.TrimSpace(a.Val)
|
val := strings.TrimSpace(a.Val)
|
||||||
if strings.Contains(key, "class") && strings.HasPrefix(val, "{") {
|
|
||||||
// This looks like a Vue or AlpineJS class binding.
|
if strings.Contains(key, ":class") {
|
||||||
val = htmlJsonFixer.Replace(strings.Trim(val, "{}"))
|
if strings.HasPrefix(val, "{") {
|
||||||
lines := strings.Split(val, "\n")
|
// This looks like a Vue or AlpineJS class binding.
|
||||||
for i, l := range lines {
|
val = htmlJsonFixer.Replace(strings.Trim(val, "{}"))
|
||||||
lines[i] = strings.TrimSpace(l)
|
lines := strings.Split(val, "\n")
|
||||||
|
for i, l := range lines {
|
||||||
|
lines[i] = strings.TrimSpace(l)
|
||||||
|
}
|
||||||
|
val = strings.Join(lines, "\n")
|
||||||
|
|
||||||
|
val = jsonAttrRe.ReplaceAllString(val, "$1")
|
||||||
|
|
||||||
|
el.Classes = append(el.Classes, strings.Fields(val)...)
|
||||||
}
|
}
|
||||||
val = strings.Join(lines, "\n")
|
// Also add single quoted strings.
|
||||||
val = jsonAttrRe.ReplaceAllString(val, "$1")
|
// This may introduce some false positives, but it covers some missing cases in the above.
|
||||||
el.Classes = append(el.Classes, strings.Fields(val)...)
|
// E.g. AlpinesJS' :class="isTrue 'class1' : 'class2'"
|
||||||
|
el.Classes = append(el.Classes, extractSingleQuotedStrings(val)...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -519,3 +529,29 @@ LOOP:
|
|||||||
func isSpace(b byte) bool {
|
func isSpace(b byte) bool {
|
||||||
return b == ' ' || b == '\t' || b == '\n'
|
return b == ' ' || b == '\t' || b == '\n'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractSingleQuotedStrings(s string) []string {
|
||||||
|
var (
|
||||||
|
inQuote bool
|
||||||
|
lo int
|
||||||
|
hi int
|
||||||
|
)
|
||||||
|
|
||||||
|
var words []string
|
||||||
|
|
||||||
|
for i, r := range s {
|
||||||
|
switch {
|
||||||
|
case r == '\'':
|
||||||
|
if !inQuote {
|
||||||
|
inQuote = true
|
||||||
|
lo = i + 1
|
||||||
|
} else {
|
||||||
|
inQuote = false
|
||||||
|
hi = i
|
||||||
|
words = append(words, strings.Fields(s[lo:hi])...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return words
|
||||||
|
}
|
||||||
|
@ -99,6 +99,8 @@ func TestClassCollector(t *testing.T) {
|
|||||||
pl-2: b == 3,
|
pl-2: b == 3,
|
||||||
'text-gray-600': (a > 1)
|
'text-gray-600': (a > 1)
|
||||||
}" class="block w-36 cursor-pointer pr-3 no-underline capitalize"></a>`, f("a", "block capitalize cursor-pointer no-underline pl-2 pl-3 pr-3 text-a text-b text-gray-600 w-36", "")},
|
}" class="block w-36 cursor-pointer pr-3 no-underline capitalize"></a>`, f("a", "block capitalize cursor-pointer no-underline pl-2 pl-3 pr-3 text-a text-b text-gray-600 w-36", "")},
|
||||||
|
{"AlpineJS bind 6", `<button :class="isActive(32) ? 'border-gray-500 bg-white pt border-t-2' : 'border-transparent hover:bg-gray-100'"></button>`, f("button", "bg-white border-gray-500 border-t-2 border-transparent hover:bg-gray-100 pt", "")},
|
||||||
|
{"AlpineJS bind 7", `<button :class="{ 'border-gray-500 bg-white pt border-t-2': isActive(32), 'border-transparent hover:bg-gray-100': !isActive(32) }"></button>`, f("button", "bg-white border-gray-500 border-t-2 border-transparent hover:bg-gray-100 pt", "")},
|
||||||
{"AlpineJS transition 1", `<div x-transition:enter-start="opacity-0 transform mobile:-translate-x-8 sm:-translate-y-8">`, f("div", "mobile:-translate-x-8 opacity-0 sm:-translate-y-8 transform", "")},
|
{"AlpineJS transition 1", `<div x-transition:enter-start="opacity-0 transform mobile:-translate-x-8 sm:-translate-y-8">`, f("div", "mobile:-translate-x-8 opacity-0 sm:-translate-y-8 transform", "")},
|
||||||
{"Vue bind", `<div v-bind:class="{ active: isActive }"></div>`, f("div", "active", "")},
|
{"Vue bind", `<div v-bind:class="{ active: isActive }"></div>`, f("div", "active", "")},
|
||||||
// Issue #7746
|
// Issue #7746
|
||||||
@ -136,7 +138,9 @@ func TestClassCollector(t *testing.T) {
|
|||||||
{minify: true},
|
{minify: true},
|
||||||
} {
|
} {
|
||||||
|
|
||||||
c.Run(fmt.Sprintf("%s--minify-%t", test.name, variant.minify), func(c *qt.C) {
|
name := fmt.Sprintf("%s--minify-%t", test.name, variant.minify)
|
||||||
|
|
||||||
|
c.Run(name, func(c *qt.C) {
|
||||||
w := newHTMLElementsCollectorWriter(newHTMLElementsCollector(
|
w := newHTMLElementsCollectorWriter(newHTMLElementsCollector(
|
||||||
config.BuildStats{Enable: true},
|
config.BuildStats{Enable: true},
|
||||||
))
|
))
|
||||||
|
Reference in New Issue
Block a user