8000 minify/js/js.go at master · xliiauo/minify · GitHub
[go: up one dir, main page]

Skip to content

Latest commit

 

History

History
1280 lines (1224 loc) · 35.2 KB

js.go

File metadata and controls

1280 lines (1224 loc) · 35.2 KB
m.write(colonBytes)
}
m.minifyBindingElement(item.Value)
}
if binding.Rest != nil {
if 0 < len(binding.List) {
m.write(commaBytes)
}
m.write(ellipsisBytes)
m.write(binding.Rest.Data)
}
m.write(closeBraceBytes)
}
}
func (m *jsMinifier) minifyBinaryExpr(expr *js.BinaryExpr) bool {
if lit, ok := expr.Y.(*js.LiteralExpr); ok && lit.TokenType == js.StringToken && expr.Op == js.AddToken {
// merge strings that are added together
n := len(lit.Data) - 2
strings := []*js.LiteralExpr{lit}
left := expr
for {
if 50 < len(strings) {
return false // limit recursion
} else if lit, ok := left.X.(*js.LiteralExpr); ok && lit.TokenType == js.StringToken {
n += len(lit.Data) - 2
strings = append(strings, lit)
break
} else if left, ok = left.X.(*js.BinaryExpr); ok && left.Op == js.AddToken {
if lit, ok := left.Y.(*js.LiteralExpr); ok && lit.TokenType == js.StringToken {
n += len(lit.Data) - 2
strings = append(strings, lit)
continue
}
}
return false
}
b := make([]byte, 0, n+2)
b = append(b, strings[len(strings)-1].Data[:len(strings[len(strings)-1].Data)-1]...)
for i := len(strings) - 2; 0 < i; i-- {
b = append(b, strings[i].Data[1:len(strings[i].Data)-1]...)
}
b = append(b, strings[0].Data[1:]...)
b[len(b)-1] = b[0]
// unescaped quotes will be repaired in minifyString
m.write(minifyString(b))
return true
}
return false
}
func (m *jsMinifier) minifyExpr(i js.IExpr, prec js.OpPrec) {
switch expr := i.(type) {
case *js.Var:
for expr.Link != nil {
expr = expr.Link
}
data := expr.Data
if bytes.Equal(data, undefinedBytes) { // TODO: only if not defined
if js.OpUnary < prec {
m.write(groupedVoidZeroBytes)
} else {
m.write(voidZeroBytes)
}
} else if bytes.Equal(data, infinityBytes) { // TODO: only if not defined
if js.OpMul < prec {
m.write(groupedOneDivZeroBytes)
} else {
m.write(oneDivZeroBytes)
}
} else {
m.write(data)
}
case *js.LiteralExpr:
if expr.TokenType == js.DecimalToken {
m.write(minify.Number(expr.Data, m.o.Precision))
} else if expr.TokenType == js.BinaryToken {
m.write(binaryNumber(expr.Data, m.o.Precision))
} else if expr.TokenType == js.OctalToken {
m.write(octalNumber(expr.Data, m.o.Precision))
} else if expr.TokenType == js.HexadecimalToken {
m.write(hexadecimalNumber(expr.Data, m.o.Precision))
} else if expr.TokenType == js.TrueToken {
if js.OpUnary < prec {
m.write(groupedNotZeroBytes)
} else {
m.write(notZeroBytes)
}
} else if expr.TokenType == js.FalseToken {
if js.OpUnary < prec {
m.write(groupedNotOneBytes)
} else {
m.write(notOneBytes)
}
} else if expr.TokenType == js.StringToken {
m.write(minifyString(expr.Data))
} else {
m.write(expr.Data)
}
case *js.BinaryExpr:
if m.minifyBinaryExpr(expr) {
break
}
precLeft := binaryLeftPrecMap[expr.Op]
// convert (a,b)&&c into a,b&&c but not a=(b,c)&&d into a=(b,c&&d)
if prec <= js.OpExpr {
if group, ok := expr.X.(*js.GroupExpr); ok {
if binary, ok := group.X.(*js.BinaryExpr); ok && binary.Op == js.CommaToken && js.OpAnd <= exprPrec(binary.Y) {
expr.X = group.X
precLeft = js.OpExpr
}
}
}
if expr.Op == js.InstanceofToken || expr.Op == js.InToken {
group := expr.Op == js.InToken && m.inFor
if group {
m.write(openParenBytes)
}
m.minifyExpr(expr.X, precLeft)
m.writeSpaceAfterIdent()
m.write(expr.Op.Bytes())
m.writeSpaceBeforeIdent()
m.minifyExpr(expr.Y, binaryRightPrecMap[expr.Op])
if group {
m.write(closeParenBytes)
}
} else {
m.minifyExpr(expr.X, precLeft)
if expr.Op == js.GtToken && m.prev[len(m.prev)-1] == '-' {
m.write(spaceBytes)
} else if expr.Op == js.EqEqEqToken || expr.Op == js.NotEqEqToken {
if left, ok := expr.X.(*js.UnaryExpr); ok && left.Op == js.TypeofToken {
if right, ok := expr.Y.(*js.LiteralExpr); ok && right.TokenType == js.StringToken {
if expr.Op == js.EqEqEqToken {
expr.Op = js.EqEqToken
} else {
expr.Op = js.NotEqToken
}
}
} else if right, ok := expr.Y.(*js.UnaryExpr); ok && right.Op == js.TypeofToken {
if left, ok := expr.X.(*js.LiteralExpr); ok && left.TokenType == js.StringToken {
if expr.Op == js.EqEqEqToken {
expr.Op = js.EqEqToken
} else {
expr.Op = js.NotEqToken
}
}
}
}
m.write(expr.Op.Bytes())
if expr.Op == js.AddToken {
// +++ => + ++
m.writeSpaceBefore('+')
} else if expr.Op == js.SubToken {
// --- => - --
m.writeSpaceBefore('-')
} else if expr.Op == js.DivToken {
// // => / /
m.writeSpaceBefore('/')
}
m.minifyExpr(expr.Y, binaryRightPrecMap[expr.Op])
}
case *js.UnaryExpr:
if expr.Op == js.PostIncrToken || expr.Op == js.PostDecrToken {
m.minifyExpr(expr.X, unaryPrecMap[expr.Op])
m.write(expr.Op.Bytes())
} else {
isLtNot := expr.Op == js.NotToken && len(m.prev) == 1 && m.prev[0] == '<'
m.write(expr.Op.Bytes())
if expr.Op == js.DeleteToken || expr.Op == js.VoidToken || expr.Op == js.TypeofToken || expr.Op == js.AwaitToken {
m.writeSpaceBeforeIdent()
} else if expr.Op == js.PosToken {
// +++ => + ++
m.writeSpaceBefore('+')
} else if expr.Op == js.NegToken || isLtNot {
// --- => - --
// <!-- => <! --
m.writeSpaceBefore('-')
} else if expr.Op == js.NotToken {
if lit, ok := expr.X.(*js.LiteralExpr); ok && (lit.TokenType == js.StringToken || lit.TokenType == js.RegExpToken) {
// !"string" => !1
m.write(oneBytes)
break
} else if ok && lit.TokenType == js.DecimalToken {
// !123 => !1 (except for !0)
if num := minify.Number(lit.Data, m.o.Precision); len(num) == 1 && num[0] == '0' {
m.write(zeroBytes)
} else {
m.write(oneBytes)
}
break
}
}
m.minifyExpr(expr.X, unaryPrecMap[expr.Op])
}
case *js.DotExpr:
if group, ok := expr.X.(*js.GroupExpr); ok {
if lit, ok := group.X.(*js.LiteralExpr); ok && lit.TokenType == js.DecimalToken {
num := minify.Number(lit.Data, m.o.Precision)
isInt := true
for _, c := range num {
if c == '.' || c == 'e' || c == 'E' {
isInt = false
break
}
}
if isInt {
m.write(num)
m.write(dotBytes)
} else {
m.write(num)
}
m.write(dotBytes)
m.write(expr.Y.Data)
break
}
}
if prec < js.OpMember {
m.minifyExpr(expr.X, js.OpCall)
} else {
m.minifyExpr(expr.X, js.OpMember)
}
if last := m.prev[len(m.prev)-1]; '0' <= last && last <= '9' {
isInteger := true
for _, c := range m.prev[:len(m.prev)-1] {
if c < '0' || '9' < c {
isInteger = false
break
}
}
0