1 package minify 2 3 import ( 4 "strconv" 5 "strings" 6 7 "vimagination.zapto.org/javascript" 8 "vimagination.zapto.org/javascript/scope" 9 "vimagination.zapto.org/javascript/walk" 10 ) 11 12 type Minifier struct { 13 Option 14 } 15 16 func New(opts ...Option) *Minifier { 17 m := new(Minifier) 18 if len(opts) == 0 { 19 m.Option = Safe 20 } 21 for _, opt := range opts { 22 m.Option |= opt 23 } 24 return m 25 } 26 27 type walker struct { 28 *Minifier 29 } 30 31 func (w *walker) Handle(t javascript.Type) error { 32 if err := walk.Walk(t, w); err != nil { 33 return err 34 } 35 switch t := t.(type) { 36 case *javascript.TemplateLiteral: 37 w.minifyTemplates(t) 38 case *javascript.PrimaryExpression: 39 w.minifyLiterals(t) 40 w.minifyNonHoistableNames(t) 41 case *javascript.ArrowFunction: 42 w.minifyArrowFunc(t) 43 w.minifyLastReturnStatementInArrowFn(t) 44 w.fixFirstArrowFuncExpression(t) 45 case *javascript.Statement: 46 w.minifyBlockToStatement(t) 47 w.minifyIfToConditional(t) 48 w.removeDebugger(t) 49 case *javascript.PropertyName: 50 w.minifyObjectKeys(t) 51 case *javascript.AssignmentExpression: 52 w.minifyFunctionExpressionAsArrowFunc(t) 53 w.minifyAEParens(t) 54 case *javascript.ParenthesizedExpression: 55 w.minifyParenthsizedExpressionParens(t) 56 case *javascript.Expression: 57 w.minifyExpressionParens(t) 58 case *javascript.Argument: 59 w.minifyArgumentParens(t) 60 case *javascript.MemberExpression: 61 w.minifyMemberExpressionParens(t) 62 case *javascript.CallExpression: 63 w.minifyCallExpressionParens(t) 64 case *javascript.LeftHandSideExpression: 65 w.minifyLHSExpressionParens(t) 66 case *javascript.Block: 67 w.minifyRemoveDeadCode(t) 68 blockAsModule(t, w.minifyEmptyStatement) 69 blockAsModule(t, w.minifyExpressionRun) 70 blockAsModule(t, w.fixFirstExpression) 71 blockAsModule(t, w.minifyLexical) 72 blockAsModule(t, w.minifyExpressionsBetweenLexicals) 73 case *javascript.Module: 74 w.minifyEmptyStatement(t) 75 w.minifyExpressionRun(t) 76 w.fixFirstExpression(t) 77 w.minifyLexical(t) 78 w.minifyExpressionsBetweenLexicals(t) 79 case *javascript.FunctionDeclaration: 80 w.minifyLastReturnStatement(t) 81 case *javascript.ConditionalExpression: 82 w.minifyConditionExpressionParens(t) 83 } 84 85 return nil 86 } 87 88 func (m *Minifier) Process(jm *javascript.Module) { 89 if m.Has(RemoveDeadCode) { 90 removeDeadCode(jm) 91 } 92 walk.Walk(jm, &walker{Minifier: m}) 93 94 if m.Has(RenameIdentifiers) { 95 renameIdentifiers(jm) 96 } 97 } 98 99 func minifyTemplate(t *javascript.Token) { 100 if t != nil { 101 str, err := javascript.UnquoteTemplate(t.Data) 102 if err != nil { 103 return 104 } 105 res := javascript.QuoteTemplate(str, javascript.TokenTypeToTemplateType(t.Type)) 106 if len(res) < len(t.Data) { 107 t.Data = res 108 } 109 } 110 } 111 112 func (m *Minifier) minifyTemplates(t *javascript.TemplateLiteral) { 113 if m.Has(Literals) { 114 minifyTemplate(t.NoSubstitutionTemplate) 115 minifyTemplate(t.TemplateHead) 116 for _, m := range t.TemplateMiddleList { 117 minifyTemplate(m) 118 } 119 minifyTemplate(t.TemplateTail) 120 } 121 } 122 123 func (m *Minifier) minifyLiterals(pe *javascript.PrimaryExpression) { 124 if m.Has(Literals) { 125 if pe.Literal != nil { 126 switch pe.Literal.Type { 127 case javascript.TokenBooleanLiteral: 128 switch pe.Literal.Data { 129 case "true": 130 pe.Literal.Data = "!0" 131 case "false": 132 pe.Literal.Data = "!1" 133 } 134 case javascript.TokenStringLiteral: 135 str, err := javascript.Unquote(pe.Literal.Data) 136 if err != nil { 137 return 138 } 139 tmpl := javascript.QuoteTemplate(str, javascript.TemplateNoSubstitution) 140 if len(tmpl) < len(pe.Literal.Data) { 141 pe.TemplateLiteral = &javascript.TemplateLiteral{ 142 NoSubstitutionTemplate: pe.Literal, 143 Tokens: pe.Tokens, 144 } 145 pe.Literal = nil 146 pe.TemplateLiteral.NoSubstitutionTemplate.Type = javascript.TokenNoSubstitutionTemplate 147 pe.TemplateLiteral.NoSubstitutionTemplate.Data = tmpl 148 } 149 case javascript.TokenNumericLiteral: 150 minifyNumbers(pe.Literal) 151 } 152 } else if pe.IdentifierReference != nil && pe.IdentifierReference.Data == "undefined" { 153 pe.IdentifierReference.Data = "void 0" 154 } else if pe.TemplateLiteral != nil && pe.TemplateLiteral.NoSubstitutionTemplate != nil { 155 str, err := javascript.UnquoteTemplate(pe.TemplateLiteral.NoSubstitutionTemplate.Data) 156 if err != nil { 157 return 158 } 159 asStrLit := strconv.Quote(str) 160 if len(asStrLit) < len(pe.TemplateLiteral.NoSubstitutionTemplate.Data) { 161 tk := pe.TemplateLiteral.NoSubstitutionTemplate 162 tk.Data = asStrLit 163 tk.Type = javascript.TokenStringLiteral 164 pe.TemplateLiteral = nil 165 pe.Literal = tk 166 } 167 } 168 } 169 } 170 171 func minifyNumbers(nt *javascript.Token) { 172 d := nt.Data 173 d = strings.ReplaceAll(d, "_", "") 174 if len(d) < len(nt.Data) { 175 nt.Data = d 176 } 177 if !strings.HasSuffix(d, "n") { 178 var n float64 179 if strings.HasPrefix(d, "0o") || strings.HasPrefix(d, "0O") { 180 h, err := strconv.ParseUint(d[2:], 8, 64) 181 if err != nil { 182 return 183 } 184 n = float64(h) 185 } else if strings.HasPrefix(d, "0b") || strings.HasPrefix(d, "0B") { 186 h, err := strconv.ParseUint(d[2:], 2, 64) 187 if err != nil { 188 return 189 } 190 n = float64(h) 191 } else if strings.HasPrefix(d, "0x") || strings.HasPrefix(d, "0X") { 192 h, err := strconv.ParseUint(d[2:], 16, 64) 193 if err != nil { 194 return 195 } 196 n = float64(h) 197 } else { 198 f, err := strconv.ParseFloat(nt.Data, 64) 199 if err != nil { 200 return 201 } 202 n = f 203 } 204 d = strconv.FormatFloat(n, 'f', -1, 64) 205 if strings.HasSuffix(d, "000") { 206 var e uint64 207 for strings.HasSuffix(d, "0") { 208 d = d[:len(d)-1] 209 e++ 210 } 211 d += "e" + strconv.FormatUint(e, 10) 212 } else if strings.HasPrefix(d, "0.00") { 213 for strings.HasSuffix(d, "0") { 214 d = d[:len(d)-1] 215 } 216 d = d[2:] 217 e := uint64(len(d)) 218 for strings.HasPrefix(d, "0") { 219 d = d[1:] 220 } 221 d = d + "e-" + strconv.FormatUint(e, 10) 222 } else if !strings.Contains(d, ".") && n >= 999999999999 { 223 d = "0x" + strconv.FormatUint(uint64(n), 16) 224 } else { 225 d = strconv.FormatFloat(n, 'f', -1, 64) 226 } 227 } 228 if len(d) < len(nt.Data) { 229 nt.Data = d 230 } 231 } 232 233 func (m *Minifier) minifyArrowFunc(af *javascript.ArrowFunction) { 234 if m.Has(ArrowFn) { 235 if af.FormalParameters != nil && len(af.FormalParameters.FormalParameterList) == 1 && af.FormalParameters.ArrayBindingPattern == nil && af.FormalParameters.ObjectBindingPattern == nil && af.FormalParameters.BindingIdentifier == nil { 236 if fp := af.FormalParameters.FormalParameterList[0]; fp.Initializer == nil && fp.SingleNameBinding != nil && fp.ArrayBindingPattern == nil && fp.ObjectBindingPattern == nil { 237 af.BindingIdentifier = fp.SingleNameBinding 238 af.FormalParameters = nil 239 } 240 } 241 if af.FunctionBody != nil { 242 if af.FormalParameters != nil { 243 if len(af.FormalParameters.FormalParameterList) == 1 && af.FormalParameters.FormalParameterList[0].SingleNameBinding != nil && af.FormalParameters.FormalParameterList[0].Initializer == nil && af.FormalParameters.BindingIdentifier == nil && af.FormalParameters.ArrayBindingPattern == nil && af.FormalParameters.ObjectBindingPattern == nil { 244 af.BindingIdentifier = af.FormalParameters.FormalParameterList[0].SingleNameBinding 245 af.FormalParameters = nil 246 } 247 } 248 expressions, hasReturn := statementsListItemsAsExpressionsAndReturn(af.FunctionBody.StatementList) 249 if hasReturn { 250 if len(expressions) == 1 { 251 af.FunctionBody = nil 252 af.AssignmentExpression = &expressions[0] 253 } else if len(expressions) != 0 { 254 af.AssignmentExpression = &javascript.AssignmentExpression{ 255 ConditionalExpression: javascript.WrapConditional(&javascript.ParenthesizedExpression{ 256 Expressions: expressions, 257 Tokens: af.FunctionBody.Tokens, 258 }), 259 Tokens: af.FunctionBody.Tokens, 260 } 261 af.FunctionBody = nil 262 } 263 } 264 } 265 } 266 } 267 268 func (m *Minifier) minifyIfToConditional(s *javascript.Statement) { 269 if m.Has(IfToConditional) && s.IfStatement != nil && s.IfStatement.ElseStatement != nil { 270 last := s.IfStatement.Expression.Expressions[len(s.IfStatement.Expression.Expressions)-1] 271 if last.AssignmentOperator != javascript.AssignmentNone || last.ConditionalExpression == nil || last.ArrowFunction != nil || last.AssignmentExpression != nil || last.AssignmentPattern != nil || last.Delegate || last.LeftHandSideExpression != nil || last.Yield { 272 return 273 } 274 var ( 275 ifExpressions, elseExpressions []javascript.AssignmentExpression 276 ifReturn, elseReturn bool 277 ) 278 if isNonEmptyReturnStatement(&s.IfStatement.Statement) { 279 ifReturn = true 280 ifExpressions = s.IfStatement.Statement.ExpressionStatement.Expressions 281 } else if isStatementExpression(&s.IfStatement.Statement) { 282 ifExpressions = s.IfStatement.Statement.ExpressionStatement.Expressions 283 } else if s.IfStatement.Statement.BlockStatement != nil { 284 ifExpressions, ifReturn = statementsListItemsAsExpressionsAndReturn(s.IfStatement.Statement.BlockStatement.StatementList) 285 } 286 if len(ifExpressions) == 0 { 287 return 288 } 289 if isNonEmptyReturnStatement(s.IfStatement.ElseStatement) { 290 elseReturn = true 291 elseExpressions = s.IfStatement.ElseStatement.ExpressionStatement.Expressions 292 } else if isStatementExpression(s.IfStatement.ElseStatement) { 293 elseExpressions = s.IfStatement.ElseStatement.ExpressionStatement.Expressions 294 } else if s.IfStatement.ElseStatement.BlockStatement != nil { 295 elseExpressions, elseReturn = statementsListItemsAsExpressionsAndReturn(s.IfStatement.ElseStatement.BlockStatement.StatementList) 296 } 297 if ifReturn != elseReturn { 298 return 299 } 300 if len(elseExpressions) == 0 { 301 return 302 } else if len(elseExpressions) == 1 { 303 last.ConditionalExpression.False = &elseExpressions[0] 304 } else { 305 last.ConditionalExpression.False = &javascript.AssignmentExpression{ 306 ConditionalExpression: javascript.WrapConditional(&javascript.ParenthesizedExpression{ 307 Expressions: elseExpressions, 308 Tokens: s.IfStatement.ElseStatement.Tokens, 309 }), 310 Tokens: s.IfStatement.ElseStatement.Tokens, 311 } 312 } 313 if len(ifExpressions) == 1 { 314 last.ConditionalExpression.True = &ifExpressions[0] 315 } else { 316 last.ConditionalExpression.True = &javascript.AssignmentExpression{ 317 ConditionalExpression: javascript.WrapConditional(&javascript.ParenthesizedExpression{ 318 Expressions: ifExpressions, 319 Tokens: s.IfStatement.Statement.Tokens, 320 }), 321 Tokens: s.IfStatement.Statement.Tokens, 322 } 323 } 324 if ifReturn { 325 s.Type = javascript.StatementReturn 326 } 327 s.ExpressionStatement = &s.IfStatement.Expression 328 s.IfStatement = nil 329 } 330 } 331 332 func (m *Minifier) removeDebugger(s *javascript.Statement) { 333 if m.Has(RemoveDebugger) && s.Type == javascript.StatementDebugger { 334 s.Type = javascript.StatementNormal 335 } 336 } 337 338 func (m *Minifier) minifyBlockToStatement(s *javascript.Statement) { 339 if m.Has(BlocksToStatement) && s.BlockStatement != nil { 340 if l := len(s.BlockStatement.StatementList); l == 1 { 341 if s.BlockStatement.StatementList[0].Statement != nil { 342 *s = *s.BlockStatement.StatementList[0].Statement 343 } 344 } else if l > 1 { 345 if expressions, hasReturn := statementsListItemsAsExpressionsAndReturn(s.BlockStatement.StatementList); !hasReturn && len(expressions) > 0 { 346 *s = javascript.Statement{ 347 ExpressionStatement: &javascript.Expression{ 348 Expressions: expressions, 349 Tokens: s.Tokens, 350 }, 351 Tokens: s.Tokens, 352 } 353 } 354 } 355 } 356 } 357 358 func (m *Minifier) minifyObjectKeys(p *javascript.PropertyName) { 359 if m.Has(Keys) { 360 if ae := p.ComputedPropertyName; ae != nil && ae.AssignmentOperator == javascript.AssignmentNone && ae.ConditionalExpression != nil && !ae.Yield { 361 pe, ok := javascript.UnwrapConditional(ae.ConditionalExpression).(*javascript.PrimaryExpression) 362 if ok && pe.Literal != nil && pe.Literal.Type != javascript.TokenRegularExpressionLiteral { 363 p.LiteralPropertyName = pe.Literal 364 p.ComputedPropertyName = nil 365 } 366 } 367 if p.LiteralPropertyName != nil && p.LiteralPropertyName.Type == javascript.TokenStringLiteral { 368 key, err := javascript.Unquote(p.LiteralPropertyName.Data) 369 if err == nil { 370 if isIdentifier(key) { 371 p.LiteralPropertyName.Data = key 372 p.LiteralPropertyName.Type = javascript.TokenIdentifier // This type may not be technically correct, but should not matter. 373 } else if isSimpleNumber(key) { 374 p.LiteralPropertyName.Data = key 375 p.LiteralPropertyName.Type = javascript.TokenNumericLiteral 376 } 377 } 378 } 379 } 380 } 381 382 func (m *Minifier) minifyNonHoistableNames(pe *javascript.PrimaryExpression) { 383 if m.Has(RemoveExpressionNames) { 384 if pe.FunctionExpression != nil { 385 pe.FunctionExpression.BindingIdentifier = nil 386 } else if pe.ClassExpression != nil { 387 pe.ClassExpression.BindingIdentifier = nil 388 } 389 } 390 } 391 392 func (m *Minifier) minifyFunctionExpressionAsArrowFunc(ae *javascript.AssignmentExpression) { 393 if m.Has(FunctionExpressionToArrowFunc) && ae.AssignmentOperator == javascript.AssignmentNone && ae.ConditionalExpression != nil { 394 if fe, ok := javascript.UnwrapConditional(ae.ConditionalExpression).(*javascript.FunctionDeclaration); ok && (fe.Type == javascript.FunctionAsync || fe.Type == javascript.FunctionNormal) { 395 s, err := scope.ScriptScope(&javascript.Script{ 396 StatementList: fe.FunctionBody.StatementList, 397 }, nil) 398 if err != nil { 399 return 400 } 401 _, hasArguments := s.Bindings["arguments"] 402 _, hasThis := s.Bindings["this"] 403 if hasArguments || hasThis { 404 return 405 } 406 ae.ArrowFunction = &javascript.ArrowFunction{ 407 Async: fe.Type == javascript.FunctionAsync, 408 FormalParameters: &fe.FormalParameters, 409 FunctionBody: &fe.FunctionBody, 410 Tokens: fe.Tokens, 411 } 412 ae.ConditionalExpression = nil 413 m.minifyArrowFunc(ae.ArrowFunction) 414 } 415 } 416 } 417 418 func (m *Minifier) minifyExpressionParens(e *javascript.Expression) { 419 if m.Has(UnwrapParens) { 420 e.Expressions = m.minifyParens(e.Expressions) 421 } 422 } 423 424 func (m *Minifier) minifyParenthsizedExpressionParens(pe *javascript.ParenthesizedExpression) { 425 if m.Has(UnwrapParens) { 426 pe.Expressions = m.minifyParens(pe.Expressions) 427 } 428 } 429 430 func (w *Minifier) minifyParens(e []javascript.AssignmentExpression) []javascript.AssignmentExpression { 431 for i := 0; i < len(e); i++ { 432 if pe := aeAsParen(&e[i]); pe != nil { 433 add := make([]javascript.AssignmentExpression, 0, len(pe.Expressions)+len(e)-i-1) 434 add = append(add, pe.Expressions...) 435 add = append(add, e[i+1:]...) 436 e = append(e[:i], add...) 437 i += len(pe.Expressions) - 1 438 } 439 } 440 return e 441 } 442 443 func (m *Minifier) minifyArgumentParens(a *javascript.Argument) { 444 if m.Has(UnwrapParens) { 445 if pe := aeAsParen(&a.AssignmentExpression); pe != nil && len(pe.Expressions) == 1 { 446 a.AssignmentExpression = pe.Expressions[0] 447 } 448 } 449 } 450 451 func (m *Minifier) minifyAEParens(ae *javascript.AssignmentExpression) { 452 if m.Has(UnwrapParens) { 453 if pe := aeAsParen(ae.AssignmentExpression); pe != nil && len(pe.Expressions) == 1 { 454 ae.AssignmentExpression = &pe.Expressions[0] 455 } 456 } 457 } 458 459 func (m *Minifier) minifyMemberExpressionParens(me *javascript.MemberExpression) { 460 if m.Has(UnwrapParens) && meIsSinglePe(me) { 461 switch e := javascript.UnwrapConditional(me.PrimaryExpression.ParenthesizedExpression.Expressions[0].ConditionalExpression).(type) { 462 case *javascript.PrimaryExpression: 463 me.PrimaryExpression = e 464 case *javascript.ArrayLiteral: 465 me.PrimaryExpression = &javascript.PrimaryExpression{ 466 ArrayLiteral: e, 467 Tokens: e.Tokens, 468 } 469 case *javascript.ObjectLiteral: 470 me.PrimaryExpression = &javascript.PrimaryExpression{ 471 ObjectLiteral: e, 472 Tokens: e.Tokens, 473 } 474 case *javascript.FunctionDeclaration: 475 me.PrimaryExpression = &javascript.PrimaryExpression{ 476 FunctionExpression: e, 477 Tokens: e.Tokens, 478 } 479 case *javascript.ClassDeclaration: 480 me.PrimaryExpression = &javascript.PrimaryExpression{ 481 ClassExpression: e, 482 Tokens: e.Tokens, 483 } 484 case *javascript.TemplateLiteral: 485 me.PrimaryExpression = &javascript.PrimaryExpression{ 486 TemplateLiteral: e, 487 Tokens: e.Tokens, 488 } 489 case *javascript.MemberExpression: 490 *me = *e 491 } 492 } 493 } 494 495 func (m *Minifier) minifyCallExpressionParens(ce *javascript.CallExpression) { 496 if m.Has(UnwrapParens) && meIsSinglePe(ce.MemberExpression) { 497 switch e := javascript.UnwrapConditional(ce.MemberExpression.PrimaryExpression.ParenthesizedExpression.Expressions[0].ConditionalExpression).(type) { 498 case *javascript.CallExpression: 499 ce.CallExpression = e 500 ce.MemberExpression = nil 501 } 502 } 503 } 504 505 func (m *Minifier) minifyConditionExpressionParens(ce *javascript.ConditionalExpression) { 506 if m.Has(UnwrapParens) { 507 w := javascript.UnwrapConditional(ce) 508 switch w := w.(type) { 509 case *javascript.LogicalORExpression: 510 if ce := isConditionalWrappingAConditional(w.LogicalORExpression, w); ce != nil { 511 w.LogicalORExpression = ce.LogicalORExpression 512 } 513 case *javascript.LogicalANDExpression: 514 if ce := isConditionalWrappingAConditional(w.LogicalANDExpression, w); ce != nil { 515 w.LogicalANDExpression = &ce.LogicalORExpression.LogicalANDExpression 516 } 517 case *javascript.BitwiseORExpression: 518 if ce := isConditionalWrappingAConditional(w.BitwiseORExpression, w); ce != nil { 519 w.BitwiseORExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression 520 } 521 case *javascript.BitwiseXORExpression: 522 if ce := isConditionalWrappingAConditional(w.BitwiseXORExpression, w); ce != nil { 523 w.BitwiseXORExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression.BitwiseXORExpression 524 } 525 case *javascript.BitwiseANDExpression: 526 if ce := isConditionalWrappingAConditional(w.BitwiseANDExpression, w); ce != nil { 527 w.BitwiseANDExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression.BitwiseXORExpression.BitwiseANDExpression 528 } 529 case *javascript.EqualityExpression: 530 if ce := isConditionalWrappingAConditional(w.EqualityExpression, w); ce != nil { 531 w.EqualityExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression.BitwiseXORExpression.BitwiseANDExpression.EqualityExpression 532 } 533 case *javascript.RelationalExpression: 534 if ce := isConditionalWrappingAConditional(w.RelationalExpression, w); ce != nil { 535 w.RelationalExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression.BitwiseXORExpression.BitwiseANDExpression.EqualityExpression.RelationalExpression 536 } 537 case *javascript.ShiftExpression: 538 if ce := isConditionalWrappingAConditional(w.ShiftExpression, w); ce != nil { 539 w.ShiftExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression.BitwiseXORExpression.BitwiseANDExpression.EqualityExpression.RelationalExpression.ShiftExpression 540 } 541 case *javascript.AdditiveExpression: 542 if ce := isConditionalWrappingAConditional(w.AdditiveExpression, w); ce != nil { 543 w.AdditiveExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression.BitwiseXORExpression.BitwiseANDExpression.EqualityExpression.RelationalExpression.ShiftExpression.AdditiveExpression 544 } 545 case *javascript.MultiplicativeExpression: 546 if ce := isConditionalWrappingAConditional(w.MultiplicativeExpression, w); ce != nil { 547 w.MultiplicativeExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression.BitwiseXORExpression.BitwiseANDExpression.EqualityExpression.RelationalExpression.ShiftExpression.AdditiveExpression.MultiplicativeExpression 548 } 549 case *javascript.ExponentiationExpression: 550 if ce := isConditionalWrappingAConditional(w.ExponentiationExpression, w); ce != nil { 551 w.ExponentiationExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression.BitwiseXORExpression.BitwiseANDExpression.EqualityExpression.RelationalExpression.ShiftExpression.AdditiveExpression.MultiplicativeExpression.ExponentiationExpression 552 } 553 case *javascript.UpdateExpression: 554 if w.UnaryExpression != nil { 555 if ce := isConditionalWrappingAConditional(w.UnaryExpression, w); ce != nil { 556 w.UnaryExpression = &ce.LogicalORExpression.LogicalANDExpression.BitwiseORExpression.BitwiseXORExpression.BitwiseANDExpression.EqualityExpression.RelationalExpression.ShiftExpression.AdditiveExpression.MultiplicativeExpression.ExponentiationExpression.UnaryExpression 557 } 558 } 559 } 560 } 561 } 562 563 func (m *Minifier) minifyLHSExpressionParens(lhs *javascript.LeftHandSideExpression) { 564 if m.Has(UnwrapParens) && lhs.NewExpression != nil && lhs.NewExpression.News == 0 { 565 ce := meAsCE(&lhs.NewExpression.MemberExpression) 566 if ce != nil { 567 lhs.CallExpression = ce 568 lhs.NewExpression = nil 569 } 570 } 571 } 572 573 func (m *Minifier) minifyEmptyStatement(jm *javascript.Module) { 574 for i := 0; i < len(jm.ModuleListItems); i++ { 575 if jm.ModuleListItems[i].StatementListItem != nil && isEmptyStatement(jm.ModuleListItems[i].StatementListItem.Statement) { 576 jm.ModuleListItems = append(jm.ModuleListItems[:i], jm.ModuleListItems[i+1:]...) 577 i-- 578 } 579 } 580 } 581 582 func (m *Minifier) minifyLastReturnStatement(f *javascript.FunctionDeclaration) { 583 if m.Has(RemoveLastEmptyReturn) { 584 removeLastReturnStatement(&f.FunctionBody) 585 } 586 } 587 588 func (m *Minifier) minifyLastReturnStatementInArrowFn(af *javascript.ArrowFunction) { 589 if m.Has(RemoveLastEmptyReturn) && af.FunctionBody != nil { 590 removeLastReturnStatement(af.FunctionBody) 591 } 592 } 593 594 func (m *Minifier) minifyExpressionRun(jm *javascript.Module) { 595 if m.Has(CombineExpressionRuns) && len(jm.ModuleListItems) > 1 { 596 lastWasExpression := isStatementListItemExpression(jm.ModuleListItems[0].StatementListItem) 597 for i := 1; i < len(jm.ModuleListItems); i++ { 598 isExpression := isStatementListItemExpression(jm.ModuleListItems[i].StatementListItem) 599 if isExpression && lastWasExpression { 600 e := jm.ModuleListItems[i-1].StatementListItem.Statement.ExpressionStatement 601 e.Expressions = append(e.Expressions, jm.ModuleListItems[i].StatementListItem.Statement.ExpressionStatement.Expressions...) 602 jm.ModuleListItems = append(jm.ModuleListItems[:i], jm.ModuleListItems[i+1:]...) 603 i-- 604 } else { 605 lastWasExpression = isExpression 606 } 607 } 608 } 609 } 610 611 func (m *Minifier) fixFirstExpression(jm *javascript.Module) { 612 if m.Has(UnwrapParens) { 613 for n := range jm.ModuleListItems { 614 if isStatementListItemExpression(jm.ModuleListItems[n].StatementListItem) { 615 fixWrapping(jm.ModuleListItems[n].StatementListItem.Statement) 616 } 617 } 618 } 619 } 620 621 func (m *Minifier) fixFirstArrowFuncExpression(af *javascript.ArrowFunction) { 622 if m.Has(UnwrapParens) && af.AssignmentExpression != nil { 623 fixWrapping(&javascript.Statement{ 624 ExpressionStatement: &javascript.Expression{ 625 Expressions: []javascript.AssignmentExpression{*af.AssignmentExpression}, 626 }, 627 }) 628 } 629 } 630 631 func (m *Minifier) minifyRemoveDeadCode(b *javascript.Block) { 632 if m.Has(RemoveDeadCode) { 633 retPos := -1 634 for n := range b.StatementList { 635 if isReturnStatement(b.StatementList[n].Statement) { 636 retPos = n 637 break 638 } 639 } 640 if retPos >= 0 { 641 for i := retPos + 1; i < len(b.StatementList); i++ { 642 if !isHoistable(&b.StatementList[i]) { 643 b.StatementList = append(b.StatementList[:i], b.StatementList[i+1:]...) 644 i-- 645 } 646 } 647 } 648 } 649 } 650 651 func (m *Minifier) minifyLexical(jm *javascript.Module) { 652 if m.Has(MergeLexical) { 653 last := bindableNone 654 for i := 0; i < len(jm.ModuleListItems); i++ { 655 next := sliBindable(jm.ModuleListItems[i].StatementListItem) 656 if last == next { 657 switch next { 658 case bindableConst, bindableLet: 659 ld := jm.ModuleListItems[i-1].StatementListItem.Declaration.LexicalDeclaration 660 ld.BindingList = append(ld.BindingList, jm.ModuleListItems[i].StatementListItem.Declaration.LexicalDeclaration.BindingList...) 661 jm.ModuleListItems = append(jm.ModuleListItems[:i], jm.ModuleListItems[i+1:]...) 662 i-- 663 case bindableVar: 664 vs := jm.ModuleListItems[i-1].StatementListItem.Statement.VariableStatement 665 vs.VariableDeclarationList = append(vs.VariableDeclarationList, jm.ModuleListItems[i].StatementListItem.Statement.VariableStatement.VariableDeclarationList...) 666 jm.ModuleListItems = append(jm.ModuleListItems[:i], jm.ModuleListItems[i+1:]...) 667 i-- 668 } 669 } 670 last = next 671 } 672 } 673 } 674 675 func (m *Minifier) minifyExpressionsBetweenLexicals(jm *javascript.Module) { 676 if m.Has(MergeLexical) && m.Has(CombineExpressionRuns) { 677 for i := 2; i < len(jm.ModuleListItems); i++ { 678 if last := sliBindable(jm.ModuleListItems[i-2].StatementListItem); (last == bindableLet || last == bindableConst || last == bindableVar) && isStatementListItemExpression(jm.ModuleListItems[i-1].StatementListItem) && sliBindable(jm.ModuleListItems[i].StatementListItem) == last { 679 var flbs, lbs []javascript.LexicalBinding 680 if last == bindableVar { 681 flbs = jm.ModuleListItems[i-2].StatementListItem.Statement.VariableStatement.VariableDeclarationList 682 lbs = jm.ModuleListItems[i].StatementListItem.Statement.VariableStatement.VariableDeclarationList 683 } else { 684 flbs = jm.ModuleListItems[i-2].StatementListItem.Declaration.LexicalDeclaration.BindingList 685 lbs = jm.ModuleListItems[i].StatementListItem.Declaration.LexicalDeclaration.BindingList 686 } 687 back := 0 688 for n := range lbs { 689 if pe := aeAsParen(lbs[n].Initializer); pe != nil { 690 pe.Expressions = append(jm.ModuleListItems[i-1].StatementListItem.Statement.ExpressionStatement.Expressions, pe.Expressions...) 691 back = 1 692 break 693 } else if !isSimpleAE(lbs[n].Initializer) { 694 lbs[n].Initializer = &javascript.AssignmentExpression{ 695 ConditionalExpression: javascript.WrapConditional(&javascript.ParenthesizedExpression{ 696 Expressions: append(jm.ModuleListItems[i-1].StatementListItem.Statement.ExpressionStatement.Expressions, *lbs[n].Initializer), 697 }), 698 } 699 back = 1 700 break 701 } 702 } 703 flbs = append(flbs, lbs...) 704 if last == bindableVar { 705 jm.ModuleListItems[i-2].StatementListItem.Statement.VariableStatement.VariableDeclarationList = flbs 706 } else { 707 jm.ModuleListItems[i-2].StatementListItem.Declaration.LexicalDeclaration.BindingList = flbs 708 } 709 jm.ModuleListItems = append(jm.ModuleListItems[:i-back], jm.ModuleListItems[i+1:]...) 710 i-- 711 } 712 } 713 } 714 } 715