Skip to content

Commit

Permalink
JS: write a!=null?a():undefined as a?.()
Browse files Browse the repository at this point in the history
  • Loading branch information
tdewolff committed Jun 2, 2022
1 parent 373a332 commit b2c6f78
Show file tree
Hide file tree
Showing 5 changed files with 271 additions and 223 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ require (
github.com/fsnotify/fsnotify v1.5.4
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2
github.com/spf13/pflag v1.0.5
github.com/tdewolff/parse/v2 v2.5.32
github.com/tdewolff/parse/v2 v2.5.33
github.com/tdewolff/test v1.0.6
)
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2 h1:JAEbJn3j/FrhdWA9jW8
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/tdewolff/parse/v2 v2.5.32 h1:paypOpwqGqCjzm/QpgZj8+J4O4glJpe2qCtzM9zffgY=
github.com/tdewolff/parse/v2 v2.5.32/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/parse/v2 v2.5.33 h1:D75KlhAeCSQg4Na8cWKehJdPJoZxwdpRbTZw7lZFWNQ=
github.com/tdewolff/parse/v2 v2.5.33/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/test v1.0.6 h1:76mzYJQ83Op284kMT+63iCNCI7NEERsIN8dLM+RiKr4=
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad h1:ntjMns5wyP/fN65tdBD4g8J5w8n015+iIIs9rtjXkY0=
Expand Down
73 changes: 19 additions & 54 deletions js/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -945,41 +945,13 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
// }
//}

// change a===null||a===undefined to a==null
if expr.Op == js.OrToken || expr.Op == js.AndToken {
eqEqOp := js.EqEqToken
eqEqEqOp := js.EqEqEqToken
if expr.Op == js.AndToken {
eqEqOp = js.NotEqToken
eqEqEqOp = js.NotEqEqToken
}

left, isBinaryX := expr.X.(*js.BinaryExpr)
right, isBinaryY := expr.Y.(*js.BinaryExpr)
if isBinaryX && isBinaryY && (left.Op == eqEqOp || left.Op == eqEqEqOp) && (right.Op == eqEqOp || right.Op == eqEqEqOp) {
leftSwitched := false
var leftVar, rightVar js.IExpr
if v, ok := left.X.(*js.Var); ok && isUndefinedOrNull(left.Y) {
leftVar = v
} else if v, ok := left.Y.(*js.Var); ok && isUndefinedOrNull(left.X) {
leftSwitched = true
leftVar = v
}
if v, ok := right.X.(*js.Var); ok && isUndefinedOrNull(right.Y) {
rightVar = v
} else if v, ok := right.Y.(*js.Var); ok && isUndefinedOrNull(right.X) {
rightVar = v
}
if leftVar != nil && leftVar == rightVar {
left.Op = eqEqOp
if leftSwitched {
left.X = &js.LiteralExpr{js.NullToken, nullBytes}
} else {
left.Y = &js.LiteralExpr{js.NullToken, nullBytes}
}
expr = left
}
if v, not, ok := isUndefinedOrNullVar(expr); ok {
// change a===null||a===undefined to a==null
op := js.EqEqToken
if not {
op = js.NotEqToken
}
expr = &js.BinaryExpr{op, v, &js.LiteralExpr{js.NullToken, nullBytes}}
}

m.minifyExpr(expr.X, precLeft)
Expand All @@ -1004,12 +976,6 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
}
}
}
} else if expr.Op == js.EqEqToken || expr.Op == js.NotEqToken {
if isUndefinedOrNull(expr.X) {
expr.X = &js.LiteralExpr{js.NullToken, nullBytes}
} else if isUndefinedOrNull(expr.Y) {
expr.Y = &js.LiteralExpr{js.NullToken, nullBytes}
}
}
m.write(expr.Op.Bytes())
if expr.Op == js.AddToken {
Expand Down Expand Up @@ -1084,8 +1050,10 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
} else {
m.minifyExpr(expr.X, js.OpMember)
}
// 0 < len(m.prev) always
if last := m.prev[len(m.prev)-1]; '0' <= last && last <= '9' {
if expr.Optional {
m.write(questionBytes)
} else if last := m.prev[len(m.prev)-1]; '0' <= last && last <= '9' {
// 0 < len(m.prev) always
isInteger := true
for _, c := range m.prev[:len(m.prev)-1] {
if c < '0' || '9' < c {
Expand Down Expand Up @@ -1160,6 +1128,9 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
} else {
m.minifyExpr(expr.Tag, js.OpMember)
}
if expr.Optional {
m.write(optChainBytes)
}
}
parentInFor := m.inFor
m.inFor = false
Expand Down Expand Up @@ -1210,6 +1181,9 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
m.minifyExpr(expr.X, js.OpCall)
parentInFor := m.inFor
m.inFor = false
if expr.Optional {
m.write(optChainBytes)
}
m.minifyArguments(expr.Args)
m.inFor = parentInFor
case *js.IndexExpr:
Expand All @@ -1223,6 +1197,9 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
} else {
m.minifyExpr(expr.X, js.OpMember)
}
if expr.Optional {
m.write(optChainBytes)
}
if lit, ok := expr.Y.(*js.LiteralExpr); ok && lit.TokenType == js.StringToken && 2 < len(lit.Data) {
if isIdent := js.AsIdentifierName(lit.Data[1 : len(lit.Data)-1]); isIdent {
m.write(dotBytes)
Expand All @@ -1247,18 +1224,6 @@ func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
m.minifyExpr(expr.X, js.OpAssign)
m.write(colonBytes)
m.minifyExpr(expr.Y, js.OpAssign)
case *js.OptChainExpr:
m.minifyExpr(expr.X, js.OpCall)
m.write(optChainBytes)
if callExpr, ok := expr.Y.(*js.CallExpr); ok {
m.minifyArguments(callExpr.Args)
} else if indexExpr, ok := expr.Y.(*js.IndexExpr); ok {
m.write(openBracketBytes)
m.minifyExpr(indexExpr.Y, js.OpExpr)
m.write(closeBracketBytes)
} else {
m.minifyExpr(expr.Y, js.OpPrimary) // TemplateExpr or LiteralExpr
}
case *js.VarDecl:
m.minifyVarDecl(expr, true) // happens in for statement or when vars were hoisted
case *js.FuncDecl:
Expand Down
10 changes: 10 additions & 0 deletions js/js_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,16 @@ func TestJS(t *testing.T) {
{`!!(a===b||c===d)`, `a===b||c===d`},
{`!(a!==null)`, `a===null`},
{`a==void 0`, `a==null`},
//{`if(a!==null&&a!==undefined)a.b()`, `a?.b()`}, // returns undefined instead of false
{`(a===null||a===undefined)?undefined:a()`, `a?.()`},
{`(a===null||a===undefined)?undefined:a[0]`, `a?.[0]`},
{"(a===null||a===undefined)?undefined:a`tpl`", "a?.`tpl`"},
{`(a===null||a===undefined)?undefined:a.b`, `a?.b`},
{`(a===null||a===undefined)?undefined:a.b()`, `a?.b()`},
{`(a===null||a===undefined)?undefined:a.b[0]`, `a?.b[0]`},
{"(a===null||a===undefined)?undefined:a.b`tpl`", "a?.b`tpl`"},
{`(a===null||a===undefined)?undefined:a.#b`, `a?.#b`},
{`(((a===null)||(a===undefined)))?undefined:a()`, `a?.()`},

// other
{`async function g(){await x+y}`, `async function g(){await x+y}`},
Expand Down
Loading

0 comments on commit b2c6f78

Please sign in to comment.