Skip to content

Commit be173cd

Browse files
committed
JS: support shortening of class private elements, fixes #652
1 parent 7575c43 commit be173cd

File tree

5 files changed

+110
-78
lines changed

5 files changed

+110
-78
lines changed

go.mod

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
module github.com/tdewolff/minify/v2
22

3-
go 1.17
3+
go 1.24.0
44

55
toolchain go1.24.1
66

77
require (
88
github.com/djherbis/atime v1.1.0
9-
github.com/fsnotify/fsnotify v1.8.0
10-
github.com/tdewolff/argp v0.0.0-20250209172303-079abae893fb
11-
github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a
9+
github.com/fsnotify/fsnotify v1.9.0
10+
github.com/tdewolff/argp v0.0.0-20250430135133-0f54527d2b1e
11+
github.com/tdewolff/parse/v2 v2.8.5
1212
github.com/tdewolff/test v1.0.11
1313
)
1414

1515
require (
1616
github.com/jmoiron/sqlx v1.4.0 // indirect
1717
github.com/pelletier/go-toml v1.9.5 // indirect
18-
golang.org/x/sys v0.30.0 // indirect
18+
golang.org/x/sys v0.37.0 // indirect
1919
gopkg.in/yaml.v3 v3.0.1 // indirect
2020
)

go.sum

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,26 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
22
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
33
github.com/djherbis/atime v1.1.0 h1:rgwVbP/5by8BvvjBNrbh64Qz33idKT3pSnMSJsxhi0g=
44
github.com/djherbis/atime v1.1.0/go.mod h1:28OF6Y8s3NQWwacXc5eZTsEsiMzp7LF8MbXE+XJPdBE=
5-
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
6-
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
7-
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
8-
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
5+
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
6+
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
97
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
108
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
11-
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
129
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
1310
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
14-
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
1511
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
1612
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
17-
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
1813
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
1914
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
2015
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
2116
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
22-
github.com/tdewolff/argp v0.0.0-20250209172303-079abae893fb h1:Id2aj2q74MC+yHBGQNeTVuQp/gqJd3vZjMmdoVPPfVc=
23-
github.com/tdewolff/argp v0.0.0-20250209172303-079abae893fb/go.mod h1:PKhwRVvnrI2gye5NRF3c4VWbE+3E9mGyRUsNWGcJlDY=
24-
github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a h1:Rmq+utdraciok/97XHRweYdsAo/M4LOswpCboo3yvN4=
25-
github.com/tdewolff/parse/v2 v2.8.5-0.20251020133559-0efcf90bef1a/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
26-
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
17+
github.com/tdewolff/argp v0.0.0-20250430135133-0f54527d2b1e h1:2jfHhbjBKS2wfyvcz5W2eOkQVKv57DKM1C/QYhTovhs=
18+
github.com/tdewolff/argp v0.0.0-20250430135133-0f54527d2b1e/go.mod h1:xw2b1X81m4zY1OGytzHNr/YKXbf/STHkK5idoNamlYE=
19+
github.com/tdewolff/parse/v2 v2.8.5 h1:ZmBiA/8Do5Rpk7bDye0jbbDUpXXbCdc3iah4VeUvwYU=
20+
github.com/tdewolff/parse/v2 v2.8.5/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
2721
github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=
2822
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
29-
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
30-
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
31-
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
23+
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
24+
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
3225
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
3326
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3427
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=

js/js.go

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package js
33

44
import (
55
"bytes"
6+
"fmt"
67
"io"
78
"sort"
89

@@ -627,6 +628,14 @@ func (m *jsMinifier) minifyFuncDecl(decl *js.FuncDecl, inExpr bool) {
627628
m.renamer.rename = parentRename
628629
}
629630

631+
func (m *jsMinifier) minifyClassElementName(name js.ClassElementName) {
632+
if name.Private != nil {
633+
m.write(name.Private.Data)
634+
} else {
635+
m.minifyPropertyName(name.PropertyName)
636+
}
637+
}
638+
630639
func (m *jsMinifier) minifyMethodDecl(decl *js.MethodDecl) {
631640
parentRename := m.renamer.rename
632641
m.renamer.rename = !decl.Body.Scope.HasWith && !m.o.KeepVarNames
@@ -653,7 +662,7 @@ func (m *jsMinifier) minifyMethodDecl(decl *js.MethodDecl) {
653662
m.write(setBytes)
654663
m.writeSpaceBeforeIdent()
655664
}
656-
m.minifyPropertyName(decl.Name)
665+
m.minifyClassElementName(decl.Name)
657666
m.renamer.renameScope(decl.Body.Scope)
658667
m.minifyParams(decl.Params, !decl.Set)
659668
m.minifyBlockStmt(&decl.Body)
@@ -744,6 +753,7 @@ func (m *jsMinifier) minifyClassDecl(decl *js.ClassDecl) {
744753
m.writeSpaceBeforeIdent()
745754
m.minifyExpr(decl.Extends, js.OpLHS)
746755
}
756+
m.renamer.renameClassScope(decl.Scope)
747757
m.write(openBraceBytes)
748758
m.needsSemicolon = false
749759
for _, item := range decl.List {
@@ -760,7 +770,7 @@ func (m *jsMinifier) minifyClassDecl(decl *js.ClassDecl) {
760770
m.write(spaceBytes)
761771
}
762772
}
763-
m.minifyPropertyName(item.Name)
773+
m.minifyClassElementName(item.Name)
764774
if item.Init != nil {
765775
m.write(equalBytes)
766776
m.minifyExpr(item.Init, js.OpAssign)
@@ -1092,6 +1102,18 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
10921102
m.minifyExpr(expr.X, unaryPrecMap[expr.Op])
10931103
}
10941104
case *js.DotExpr:
1105+
var yData []byte
1106+
if lit, ok := expr.Y.(js.LiteralExpr); ok {
1107+
yData = lit.Data
1108+
} else if v, ok := expr.Y.(*js.Var); ok {
1109+
for v.Link != nil {
1110+
v = v.Link
1111+
}
1112+
yData = v.Data
1113+
} else {
1114+
panic(fmt.Sprintf("bad type: %T!=(js.LiteralExpr,*js.Var)", expr.Y)) // should never happen
1115+
}
1116+
10951117
optionalLeft := false
10961118
if group, ok := expr.X.(*js.GroupExpr); ok {
10971119
if lit, ok := group.X.(*js.LiteralExpr); ok && (lit.TokenType == js.DecimalToken || lit.TokenType == js.IntegerToken) {
@@ -1102,7 +1124,7 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
11021124
m.write(dotBytes)
11031125
}
11041126
m.write(dotBytes)
1105-
m.write(expr.Y.Data)
1127+
m.write(yData)
11061128
break
11071129
} else if dot, ok := group.X.(*js.DotExpr); ok {
11081130
optionalLeft = dot.Optional
@@ -1132,7 +1154,7 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
11321154
}
11331155
}
11341156
m.write(dotBytes)
1135-
m.write(expr.Y.Data)
1157+
m.write(yData)
11361158
case *js.GroupExpr:
11371159
if cond, ok := expr.X.(*js.CondExpr); ok {
11381160
expr.X = m.optimizeCondExpr(cond, js.OpExpr)
@@ -1282,69 +1304,71 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
12821304
}
12831305
}
12841306
} else if dot, ok := expr.X.(*js.DotExpr); ok {
1285-
if v, ok := dot.X.(*js.Var); ok && v.Decl == js.NoDecl && bytes.Equal(v.Data, MathBytes) {
1286-
if bytes.Equal(dot.Y.Data, []byte("pow")) {
1287-
// Math.pow(a,b) => a**b
1288-
if len(expr.Args.List) == 2 {
1289-
if js.OpExp < prec {
1290-
m.write(openParenBytes)
1291-
}
1292-
m.minifyExpr(&js.GroupExpr{expr.Args.List[0].Value}, js.OpUpdate)
1293-
m.write(expBytes)
1294-
m.minifyExpr(&js.GroupExpr{expr.Args.List[1].Value}, js.OpExp)
1295-
if js.OpExp < prec {
1296-
m.write(closeParenBytes)
1297-
}
1298-
break
1299-
}
1300-
} else if bytes.Equal(dot.Y.Data, []byte("trunc")) {
1301-
// Math.trunc(x) => x|0
1302-
if len(expr.Args.List) == 1 {
1303-
if js.OpBitOr < prec {
1304-
m.write(openParenBytes)
1305-
}
1306-
m.minifyExpr(&js.GroupExpr{expr.Args.List[0].Value}, js.OpBitOr)
1307-
m.write(bitOrBytes)
1308-
m.write(zeroBytes)
1309-
if js.OpBitOr < prec {
1310-
m.write(closeParenBytes)
1311-
}
1312-
break
1313-
}
1314-
} else if bytes.Equal(dot.Y.Data, []byte("abs")) {
1315-
// Math.abs(x) => x<0?-x:x
1316-
if len(expr.Args.List) == 1 {
1317-
groupLen := 0
1318-
if js.OpAssign < prec {
1319-
groupLen = 2
1307+
if x, ok := dot.X.(*js.Var); ok && x.Decl == js.NoDecl && bytes.Equal(x.Data, MathBytes) {
1308+
if y, ok := dot.Y.(js.LiteralExpr); ok {
1309+
if bytes.Equal(y.Data, []byte("pow")) {
1310+
// Math.pow(a,b) => a**b
1311+
if len(expr.Args.List) == 2 {
1312+
if js.OpExp < prec {
1313+
m.write(openParenBytes)
1314+
}
1315+
m.minifyExpr(&js.GroupExpr{expr.Args.List[0].Value}, js.OpUpdate)
1316+
m.write(expBytes)
1317+
m.minifyExpr(&js.GroupExpr{expr.Args.List[1].Value}, js.OpExp)
1318+
if js.OpExp < prec {
1319+
m.write(closeParenBytes)
1320+
}
1321+
break
13201322
}
1321-
if v, ok := expr.Args.List[0].Value.(*js.Var); ok && len(v.Data)*2+groupLen+5 < 10 {
1322-
if js.OpAssign < prec {
1323+
} else if bytes.Equal(y.Data, []byte("trunc")) {
1324+
// Math.trunc(x) => x|0
1325+
if len(expr.Args.List) == 1 {
1326+
if js.OpBitOr < prec {
13231327
m.write(openParenBytes)
13241328
}
1325-
m.minifyExpr(v, js.OpCoalesce)
1326-
m.write([]byte("<0?-"))
1327-
m.minifyExpr(v, js.OpAssign)
1328-
m.write(colonBytes)
1329-
m.minifyExpr(v, js.OpAssign)
1330-
if js.OpAssign < prec {
1329+
m.minifyExpr(&js.GroupExpr{expr.Args.List[0].Value}, js.OpBitOr)
1330+
m.write(bitOrBytes)
1331+
m.write(zeroBytes)
1332+
if js.OpBitOr < prec {
13311333
m.write(closeParenBytes)
13321334
}
13331335
break
13341336
}
1335-
}
1336-
} else if bytes.Equal(dot.Y.Data, []byte("sqrt")) {
1337-
// Math.sqrt(x) => x**.5
1338-
if len(expr.Args.List) == 1 {
1339-
if js.OpExp < prec {
1340-
m.write(openParenBytes)
1337+
} else if bytes.Equal(y.Data, []byte("abs")) {
1338+
// Math.abs(x) => x<0?-x:x
1339+
if len(expr.Args.List) == 1 {
1340+
groupLen := 0
1341+
if js.OpAssign < prec {
1342+
groupLen = 2
1343+
}
1344+
if v, ok := expr.Args.List[0].Value.(*js.Var); ok && len(v.Data)*2+groupLen+5 < 10 {
1345+
if js.OpAssign < prec {
1346+
m.write(openParenBytes)
1347+
}
1348+
m.minifyExpr(v, js.OpCoalesce)
1349+
m.write([]byte("<0?-"))
1350+
m.minifyExpr(v, js.OpAssign)
1351+
m.write(colonBytes)
1352+
m.minifyExpr(v, js.OpAssign)
1353+
if js.OpAssign < prec {
1354+
m.write(closeParenBytes)
1355+
}
1356+
break
1357+
}
13411358
}
1342-
m.minifyExpr(&js.GroupExpr{expr.Args.List[0].Value}, js.OpUpdate)
1343-
m.write([]byte("**.5"))
1344-
if js.OpExp < prec {
1345-
m.write(closeParenBytes)
1359+
} else if bytes.Equal(y.Data, []byte("sqrt")) {
1360+
// Math.sqrt(x) => x**.5
1361+
if len(expr.Args.List) == 1 {
1362+
if js.OpExp < prec {
1363+
m.write(openParenBytes)
1364+
}
1365+
m.minifyExpr(&js.GroupExpr{expr.Args.List[0].Value}, js.OpUpdate)
1366+
m.write([]byte("**.5"))
1367+
if js.OpExp < prec {
1368+
m.write(closeParenBytes)
1369+
}
1370+
break
13461371
}
1347-
break
13481372
}
13491373
}
13501374
}

js/js_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -933,6 +933,7 @@ func TestJSVarRenaming(t *testing.T) {
933933
{`()=>{let foo;Math.abs(foo)}`, `()=>{let a;a<0?-a:a}`},
934934
{`()=>{let foo;isNaN(foo)}`, `()=>{let a;isNaN(a)}`},
935935
{`()=>{let isNaN;isNaN(foo)}`, `()=>{let a;a(foo)}`},
936+
{`class a { #foo = 1; #bar() { this.#foo++ } }`, `class a{#a=1;#b(){this.#a++}}`},
936937

937938
// go-fuzz
938939
{`var ÆÆ,ÆÆ=t;var ÆÆ=v,a=ÿ`, `var ÆÆ=t,ÆÆ=v,a=ÿ`},

js/vars.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ func (r *renamer) renameScope(scope js.Scope) {
6464
}
6565
}
6666

67+
// rename all private elements in a class
68+
func (r *renamer) renameClassScope(scope js.Scope) {
69+
if !r.rename {
70+
return
71+
}
72+
73+
i := 0
74+
sort.Sort(js.VarsByUses(scope.Declared))
75+
for _, v := range scope.Declared {
76+
v.Data = append(v.Data[:1], r.getName(v.Data[1:], i)...) // keep #
77+
i++
78+
}
79+
}
80+
6781
func (r *renamer) isReserved(name []byte, undeclared js.VarArray) bool {
6882
if 1 < len(name) { // there are no keywords or known globals that are one character long
6983
if _, ok := r.reserved[string(name)]; ok {

0 commit comments

Comments
 (0)