Skip to content

Commit ee34e32

Browse files
committed
JS: add shorter versions for isNaN, parseInt, Number, Math.pow, Math.abs, and Math.trunc, fixes #790
1 parent 55cda6b commit ee34e32

File tree

3 files changed

+148
-2
lines changed

3 files changed

+148
-2
lines changed

js/js.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/tdewolff/minify/v2"
1010
"github.com/tdewolff/parse/v2"
1111
"github.com/tdewolff/parse/v2/js"
12+
"github.com/tdewolff/parse/v2/strconv"
1213
)
1314

1415
type blockType int
@@ -1200,6 +1201,131 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
12001201
}
12011202
}
12021203
case *js.CallExpr:
1204+
if v, ok := expr.X.(*js.Var); ok && v.Decl == js.NoDecl {
1205+
if bytes.Equal(v.Data, isNaNBytes) {
1206+
// isNaN(x) => x!=x
1207+
if len(expr.Args.List) == 1 {
1208+
groupLen := 0
1209+
if js.OpEquals < prec {
1210+
groupLen = 2
1211+
}
1212+
if arg, ok := expr.Args.List[0].Value.(*js.Var); ok && len(arg.Data)+groupLen < 7 {
1213+
if js.OpEquals < prec {
1214+
m.write(openParenBytes)
1215+
}
1216+
m.write(arg.Data)
1217+
m.write(notEqualBytes)
1218+
m.write(arg.Data)
1219+
if js.OpEquals < prec {
1220+
m.write(closeParenBytes)
1221+
}
1222+
break
1223+
}
1224+
}
1225+
} else if bytes.Equal(v.Data, NumberBytes) {
1226+
// Number(x) => +x
1227+
if len(expr.Args.List) == 1 {
1228+
if js.OpUnary < prec {
1229+
m.write(openParenBytes)
1230+
}
1231+
m.write(plusBytes)
1232+
m.minifyExpr(expr.Args.List[0].Value, js.OpUnary)
1233+
if js.OpUnary < prec {
1234+
m.write(closeParenBytes)
1235+
}
1236+
break
1237+
}
1238+
} else if bytes.Equal(v.Data, parseIntBytes) {
1239+
// parseInt(x) => +x
1240+
base := 10
1241+
if len(expr.Args.List) == 2 {
1242+
base = 0
1243+
if arg, ok := expr.Args.List[1].Value.(*js.LiteralExpr); ok {
1244+
if i, n := strconv.ParseInt(arg.Data); n == len(arg.Data) {
1245+
base = int(i)
1246+
}
1247+
}
1248+
}
1249+
if len(expr.Args.List) == 1 || len(expr.Args.List) == 2 && (base == 2 || base == 8 || base == 10 || base == 16) {
1250+
if arg, ok := expr.Args.List[0].Value.(*js.Var); ok {
1251+
if js.OpUnary < prec {
1252+
m.write(openParenBytes)
1253+
}
1254+
m.write(plusBytes)
1255+
if base == 10 {
1256+
m.write(arg.Data)
1257+
} else if base == 2 {
1258+
m.write([]byte(`("0b"+`))
1259+
m.write(arg.Data)
1260+
m.write(closeParenBytes)
1261+
} else if base == 8 {
1262+
m.write([]byte(`("0o"+`))
1263+
m.write(arg.Data)
1264+
m.write(closeParenBytes)
1265+
} else if base == 16 {
1266+
m.write([]byte(`("0x"+`))
1267+
m.write(arg.Data)
1268+
m.write(closeParenBytes)
1269+
}
1270+
if js.OpUnary < prec {
1271+
m.write(closeParenBytes)
1272+
}
1273+
break
1274+
}
1275+
}
1276+
}
1277+
} else if dot, ok := expr.X.(*js.DotExpr); ok {
1278+
if v, ok := dot.X.(*js.Var); ok && v.Decl == js.NoDecl && bytes.Equal(v.Data, MathBytes) {
1279+
if bytes.Equal(dot.Y.Data, []byte("pow")) {
1280+
if len(expr.Args.List) == 2 {
1281+
if js.OpExp < prec {
1282+
m.write(openParenBytes)
1283+
}
1284+
m.minifyExpr(expr.Args.List[0].Value, js.OpUpdate)
1285+
m.write(expBytes)
1286+
m.minifyExpr(expr.Args.List[1].Value, js.OpExp)
1287+
if js.OpExp < prec {
1288+
m.write(closeParenBytes)
1289+
}
1290+
break
1291+
}
1292+
} else if bytes.Equal(dot.Y.Data, []byte("trunc")) {
1293+
if len(expr.Args.List) == 1 {
1294+
if js.OpBitOr < prec {
1295+
m.write(openParenBytes)
1296+
}
1297+
m.minifyExpr(expr.Args.List[0].Value, js.OpBitOr)
1298+
m.write(bitOrBytes)
1299+
m.write(zeroBytes)
1300+
if js.OpBitOr < prec {
1301+
m.write(closeParenBytes)
1302+
}
1303+
break
1304+
}
1305+
} else if bytes.Equal(dot.Y.Data, []byte("abs")) {
1306+
if len(expr.Args.List) == 1 {
1307+
groupLen := 0
1308+
if js.OpAssign < prec {
1309+
groupLen = 2
1310+
}
1311+
if v, ok := expr.Args.List[0].Value.(*js.Var); ok && len(v.Data)*2+groupLen+5 < 10 {
1312+
if js.OpAssign < prec {
1313+
m.write(openParenBytes)
1314+
}
1315+
m.write(v.Data)
1316+
m.write([]byte("<0?-"))
1317+
m.write(v.Data)
1318+
m.write(colonBytes)
1319+
m.write(v.Data)
1320+
if js.OpAssign < prec {
1321+
m.write(closeParenBytes)
1322+
}
1323+
break
1324+
}
1325+
}
1326+
}
1327+
}
1328+
}
12031329
m.minifyExpr(expr.X, js.OpCall)
12041330
parentInFor := m.inFor
12051331
m.inFor = false

js/js_test.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,8 +679,8 @@ func TestJS(t *testing.T) {
679679
{`a=obj["3name"]`, `a=obj["3name"]`},
680680
{"a=b`tmpl${a?b:b}tmpl`", "a=b`tmpl${a,b}tmpl`"},
681681
{`a=b?.[c]`, `a=b?.[c]`},
682-
{`a=b?.["c"]`, `a=b?.c`}, // Issue 757
683-
{`a=b?.["c d"]`, `a=b?.["c d"]`}, // Issue 757
682+
{`a=b?.["c"]`, `a=b?.c`}, // #757
683+
{`a=b?.["c d"]`, `a=b?.["c d"]`}, // #757
684684
{`a=b.#c`, `a=b.#c`},
685685
{`a=b().#c`, `a=b().#c`},
686686
{`a=b?.#c`, `a=b?.#c`},
@@ -689,6 +689,18 @@ func TestJS(t *testing.T) {
689689
{`let a="string";a`, `let a="string";a`},
690690
{`f((a,b)||d)`, `f((a,b)||d)`},
691691

692+
// math functions, see #790
693+
{`Math.abs(x)`, `x<0?-x:x`},
694+
{`Math.trunc(x)`, `x|0`},
695+
{`Math.pow(a,b)`, `a**b`},
696+
{`isNaN(x)`, `x!=x`},
697+
{`Number(x)`, `+x`},
698+
{`parseInt(x)`, `+x`},
699+
{`parseInt(x,2)`, `+("0b"+x)`},
700+
{`parseInt(x,8)`, `+("0o"+x)`},
701+
{`parseInt(x,10)`, `+x`},
702+
{`parseInt(x,16)`, `+("0x"+x)`},
703+
692704
// merge expressions
693705
{`function f(){b=5;return a+b}`, `function f(){return b=5,a+b}`},
694706
{`b=5;throw a+b`, `throw b=5,a+b`},

js/util.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ var (
1515
spaceBytes = []byte(" ")
1616
newlineBytes = []byte("\n")
1717
starBytes = []byte("*")
18+
plusBytes = []byte("+")
19+
expBytes = []byte("**")
20+
bitOrBytes = []byte("|")
1821
colonBytes = []byte(":")
1922
semicolonBytes = []byte(";")
2023
commaBytes = []byte(",")
@@ -33,6 +36,7 @@ var (
3336
equalBytes = []byte("=")
3437
optChainBytes = []byte("?.")
3538
arrowBytes = []byte("=>")
39+
notEqualBytes = []byte("!=")
3640
zeroBytes = []byte("0")
3741
oneBytes = []byte("1")
3842
letBytes = []byte("let")
@@ -83,6 +87,10 @@ var (
8387
groupedNotOneBytes = []byte("(!1)")
8488
debuggerBytes = []byte("debugger")
8589
regExpScriptBytes = []byte("/script>")
90+
isNaNBytes = []byte("isNaN")
91+
NumberBytes = []byte("Number")
92+
parseIntBytes = []byte("parseInt")
93+
MathBytes = []byte("Math")
8694
)
8795

8896
func isEmptyStmt(stmt js.IStmt) bool {

0 commit comments

Comments
 (0)