terser 5.3.6 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -324,6 +324,9 @@ function set_annotation(node, annotation) {
324
324
 
325
325
  ***********************************************************************/
326
326
 
327
+ var LATEST_RAW = ""; // Only used for numbers and template strings
328
+ var LATEST_TEMPLATE_END = true;
329
+
327
330
  var KEYWORDS = "break case catch class const continue debugger default delete do else export extends finally for function if in instanceof let new return switch throw try typeof var void while with";
328
331
  var KEYWORDS_ATOM = "false null true";
329
332
  var RESERVED_WORDS = "enum implements import interface package private protected public static super this " + KEYWORDS_ATOM + " " + KEYWORDS;
@@ -379,6 +382,9 @@ var OPERATORS = makePredicate([
379
382
  "=",
380
383
  "+=",
381
384
  "-=",
385
+ "||=",
386
+ "&&=",
387
+ "??=",
382
388
  "/=",
383
389
  "*=",
384
390
  "**=",
@@ -639,29 +645,23 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
639
645
  } else if (!is_comment) {
640
646
  prev_was_dot = false;
641
647
  }
642
- var ret = {
643
- type : type,
644
- value : value,
645
- line : S.tokline,
646
- col : S.tokcol,
647
- pos : S.tokpos,
648
- endline : S.line,
649
- endcol : S.col,
650
- endpos : S.pos,
651
- nlb : S.newline_before,
652
- file : filename
653
- };
654
- if (/^(?:num|string|regexp)$/i.test(type)) {
655
- ret.raw = $TEXT.substring(ret.pos, ret.endpos);
656
- }
648
+ const line = S.tokline;
649
+ const col = S.tokcol;
650
+ const pos = S.tokpos;
651
+ const nlb = S.newline_before;
652
+ const file = filename;
653
+ let comments_before = [];
654
+ let comments_after = [];
655
+
657
656
  if (!is_comment) {
658
- ret.comments_before = S.comments_before;
659
- ret.comments_after = S.comments_before = [];
657
+ comments_before = S.comments_before;
658
+ comments_after = S.comments_before = [];
660
659
  }
661
660
  S.newline_before = false;
662
- ret = new AST_Token(ret);
663
- if (!is_comment) previous_token = ret;
664
- return ret;
661
+ const tok = new AST_Token(type, value, line, col, pos, nlb, comments_before, comments_after, file);
662
+
663
+ if (!is_comment) previous_token = tok;
664
+ return tok;
665
665
  }
666
666
 
667
667
  function skip_whitespace() {
@@ -713,6 +713,9 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
713
713
  return RE_NUM_LITERAL.test(ch);
714
714
  });
715
715
  if (prefix) num = prefix + num;
716
+
717
+ LATEST_RAW = num;
718
+
716
719
  if (RE_OCT_NUMBER.test(num) && next_token.has_directive("use strict")) {
717
720
  parse_error("Legacy octal literals are not allowed in strict mode");
718
721
  }
@@ -820,15 +823,17 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
820
823
  }
821
824
 
822
825
  var read_string = with_eof_error("Unterminated string constant", function() {
823
- var quote = next(), ret = "";
826
+ const start_pos = S.pos;
827
+ var quote = next(), ret = [];
824
828
  for (;;) {
825
829
  var ch = next(true, true);
826
830
  if (ch == "\\") ch = read_escaped_char(true, true);
827
831
  else if (ch == "\r" || ch == "\n") parse_error("Unterminated string constant");
828
832
  else if (ch == quote) break;
829
- ret += ch;
833
+ ret.push(ch);
830
834
  }
831
- var tok = token("string", ret);
835
+ var tok = token("string", ret.join(""));
836
+ LATEST_RAW = S.text.slice(start_pos, S.pos);
832
837
  tok.quote = quote;
833
838
  return tok;
834
839
  });
@@ -847,7 +852,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
847
852
  next(true, true);
848
853
  S.brace_counter++;
849
854
  tok = token(begin ? "template_head" : "template_substitution", content);
850
- tok.raw = raw;
855
+ LATEST_RAW = raw;
856
+ LATEST_TEMPLATE_END = false;
851
857
  return tok;
852
858
  }
853
859
 
@@ -863,8 +869,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
863
869
  }
864
870
  S.template_braces.pop();
865
871
  tok = token(begin ? "template_head" : "template_substitution", content);
866
- tok.raw = raw;
867
- tok.end = true;
872
+ LATEST_RAW = raw;
873
+ LATEST_TEMPLATE_END = true;
868
874
  return tok;
869
875
  });
870
876
 
@@ -897,7 +903,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
897
903
  });
898
904
 
899
905
  var read_name = with_eof_error("Unterminated identifier name", function() {
900
- var name, ch, escaped = false;
906
+ var name = [], ch, escaped = false;
901
907
  var read_escaped_identifier_char = function() {
902
908
  escaped = true;
903
909
  next();
@@ -908,17 +914,19 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
908
914
  };
909
915
 
910
916
  // Read first character (ID_Start)
911
- if ((name = peek()) === "\\") {
912
- name = read_escaped_identifier_char();
913
- if (!is_identifier_start(name)) {
917
+ if ((ch = peek()) === "\\") {
918
+ ch = read_escaped_identifier_char();
919
+ if (!is_identifier_start(ch)) {
914
920
  parse_error("First identifier char is an invalid identifier char");
915
921
  }
916
- } else if (is_identifier_start(name)) {
922
+ } else if (is_identifier_start(ch)) {
917
923
  next();
918
924
  } else {
919
925
  return "";
920
926
  }
921
927
 
928
+ name.push(ch);
929
+
922
930
  // Read ID_Continue
923
931
  while ((ch = peek()) != null) {
924
932
  if ((ch = peek()) === "\\") {
@@ -932,12 +940,13 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
932
940
  }
933
941
  next();
934
942
  }
935
- name += ch;
943
+ name.push(ch);
936
944
  }
937
- if (RESERVED_WORDS.has(name) && escaped) {
945
+ const name_str = name.join("");
946
+ if (RESERVED_WORDS.has(name_str) && escaped) {
938
947
  parse_error("Escaped characters are not allowed in keywords");
939
948
  }
940
- return name;
949
+ return name_str;
941
950
  });
942
951
 
943
952
  var read_regexp = with_eof_error("Unterminated regular expression", function(source) {
@@ -961,7 +970,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
961
970
  source += ch;
962
971
  }
963
972
  const flags = read_name();
964
- return token("regexp", { source, flags });
973
+ return token("regexp", "/" + source + "/" + flags);
965
974
  });
966
975
 
967
976
  function read_operator(prefix) {
@@ -1154,7 +1163,9 @@ var UNARY_PREFIX = makePredicate([
1154
1163
 
1155
1164
  var UNARY_POSTFIX = makePredicate([ "--", "++" ]);
1156
1165
 
1157
- var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "/=", "*=", "**=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
1166
+ var ASSIGNMENT = makePredicate([ "=", "+=", "-=", "??=", "&&=", "||=", "/=", "*=", "**=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&=" ]);
1167
+
1168
+ var LOGICAL_ASSIGNMENT = makePredicate([ "??=", "&&=", "||=" ]);
1158
1169
 
1159
1170
  var PRECEDENCE = (function(a, ret) {
1160
1171
  for (var i = 0; i < a.length; ++i) {
@@ -1192,7 +1203,7 @@ function parse($TEXT, options) {
1192
1203
  // Useful because comments_before property of call with parens outside
1193
1204
  // contains both comments inside and outside these parens. Used to find the
1194
1205
  // right #__PURE__ comments for an expression
1195
- const outer_comments_before_counts = new Map();
1206
+ const outer_comments_before_counts = new WeakMap();
1196
1207
 
1197
1208
  options = defaults(options, {
1198
1209
  bare_returns : false,
@@ -1326,7 +1337,7 @@ function parse($TEXT, options) {
1326
1337
  case "string":
1327
1338
  if (S.in_directives) {
1328
1339
  var token = peek();
1329
- if (!S.token.raw.includes("\\")
1340
+ if (!LATEST_RAW.includes("\\")
1330
1341
  && (is_token(token, "punc", ";")
1331
1342
  || is_token(token, "punc", "}")
1332
1343
  || has_newline_before(token)
@@ -2309,7 +2320,12 @@ function parse($TEXT, options) {
2309
2320
  ret = _make_symbol(AST_SymbolRef);
2310
2321
  break;
2311
2322
  case "num":
2312
- ret = new AST_Number({ start: tok, end: tok, value: tok.value });
2323
+ ret = new AST_Number({
2324
+ start: tok,
2325
+ end: tok,
2326
+ value: tok.value,
2327
+ raw: LATEST_RAW
2328
+ });
2313
2329
  break;
2314
2330
  case "big_int":
2315
2331
  ret = new AST_BigInt({ start: tok, end: tok, value: tok.value });
@@ -2323,7 +2339,9 @@ function parse($TEXT, options) {
2323
2339
  });
2324
2340
  break;
2325
2341
  case "regexp":
2326
- ret = new AST_RegExp({ start: tok, end: tok, value: tok.value });
2342
+ const [_, source, flags] = tok.value.match(/^\/(.*)\/(\w*)$/);
2343
+
2344
+ ret = new AST_RegExp({ start: tok, end: tok, value: { source, flags } });
2327
2345
  break;
2328
2346
  case "atom":
2329
2347
  switch (tok.value) {
@@ -2493,22 +2511,19 @@ function parse($TEXT, options) {
2493
2511
 
2494
2512
  segments.push(new AST_TemplateSegment({
2495
2513
  start: S.token,
2496
- raw: S.token.raw,
2514
+ raw: LATEST_RAW,
2497
2515
  value: S.token.value,
2498
2516
  end: S.token
2499
2517
  }));
2500
- while (!S.token.end) {
2518
+
2519
+ while (!LATEST_TEMPLATE_END) {
2501
2520
  next();
2502
2521
  handle_regexp();
2503
2522
  segments.push(expression(true));
2504
2523
 
2505
- if (!is_token("template_substitution")) {
2506
- unexpected();
2507
- }
2508
-
2509
2524
  segments.push(new AST_TemplateSegment({
2510
2525
  start: S.token,
2511
- raw: S.token.raw,
2526
+ raw: LATEST_RAW,
2512
2527
  value: S.token.value,
2513
2528
  end: S.token
2514
2529
  }));
@@ -2602,6 +2617,7 @@ function parse($TEXT, options) {
2602
2617
  left: value,
2603
2618
  operator: "=",
2604
2619
  right: expression(false),
2620
+ logical: false,
2605
2621
  end: prev()
2606
2622
  });
2607
2623
  }
@@ -3370,11 +3386,13 @@ function parse($TEXT, options) {
3370
3386
  if (is("operator") && ASSIGNMENT.has(val)) {
3371
3387
  if (is_assignable(left) || (left = to_destructuring(left)) instanceof AST_Destructuring) {
3372
3388
  next();
3389
+
3373
3390
  return new AST_Assign({
3374
3391
  start : start,
3375
3392
  left : left,
3376
3393
  operator : val,
3377
3394
  right : maybe_assign(no_in),
3395
+ logical : LOGICAL_ASSIGNMENT.has(val),
3378
3396
  end : prev()
3379
3397
  });
3380
3398
  }
@@ -3518,8 +3536,54 @@ function DEFNODE(type, props, methods, base = AST_Node) {
3518
3536
  return ctor;
3519
3537
  }
3520
3538
 
3521
- var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before comments_after file raw quote end", {
3522
- }, null);
3539
+ const has_tok_flag = (tok, flag) => Boolean(tok.flags & flag);
3540
+ const set_tok_flag = (tok, flag, truth) => {
3541
+ if (truth) {
3542
+ tok.flags |= flag;
3543
+ } else {
3544
+ tok.flags &= ~flag;
3545
+ }
3546
+ };
3547
+
3548
+ const TOK_FLAG_NLB = 0b0001;
3549
+ const TOK_FLAG_QUOTE_SINGLE = 0b0010;
3550
+ const TOK_FLAG_QUOTE_EXISTS = 0b0100;
3551
+
3552
+ class AST_Token {
3553
+ constructor(type, value, line, col, pos, nlb, comments_before, comments_after, file) {
3554
+ this.flags = (nlb ? 1 : 0);
3555
+
3556
+ this.type = type;
3557
+ this.value = value;
3558
+ this.line = line;
3559
+ this.col = col;
3560
+ this.pos = pos;
3561
+ this.comments_before = comments_before;
3562
+ this.comments_after = comments_after;
3563
+ this.file = file;
3564
+
3565
+ Object.seal(this);
3566
+ }
3567
+
3568
+ get nlb() {
3569
+ return has_tok_flag(this, TOK_FLAG_NLB);
3570
+ }
3571
+
3572
+ set nlb(new_nlb) {
3573
+ set_tok_flag(this, TOK_FLAG_NLB, new_nlb);
3574
+ }
3575
+
3576
+ get quote() {
3577
+ return !has_tok_flag(this, TOK_FLAG_QUOTE_EXISTS)
3578
+ ? ""
3579
+ : (has_tok_flag(this, TOK_FLAG_QUOTE_SINGLE) ? "'" : '"');
3580
+ }
3581
+
3582
+ set quote(quote_type) {
3583
+ set_tok_flag(this, TOK_FLAG_QUOTE_SINGLE, quote_type === "'");
3584
+ set_tok_flag(this, TOK_FLAG_QUOTE_EXISTS, !!quote_type);
3585
+ }
3586
+ }
3523
3587
 
3524
3588
  var AST_Node = DEFNODE("Node", "start end", {
3525
3589
  _clone: function(deep) {
@@ -3988,7 +4052,7 @@ var AST_TemplateSegment = DEFNODE("TemplateSegment", "value raw", {
3988
4052
  $documentation: "A segment of a template string literal",
3989
4053
  $propdoc: {
3990
4054
  value: "Content of the segment",
3991
- raw: "Raw content of the segment"
4055
+ raw: "Raw source of the segment",
3992
4056
  }
3993
4057
  });
3994
4058
 
@@ -4500,8 +4564,11 @@ var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative",
4500
4564
  },
4501
4565
  });
4502
4566
 
4503
- var AST_Assign = DEFNODE("Assign", null, {
4567
+ var AST_Assign = DEFNODE("Assign", "logical", {
4504
4568
  $documentation: "An assignment expression — `a = b + 5`",
4569
+ $propdoc: {
4570
+ logical: "Whether it's a logical assignment"
4571
+ }
4505
4572
  }, AST_Binary);
4506
4573
 
4507
4574
  var AST_DefaultAssign = DEFNODE("DefaultAssign", null, {
@@ -4792,11 +4859,11 @@ var AST_String = DEFNODE("String", "value quote", {
4792
4859
  }
4793
4860
  }, AST_Constant);
4794
4861
 
4795
- var AST_Number = DEFNODE("Number", "value literal", {
4862
+ var AST_Number = DEFNODE("Number", "value raw", {
4796
4863
  $documentation: "A number literal",
4797
4864
  $propdoc: {
4798
4865
  value: "[number] the numeric value",
4799
- literal: "[string] numeric value as string (optional)"
4866
+ raw: "[string] numeric value as string"
4800
4867
  }
4801
4868
  }, AST_Constant);
4802
4869
 
@@ -5875,6 +5942,7 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
5875
5942
  return new AST_String(args);
5876
5943
  case "number":
5877
5944
  args.value = val;
5945
+ args.raw = M.raw || val.toString();
5878
5946
  return new AST_Number(args);
5879
5947
  case "boolean":
5880
5948
  return new (val ? AST_True : AST_False)(args);
@@ -6399,22 +6467,10 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6399
6467
 
6400
6468
  def_to_moz(AST_Constant, function To_Moz_Literal(M) {
6401
6469
  var value = M.value;
6402
- if (typeof value === "number" && (value < 0 || (value === 0 && 1 / value < 0))) {
6403
- return {
6404
- type: "UnaryExpression",
6405
- operator: "-",
6406
- prefix: true,
6407
- argument: {
6408
- type: "Literal",
6409
- value: -value,
6410
- raw: M.start.raw
6411
- }
6412
- };
6413
- }
6414
6470
  return {
6415
6471
  type: "Literal",
6416
6472
  value: value,
6417
- raw: M.start.raw
6473
+ raw: M.raw || M.print_to_string()
6418
6474
  };
6419
6475
  });
6420
6476
 
@@ -6439,40 +6495,36 @@ def_transform(AST_PrefixedTemplateString, function(self, tw) {
6439
6495
 
6440
6496
  /* -----[ tools ]----- */
6441
6497
 
6442
- function raw_token(moznode) {
6443
- if (moznode.type == "Literal") {
6444
- return moznode.raw != null ? moznode.raw : moznode.value + "";
6445
- }
6446
- }
6447
-
6448
6498
  function my_start_token(moznode) {
6449
6499
  var loc = moznode.loc, start = loc && loc.start;
6450
6500
  var range = moznode.range;
6451
- return new AST_Token({
6452
- file : loc && loc.source,
6453
- line : start && start.line,
6454
- col : start && start.column,
6455
- pos : range ? range[0] : moznode.start,
6456
- endline : start && start.line,
6457
- endcol : start && start.column,
6458
- endpos : range ? range[0] : moznode.start,
6459
- raw : raw_token(moznode),
6460
- });
6501
+ return new AST_Token(
6502
+ "",
6503
+ "",
6504
+ start && start.line || 0,
6505
+ start && start.column || 0,
6506
+ range ? range [0] : moznode.start,
6507
+ false,
6508
+ [],
6509
+ [],
6510
+ loc && loc.source,
6511
+ );
6461
6512
  }
6462
6513
 
6463
6514
  function my_end_token(moznode) {
6464
6515
  var loc = moznode.loc, end = loc && loc.end;
6465
6516
  var range = moznode.range;
6466
- return new AST_Token({
6467
- file : loc && loc.source,
6468
- line : end && end.line,
6469
- col : end && end.column,
6470
- pos : range ? range[1] : moznode.end,
6471
- endline : end && end.line,
6472
- endcol : end && end.column,
6473
- endpos : range ? range[1] : moznode.end,
6474
- raw : raw_token(moznode),
6475
- });
6517
+ return new AST_Token(
6518
+ "",
6519
+ "",
6520
+ end && end.line || 0,
6521
+ end && end.column || 0,
6522
+ range ? range [0] : moznode.end,
6523
+ false,
6524
+ [],
6525
+ [],
6526
+ loc && loc.source,
6527
+ );
6476
6528
  }
6477
6529
 
6478
6530
  function map(moztype, mytype, propmap) {
@@ -7868,6 +7920,9 @@ function OutputStream(options) {
7868
7920
  }
7869
7921
  output.print("`");
7870
7922
  });
7923
+ DEFPRINT(AST_TemplateSegment, function(self, output) {
7924
+ output.print_template_string_chars(self.value);
7925
+ });
7871
7926
 
7872
7927
  AST_Arrow.DEFMETHOD("_do_print", function(output) {
7873
7928
  var self = this;
@@ -8605,8 +8660,8 @@ function OutputStream(options) {
8605
8660
  output.print_string(self.getValue(), self.quote, output.in_directive);
8606
8661
  });
8607
8662
  DEFPRINT(AST_Number, function(self, output) {
8608
- if ((output.option("keep_numbers") || output.use_asm) && self.start && self.start.raw != null) {
8609
- output.print(self.start.raw);
8663
+ if ((output.option("keep_numbers") || output.use_asm) && self.raw) {
8664
+ output.print(self.raw);
8610
8665
  } else {
8611
8666
  output.print(make_num(self.getValue()));
8612
8667
  }
@@ -10664,6 +10719,7 @@ AST_Scope.DEFMETHOD("process_expression", function(insert, compressor) {
10664
10719
  function read_property(obj, key) {
10665
10720
  key = get_value(key);
10666
10721
  if (key instanceof AST_Node) return;
10722
+
10667
10723
  var value;
10668
10724
  if (obj instanceof AST_Array) {
10669
10725
  var elements = obj.elements;
@@ -10678,6 +10734,7 @@ function read_property(obj, key) {
10678
10734
  if (!value && props[i].key === key) value = props[i].value;
10679
10735
  }
10680
10736
  }
10737
+
10681
10738
  return value instanceof AST_SymbolRef && value.fixed_value() || value;
10682
10739
  }
10683
10740
 
@@ -10810,38 +10867,56 @@ function is_modified(compressor, tw, node, value, level, immutable) {
10810
10867
  || value instanceof AST_This;
10811
10868
  }
10812
10869
 
10813
- function mark_escaped(tw, d, scope, node, value, level, depth) {
10870
+ // A definition "escapes" when its value can leave the point of use.
10871
+ // Example: `a = b || c`
10872
+ // In this example, "b" and "c" are escaping, because they're going into "a"
10873
+ //
10874
+ // def.escaped is != 0 when it escapes.
10875
+ //
10876
+ // When greater than 1, it means that N chained properties will be read off
10877
+ // of that def before an escape occurs. This is useful for evaluating
10878
+ // property accesses, where you need to know when to stop.
10879
+ function mark_escaped(tw, d, scope, node, value, level = 0, depth = 1) {
10814
10880
  var parent = tw.parent(level);
10815
10881
  if (value) {
10816
10882
  if (value.is_constant()) return;
10817
10883
  if (value instanceof AST_ClassExpression) return;
10818
10884
  }
10819
- if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
10885
+
10886
+ if (
10887
+ parent instanceof AST_Assign && (parent.operator === "=" || parent.logical) && node === parent.right
10820
10888
  || parent instanceof AST_Call && (node !== parent.expression || parent instanceof AST_New)
10821
10889
  || parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope
10822
10890
  || parent instanceof AST_VarDef && node === parent.value
10823
- || parent instanceof AST_Yield && node === parent.value && node.scope !== d.scope) {
10891
+ || parent instanceof AST_Yield && node === parent.value && node.scope !== d.scope
10892
+ ) {
10824
10893
  if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
10825
10894
  if (!d.escaped || d.escaped > depth) d.escaped = depth;
10826
10895
  return;
10827
- } else if (parent instanceof AST_Array
10896
+ } else if (
10897
+ parent instanceof AST_Array
10828
10898
  || parent instanceof AST_Await
10829
10899
  || parent instanceof AST_Binary && lazy_op.has(parent.operator)
10830
10900
  || parent instanceof AST_Conditional && node !== parent.condition
10831
10901
  || parent instanceof AST_Expansion
10832
- || parent instanceof AST_Sequence && node === parent.tail_node()) {
10902
+ || parent instanceof AST_Sequence && node === parent.tail_node()
10903
+ ) {
10833
10904
  mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
10834
10905
  } else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
10835
10906
  var obj = tw.parent(level + 1);
10907
+
10836
10908
  mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
10837
10909
  } else if (parent instanceof AST_PropAccess && node === parent.expression) {
10838
10910
  value = read_property(value, parent.property);
10911
+
10839
10912
  mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
10840
10913
  if (value) return;
10841
10914
  }
10915
+
10842
10916
  if (level > 0) return;
10843
10917
  if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
10844
10918
  if (parent instanceof AST_SimpleStatement) return;
10919
+
10845
10920
  d.direct_access = true;
10846
10921
  }
10847
10922
 
@@ -10865,32 +10940,64 @@ function is_modified(compressor, tw, node, value, level, immutable) {
10865
10940
  suppress(node.left);
10866
10941
  return;
10867
10942
  }
10943
+
10944
+ const finish_walk = () => {
10945
+ if (node.logical) {
10946
+ node.left.walk(tw);
10947
+
10948
+ push(tw);
10949
+ node.right.walk(tw);
10950
+ pop(tw);
10951
+
10952
+ return true;
10953
+ }
10954
+ };
10955
+
10868
10956
  var sym = node.left;
10869
- if (!(sym instanceof AST_SymbolRef)) return;
10957
+ if (!(sym instanceof AST_SymbolRef)) return finish_walk();
10958
+
10870
10959
  var def = sym.definition();
10871
10960
  var safe = safe_to_assign(tw, def, sym.scope, node.right);
10872
10961
  def.assignments++;
10873
- if (!safe) return;
10962
+ if (!safe) return finish_walk();
10963
+
10874
10964
  var fixed = def.fixed;
10875
- if (!fixed && node.operator != "=") return;
10965
+ if (!fixed && node.operator != "=" && !node.logical) return finish_walk();
10966
+
10876
10967
  var eq = node.operator == "=";
10877
10968
  var value = eq ? node.right : node;
10878
- if (is_modified(compressor, tw, node, value, 0)) return;
10969
+ if (is_modified(compressor, tw, node, value, 0)) return finish_walk();
10970
+
10879
10971
  def.references.push(sym);
10880
- if (!eq) def.chained = true;
10881
- def.fixed = eq ? function() {
10882
- return node.right;
10883
- } : function() {
10884
- return make_node(AST_Binary, node, {
10885
- operator: node.operator.slice(0, -1),
10886
- left: fixed instanceof AST_Node ? fixed : fixed(),
10887
- right: node.right
10888
- });
10889
- };
10972
+
10973
+ if (!node.logical) {
10974
+ if (!eq) def.chained = true;
10975
+
10976
+ def.fixed = eq ? function() {
10977
+ return node.right;
10978
+ } : function() {
10979
+ return make_node(AST_Binary, node, {
10980
+ operator: node.operator.slice(0, -1),
10981
+ left: fixed instanceof AST_Node ? fixed : fixed(),
10982
+ right: node.right
10983
+ });
10984
+ };
10985
+ }
10986
+
10987
+ if (node.logical) {
10988
+ mark(tw, def, false);
10989
+ push(tw);
10990
+ node.right.walk(tw);
10991
+ pop(tw);
10992
+ return true;
10993
+ }
10994
+
10890
10995
  mark(tw, def, false);
10891
10996
  node.right.walk(tw);
10892
10997
  mark(tw, def, true);
10998
+
10893
10999
  mark_escaped(tw, def, sym.scope, node, value, 0, 1);
11000
+
10894
11001
  return true;
10895
11002
  });
10896
11003
  def_reduce_vars(AST_Binary, function(tw) {
@@ -11493,7 +11600,8 @@ function tighten_body(statements, compressor) {
11493
11600
  }
11494
11601
  // Stop immediately if these node types are encountered
11495
11602
  var parent = scanner.parent();
11496
- if (node instanceof AST_Assign && node.operator != "=" && lhs.equivalent_to(node.left)
11603
+ if (node instanceof AST_Assign
11604
+ && (node.logical || node.operator != "=" && lhs.equivalent_to(node.left))
11497
11605
  || node instanceof AST_Await
11498
11606
  || node instanceof AST_Call && lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression)
11499
11607
  || node instanceof AST_Debugger
@@ -11561,6 +11669,7 @@ function tighten_body(statements, compressor) {
11561
11669
  }
11562
11670
  return make_node(AST_Assign, candidate, {
11563
11671
  operator: "=",
11672
+ logical: false,
11564
11673
  left: make_node(AST_SymbolRef, candidate.name, candidate.name),
11565
11674
  right: value
11566
11675
  });
@@ -11865,6 +11974,7 @@ function tighten_body(statements, compressor) {
11865
11974
  var parent = scanner.parent(level);
11866
11975
  if (parent instanceof AST_Assign) {
11867
11976
  if (write_only
11977
+ && !parent.logical
11868
11978
  && !(parent.left instanceof AST_PropAccess
11869
11979
  || lvalues.has(parent.left.name))) {
11870
11980
  return find_stop(parent, level + 1, write_only);
@@ -11919,7 +12029,9 @@ function tighten_body(statements, compressor) {
11919
12029
  }
11920
12030
 
11921
12031
  function get_lhs(expr) {
11922
- if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
12032
+ if (expr instanceof AST_Assign && expr.logical) {
12033
+ return false;
12034
+ } else if (expr instanceof AST_VarDef && expr.name instanceof AST_SymbolDeclaration) {
11923
12035
  var def = expr.name.definition();
11924
12036
  if (!member(expr.name, def.orig)) return;
11925
12037
  var referenced = def.references.length - def.replaced;
@@ -11930,14 +12042,20 @@ function tighten_body(statements, compressor) {
11930
12042
  return make_node(AST_SymbolRef, expr.name, expr.name);
11931
12043
  }
11932
12044
  } else {
11933
- const lhs = expr[expr instanceof AST_Assign ? "left" : "expression"];
12045
+ const lhs = expr instanceof AST_Assign
12046
+ ? expr.left
12047
+ : expr.expression;
11934
12048
  return !is_ref_of(lhs, AST_SymbolConst)
11935
12049
  && !is_ref_of(lhs, AST_SymbolLet) && lhs;
11936
12050
  }
11937
12051
  }
11938
12052
 
11939
12053
  function get_rvalue(expr) {
11940
- return expr[expr instanceof AST_Assign ? "right" : "value"];
12054
+ if (expr instanceof AST_Assign) {
12055
+ return expr.right;
12056
+ } else {
12057
+ return expr.value;
12058
+ }
11941
12059
  }
11942
12060
 
11943
12061
  function get_lvalues(expr) {
@@ -11996,7 +12114,9 @@ function tighten_body(statements, compressor) {
11996
12114
  && !(in_loop
11997
12115
  && (lvalues.has(lhs.name)
11998
12116
  || candidate instanceof AST_Unary
11999
- || candidate instanceof AST_Assign && candidate.operator != "="));
12117
+ || (candidate instanceof AST_Assign
12118
+ && !candidate.logical
12119
+ && candidate.operator != "=")));
12000
12120
  }
12001
12121
 
12002
12122
  function value_has_side_effects(expr) {
@@ -12423,7 +12543,7 @@ function tighten_body(statements, compressor) {
12423
12543
  var def = defn.definitions[defn.definitions.length - 1];
12424
12544
  if (!(def.value instanceof AST_Object)) return;
12425
12545
  var exprs;
12426
- if (body instanceof AST_Assign) {
12546
+ if (body instanceof AST_Assign && !body.logical) {
12427
12547
  exprs = [ body ];
12428
12548
  } else if (body instanceof AST_Sequence) {
12429
12549
  exprs = body.expressions.slice();
@@ -12646,6 +12766,8 @@ function is_undefined(node, compressor) {
12646
12766
  && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
12647
12767
  });
12648
12768
  def_may_throw_on_access(AST_Assign, function(compressor) {
12769
+ if (this.logical) return true;
12770
+
12649
12771
  return this.operator == "="
12650
12772
  && this.right._dot_throw(compressor);
12651
12773
  });
@@ -13870,6 +13992,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
13870
13992
  var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
13871
13993
  const assign_as_unused = r_keep_assign.test(compressor.option("unused")) ? return_false : function(node) {
13872
13994
  if (node instanceof AST_Assign
13995
+ && !node.logical
13873
13996
  && (has_flag(node, WRITE_ONLY) || node.operator == "=")
13874
13997
  ) {
13875
13998
  return node.left;
@@ -14088,6 +14211,7 @@ AST_Scope.DEFMETHOD("drop_unused", function(compressor) {
14088
14211
  sym.references.push(ref);
14089
14212
  var assign = make_node(AST_Assign, def, {
14090
14213
  operator: "=",
14214
+ logical: false,
14091
14215
  left: ref,
14092
14216
  right: def.value
14093
14217
  });
@@ -14523,6 +14647,8 @@ AST_Scope.DEFMETHOD("hoist_properties", function(compressor) {
14523
14647
  }
14524
14648
  });
14525
14649
  def_drop_side_effect_free(AST_Assign, function(compressor) {
14650
+ if (this.logical) return this;
14651
+
14526
14652
  var left = this.left;
14527
14653
  if (left.has_side_effects(compressor)
14528
14654
  || compressor.has_directive("use strict")
@@ -15119,6 +15245,7 @@ AST_Definitions.DEFMETHOD("to_assignments", function(compressor) {
15119
15245
  var name = make_node(AST_SymbolRef, def.name, def.name);
15120
15246
  assignments.push(make_node(AST_Assign, def, {
15121
15247
  operator : "=",
15248
+ logical: false,
15122
15249
  left : name,
15123
15250
  right : def.value
15124
15251
  }));
@@ -15149,6 +15276,17 @@ def_optimize(AST_Definitions, function(self) {
15149
15276
  return self;
15150
15277
  });
15151
15278
 
15279
+ def_optimize(AST_VarDef, function(self) {
15280
+ if (
15281
+ self.name instanceof AST_SymbolLet
15282
+ && self.value != null
15283
+ && is_undefined(self.value)
15284
+ ) {
15285
+ self.value = null;
15286
+ }
15287
+ return self;
15288
+ });
15289
+
15152
15290
  def_optimize(AST_Import, function(self) {
15153
15291
  return self;
15154
15292
  });
@@ -15733,6 +15871,7 @@ def_optimize(AST_Call, function(self, compressor) {
15733
15871
  def.references.push(sym);
15734
15872
  if (value) expressions.push(make_node(AST_Assign, self, {
15735
15873
  operator: "=",
15874
+ logical: false,
15736
15875
  left: sym,
15737
15876
  right: value.clone()
15738
15877
  }));
@@ -15776,6 +15915,7 @@ def_optimize(AST_Call, function(self, compressor) {
15776
15915
  def.references.push(sym);
15777
15916
  expressions.splice(pos++, 0, make_node(AST_Assign, var_def, {
15778
15917
  operator: "=",
15918
+ logical: false,
15779
15919
  left: sym,
15780
15920
  right: make_node(AST_Undefined, name)
15781
15921
  }));
@@ -16296,7 +16436,7 @@ def_optimize(AST_Binary, function(self, compressor) {
16296
16436
  var l = self.left;
16297
16437
  var r = self.right.evaluate(compressor);
16298
16438
  if (r != self.right) {
16299
- l.segments[l.segments.length - 1].value += r.toString();
16439
+ l.segments[l.segments.length - 1].value += String(r);
16300
16440
  return l;
16301
16441
  }
16302
16442
  }
@@ -16305,7 +16445,7 @@ def_optimize(AST_Binary, function(self, compressor) {
16305
16445
  var r = self.right;
16306
16446
  var l = self.left.evaluate(compressor);
16307
16447
  if (l != self.left) {
16308
- r.segments[0].value = l.toString() + r.segments[0].value ;
16448
+ r.segments[0].value = String(l) + r.segments[0].value;
16309
16449
  return r;
16310
16450
  }
16311
16451
  }
@@ -16733,6 +16873,10 @@ function is_reachable(self, defs) {
16733
16873
  const ASSIGN_OPS = makePredicate("+ - / * % >> << >>> | ^ &");
16734
16874
  const ASSIGN_OPS_COMMUTATIVE = makePredicate("* | ^ &");
16735
16875
  def_optimize(AST_Assign, function(self, compressor) {
16876
+ if (self.logical) {
16877
+ return self.lift_sequences(compressor);
16878
+ }
16879
+
16736
16880
  var def;
16737
16881
  if (compressor.option("dead_code")
16738
16882
  && self.left instanceof AST_SymbolRef
@@ -16950,16 +17094,20 @@ def_optimize(AST_Conditional, function(self, compressor) {
16950
17094
  // |
16951
17095
  // v
16952
17096
  // exp = foo ? something : something_else;
16953
- if (consequent instanceof AST_Assign
17097
+ if (
17098
+ consequent instanceof AST_Assign
16954
17099
  && alternative instanceof AST_Assign
16955
- && consequent.operator == alternative.operator
17100
+ && consequent.operator === alternative.operator
17101
+ && consequent.logical === alternative.logical
16956
17102
  && consequent.left.equivalent_to(alternative.left)
16957
17103
  && (!self.condition.has_side_effects(compressor)
16958
17104
  || consequent.operator == "="
16959
- && !consequent.left.has_side_effects(compressor))) {
17105
+ && !consequent.left.has_side_effects(compressor))
17106
+ ) {
16960
17107
  return make_node(AST_Assign, self, {
16961
17108
  operator: consequent.operator,
16962
17109
  left: consequent.left,
17110
+ logical: consequent.logical,
16963
17111
  right: make_node(AST_Conditional, self, {
16964
17112
  condition: self.condition,
16965
17113
  consequent: consequent.right,
@@ -17555,9 +17703,12 @@ def_optimize(AST_Yield, function(self, compressor) {
17555
17703
  });
17556
17704
 
17557
17705
  def_optimize(AST_TemplateString, function(self, compressor) {
17558
- if (!compressor.option("evaluate")
17559
- || compressor.parent() instanceof AST_PrefixedTemplateString)
17706
+ if (
17707
+ !compressor.option("evaluate")
17708
+ || compressor.parent() instanceof AST_PrefixedTemplateString
17709
+ ) {
17560
17710
  return self;
17711
+ }
17561
17712
 
17562
17713
  var segments = [];
17563
17714
  for (var i = 0; i < self.segments.length; i++) {
@@ -17593,7 +17744,17 @@ def_optimize(AST_TemplateString, function(self, compressor) {
17593
17744
  if (segments.length == 1) {
17594
17745
  return make_node(AST_String, self, segments[0]);
17595
17746
  }
17596
- if (segments.length === 3 && segments[1] instanceof AST_Node) {
17747
+
17748
+ if (
17749
+ segments.length === 3
17750
+ && segments[1] instanceof AST_Node
17751
+ && (
17752
+ segments[1].is_string(compressor)
17753
+ || segments[1].is_number(compressor)
17754
+ || is_nullish(segments[1])
17755
+ || compressor.option("unsafe")
17756
+ )
17757
+ ) {
17597
17758
  // `foo${bar}` => "foo" + bar
17598
17759
  if (segments[2].value === "") {
17599
17760
  return make_node(AST_Binary, self, {
@@ -17604,7 +17765,7 @@ def_optimize(AST_TemplateString, function(self, compressor) {
17604
17765
  right: segments[1],
17605
17766
  });
17606
17767
  }
17607
- // `{bar}baz` => bar + "baz"
17768
+ // `${bar}baz` => bar + "baz"
17608
17769
  if (segments[0].value === "") {
17609
17770
  return make_node(AST_Binary, self, {
17610
17771
  operator: "+",
@@ -26661,4 +26822,3 @@ exports._run_cli = run_cli;
26661
26822
  exports.minify = minify;
26662
26823
 
26663
26824
  })));
26664
- //# sourceMappingURL=bundle.min.js.map