Skip to content

Commit 9c8ad4d

Browse files
GustavoSepta-h
andauthored
fix: increase hash size for autogenerated IDs to reduce collisions - fixes #978
Co-authored-by: Adrian Hesketh <adrianhesketh@hushmail.com>
1 parent 4f2ce16 commit 9c8ad4d

File tree

8 files changed

+47
-23
lines changed

8 files changed

+47
-23
lines changed

.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.2.793
1+
0.2.796

generator/test-css-expression/render_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import (
88
)
99

1010
var expected = templ.ComponentCSSClass{
11-
ID: "className_34fc",
12-
Class: templ.SafeCSS(`.className_34fc{background-color:#ffffff;max-height:calc(100vh - 170px);color:#ff0000;}`),
11+
ID: "className_34fc0328",
12+
Class: templ.SafeCSS(`.className_34fc0328{background-color:#ffffff;max-height:calc(100vh - 170px);color:#ff0000;}`),
1313
}
1414

1515
func TestCSSExpression(t *testing.T) {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
<div class="red_050e">
1+
<div class="red_050e5e03">
22
Red text
33
</div>

generator/test-css-middleware/render_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
//go:embed expected.html
1717
var expected string
1818

19-
var expectedCSS = `.red_050e{color:red;}
19+
var expectedCSS = `.red_050e5e03{color:red;}
2020
`
2121

2222
func Test(t *testing.T) {

generator/test-css-usage/expected.html

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,33 @@
44
}
55
</style>
66
<div class="test">Style tags are supported</div>
7-
<style type="text/css">.cssComponentGreen_58d2{color:#00ff00;}</style>
8-
<div class="cssComponentGreen_58d2">CSS components are supported</div>
9-
<div class="cssComponentGreen_58d2 classA &amp;&amp;&amp;classB classC d e" type="button">Both CSS components and constants are supported</div>
10-
<div class="cssComponentGreen_58d2 classA &amp;&amp;&amp;classB classC d e" type="button">Both CSS components and constants are supported</div>
7+
<style type="text/css">.cssComponentGreen_58d2872e{color:#00ff00;}</style>
8+
<div class="cssComponentGreen_58d2872e">CSS components are supported</div>
9+
<div class="cssComponentGreen_58d2872e classA &amp;&amp;&amp;classB classC d e" type="button">Both CSS components and constants are supported</div>
10+
<div class="cssComponentGreen_58d2872e classA &amp;&amp;&amp;classB classC d e" type="button">Both CSS components and constants are supported</div>
1111
<div class="a c">Maps can be used to determine if a class should be added or not.</div>
12-
<style type="text/css">.e_739d{font-size:14pt;}</style>
13-
<div class="a c e_739d">KV can be used to conditionally set classes.</div>
12+
<style type="text/css">.e_739d4573{font-size:14pt;}</style>
13+
<div class="a c e_739d4573">KV can be used to conditionally set classes.</div>
1414
<div class="bg-violet-500 hover:bg-red-600 hover:bg-sky-700 text-[#50d71e] w-[calc(100%-4rem)">Psuedo attributes and complex class names are supported.</div>
1515
<div class="a&#34; onClick=&#34;alert(&#39;hello&#39;)&#34;">
1616
Class names are HTML escaped.
1717
</div>
1818
<style type="text/css">
19-
.loading_a3cc{width:50%;}
19+
.loading_a3cc3f08{width:50%;}
2020
</style>
21-
<div class="loading_a3cc">
21+
<div class="loading_a3cc3f08">
2222
CSS components can be used with arguments.
2323
</div>
2424
<style type="text/css">
25-
.loading_9ccc{width:100%;}
25+
.loading_9ccc4ca9{width:100%;}
2626
</style>
27-
<div class="loading_9ccc">
27+
<div class="loading_9ccc4ca9">
2828
CSS components can be used with arguments.
2929
</div>
3030
<style type="text/css">
31-
.windVaneRotation_b68b{transform:rotate(45deg);}
31+
.windVaneRotation_b68b990e{transform:rotate(45deg);}
3232
</style>
33-
<div class="windVaneRotation_b68b">
33+
<div class="windVaneRotation_b68b990e">
3434
Rotate
3535
</div>
3636

generator/test-element-attributes/expected.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
<style type="text/css">
2-
.important_2ed1{width:100;}
2+
.important_2ed176fc{width:100;}
33
</style>
44
<div style="width: 100;">
55
Important
66
</div>
77
<style type="text/css">
8-
.unimportant_900a{width:50;}
8+
.unimportant_900aeb18{width:50;}
99
</style>
10-
<div style="width: 100;" class="unimportant_900a">
10+
<div style="width: 100;" class="unimportant_900aeb18">
1111
Unimportant
1212
</div>
13-
<div style="width: 100;" class="unimportant_900a">
13+
<div style="width: 100;" class="unimportant_900aeb18">
1414
Else
1515
</div>
1616
<div data-script="on click

runtime.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,11 @@ func (css ComponentCSSClass) ClassName() string {
256256
// CSSID calculates an ID.
257257
func CSSID(name string, css string) string {
258258
sum := sha256.Sum256([]byte(css))
259-
hp := hex.EncodeToString(sum[:])[0:4]
259+
hs := hex.EncodeToString(sum[:])[0:8] // NOTE: See issue #978. Minimum recommended hs length is 6.
260260
// Benchmarking showed this was fastest, and with fewest allocations (1).
261261
// Using strings.Builder (2 allocs).
262262
// Using fmt.Sprintf (3 allocs).
263-
return name + "_" + hp
263+
return name + "_" + hs
264264
}
265265

266266
// NewCSSMiddleware creates HTTP middleware that renders a global stylesheet of ComponentCSSClass

runtime_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,30 @@ import (
1515
"github.com/google/go-cmp/cmp"
1616
)
1717

18+
func TestCSSID(t *testing.T) {
19+
t.Run("minimum hash suffix length is 8", func(t *testing.T) {
20+
// See issue #978.
21+
name := "classA"
22+
css := "background-color:white;"
23+
actual := len(templ.CSSID(name, css))
24+
expected := len(name) + 1 + 8
25+
if expected != actual {
26+
t.Errorf("expected length %d, got %d", expected, actual)
27+
}
28+
})
29+
t.Run("known hash collisions are avoided", func(t *testing.T) {
30+
name := "classA"
31+
// Note that the first 4 characters of the hash are the same.
32+
css1 := "grid-column:1;grid-row:1;" // After hash: f781266f
33+
css2 := "grid-column:13;grid-row:6;" // After hash: f781f18b
34+
id1 := templ.CSSID(name, css1)
35+
id2 := templ.CSSID(name, css2)
36+
if id1 == id2 {
37+
t.Errorf("hash collision: %s == %s", id1, id2)
38+
}
39+
})
40+
}
41+
1842
func TestCSSHandler(t *testing.T) {
1943
tests := []struct {
2044
name string

0 commit comments

Comments
 (0)