terser 5.17.0 → 5.17.2

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## v5.17.2
4
+ - Be less conservative when detecting use-before-definition of `var` in hoisted functions.
5
+ - Support unusual (but perfectly valid) initializers of for-in and for-of loops.
6
+ - Fix issue where hoisted function would be dropped if it was after a `continue` statement
7
+
8
+ ## v5.17.1
9
+ - Fix evaluating `.length` when the source array might've been mutated
10
+
3
11
  ## v5.17.0
4
12
  - Drop vestigial `= undefined` default argument in IIFE calls (#1366)
5
13
  - Evaluate known arrays' `.length` property when statically determinable
@@ -4515,11 +4515,14 @@ var AST_Destructuring = DEFNODE("Destructuring", "names is_array", function AST_
4515
4515
  },
4516
4516
  all_symbols: function() {
4517
4517
  var out = [];
4518
- this.walk(new TreeWalker(function (node) {
4519
- if (node instanceof AST_Symbol) {
4518
+ walk(this, node => {
4519
+ if (node instanceof AST_SymbolDeclaration) {
4520
4520
  out.push(node);
4521
4521
  }
4522
- }));
4522
+ if (node instanceof AST_Lambda) {
4523
+ return true;
4524
+ }
4525
+ });
4523
4526
  return out;
4524
4527
  }
4525
4528
  });
@@ -5050,6 +5053,13 @@ var AST_VarDef = DEFNODE("VarDef", "name value", function AST_VarDef(props) {
5050
5053
  if (this.value) push(this.value);
5051
5054
  push(this.name);
5052
5055
  },
5056
+ declarations_as_names() {
5057
+ if (this.name instanceof AST_SymbolDeclaration) {
5058
+ return [this];
5059
+ } else {
5060
+ return this.name.all_symbols();
5061
+ }
5062
+ }
5053
5063
  });
5054
5064
 
5055
5065
  var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", function AST_NameMapping(props) {
@@ -13908,6 +13918,7 @@ function is_nullish(node, compressor) {
13908
13918
  function is_lhs(node, parent) {
13909
13919
  if (parent instanceof AST_Unary && unary_side_effects.has(parent.operator)) return parent.expression;
13910
13920
  if (parent instanceof AST_Assign && parent.left === node) return node;
13921
+ if (parent instanceof AST_ForIn && parent.init === node) return node;
13911
13922
  }
13912
13923
 
13913
13924
  (function(def_find_defs) {
@@ -14496,24 +14507,16 @@ def_eval(AST_PropAccess, function (compressor, depth) {
14496
14507
  return obj.length;
14497
14508
  }
14498
14509
 
14499
- const is_spreadless_array = obj =>
14510
+ const is_spreadless_array =
14500
14511
  obj instanceof AST_Array
14501
14512
  && obj.elements.every(el => !(el instanceof AST_Expansion));
14502
14513
 
14503
14514
  if (
14504
- is_spreadless_array(obj)
14515
+ is_spreadless_array
14505
14516
  && obj.elements.every(el => !el.has_side_effects(compressor))
14506
14517
  ) {
14507
14518
  return obj.elements.length;
14508
14519
  }
14509
-
14510
- let fixed;
14511
- if (
14512
- obj instanceof AST_SymbolRef
14513
- && is_spreadless_array((fixed = obj.definition().fixed_value()))
14514
- ) {
14515
- return fixed.elements.length;
14516
- }
14517
14520
  }
14518
14521
 
14519
14522
  if (compressor.option("unsafe")) {
@@ -15845,20 +15848,31 @@ function handle_defined_after_hoist(parent) {
15845
15848
  continue;
15846
15849
  }
15847
15850
 
15848
- // Detect `call_defun(); var used_in_defun = ...`
15849
- // Because `used_in_defun` can no longer be fixed
15850
- let found_defun = false;
15851
+ // Detect `call_defun(); var used_in_defun = X`
15852
+ // Because `used_in_defun` is not certainly X when it's defined after.
15853
+ let found_defun_ref = false;
15851
15854
  let found_def_after_defun = false;
15852
- walk(parent, node => {
15855
+ walk_parent(parent, (node, info) => {
15853
15856
  if (node === defun) return true;
15854
15857
 
15855
- if (node instanceof AST_Symbol) {
15856
- if (!found_defun && node.thedef === fname_def) {
15857
- found_defun = true;
15858
- } else if (found_defun && node.thedef === def) {
15859
- found_def_after_defun = true;
15860
- return walk_abort;
15861
- }
15858
+ // Step 1: find `call_defun()` or other refs to the defun
15859
+ if (
15860
+ !found_defun_ref
15861
+ && node.thedef === fname_def
15862
+ && node instanceof AST_Symbol
15863
+ ) {
15864
+ found_defun_ref = true;
15865
+ }
15866
+
15867
+ // Step 2: if Step 1 occurred, find a var the defun uses
15868
+ if (
15869
+ found_defun_ref
15870
+ && node.thedef === def
15871
+ && (node instanceof AST_SymbolDeclaration
15872
+ || is_lhs(node, info))
15873
+ ) {
15874
+ found_def_after_defun = true;
15875
+ return walk_abort;
15862
15876
  }
15863
15877
  });
15864
15878
 
@@ -16138,12 +16152,31 @@ function is_lhs_read_only(lhs) {
16138
16152
  return false;
16139
16153
  }
16140
16154
 
16141
- // Remove code which we know is unreachable.
16155
+ /** var a = 1 --> var a*/
16156
+ function remove_initializers(var_statement) {
16157
+ var decls = [];
16158
+ var_statement.definitions.forEach(function(def) {
16159
+ if (def.name instanceof AST_SymbolDeclaration) {
16160
+ def.value = null;
16161
+ decls.push(def);
16162
+ } else {
16163
+ def.declarations_as_names().forEach(name => {
16164
+ decls.push(make_node(AST_VarDef, def, {
16165
+ name,
16166
+ value: null
16167
+ }));
16168
+ });
16169
+ }
16170
+ });
16171
+ return decls.length ? make_node(AST_Var, var_statement, { definitions: decls }) : null;
16172
+ }
16173
+
16174
+ /** Called on code which we know is unreachable, to keep elements that affect outside of it. */
16142
16175
  function trim_unreachable_code(compressor, stat, target) {
16143
16176
  walk(stat, node => {
16144
16177
  if (node instanceof AST_Var) {
16145
- node.remove_initializers();
16146
- target.push(node);
16178
+ const no_initializers = remove_initializers(node);
16179
+ if (no_initializers) target.push(no_initializers);
16147
16180
  return true;
16148
16181
  }
16149
16182
  if (
@@ -16951,27 +16984,34 @@ function tighten_body(statements, compressor) {
16951
16984
  }
16952
16985
 
16953
16986
  if (stat instanceof AST_If) {
16954
- var ab = aborts(stat.body);
16955
- if (can_merge_flow(ab)) {
16987
+ let ab, new_else;
16988
+
16989
+ ab = aborts(stat.body);
16990
+ if (
16991
+ can_merge_flow(ab)
16992
+ && (new_else = as_statement_array_with_return(stat.body, ab))
16993
+ ) {
16956
16994
  if (ab.label) {
16957
16995
  remove(ab.label.thedef.references, ab);
16958
16996
  }
16959
16997
  CHANGED = true;
16960
16998
  stat = stat.clone();
16961
16999
  stat.condition = stat.condition.negate(compressor);
16962
- var body = as_statement_array_with_return(stat.body, ab);
16963
17000
  stat.body = make_node(AST_BlockStatement, stat, {
16964
17001
  body: as_statement_array(stat.alternative).concat(extract_functions())
16965
17002
  });
16966
17003
  stat.alternative = make_node(AST_BlockStatement, stat, {
16967
- body: body
17004
+ body: new_else
16968
17005
  });
16969
17006
  statements[i] = stat.transform(compressor);
16970
17007
  continue;
16971
17008
  }
16972
17009
 
16973
- var ab = aborts(stat.alternative);
16974
- if (can_merge_flow(ab)) {
17010
+ ab = aborts(stat.alternative);
17011
+ if (
17012
+ can_merge_flow(ab)
17013
+ && (new_else = as_statement_array_with_return(stat.alternative, ab))
17014
+ ) {
16975
17015
  if (ab.label) {
16976
17016
  remove(ab.label.thedef.references, ab);
16977
17017
  }
@@ -16980,9 +17020,8 @@ function tighten_body(statements, compressor) {
16980
17020
  stat.body = make_node(AST_BlockStatement, stat.body, {
16981
17021
  body: as_statement_array(stat.body).concat(extract_functions())
16982
17022
  });
16983
- var body = as_statement_array_with_return(stat.alternative, ab);
16984
17023
  stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
16985
- body: body
17024
+ body: new_else
16986
17025
  });
16987
17026
  statements[i] = stat.transform(compressor);
16988
17027
  continue;
@@ -17097,7 +17136,11 @@ function tighten_body(statements, compressor) {
17097
17136
  }
17098
17137
 
17099
17138
  function as_statement_array_with_return(node, ab) {
17100
- var body = as_statement_array(node).slice(0, -1);
17139
+ var body = as_statement_array(node);
17140
+ if (ab !== body[body.length - 1]) {
17141
+ return undefined;
17142
+ }
17143
+ body = body.slice(0, -1);
17101
17144
  if (ab.value) {
17102
17145
  body.push(make_node(AST_SimpleStatement, ab.value, {
17103
17146
  body: ab.value.expression
@@ -19373,26 +19416,6 @@ def_optimize(AST_Try, function(self, compressor) {
19373
19416
  return self;
19374
19417
  });
19375
19418
 
19376
- AST_Definitions.DEFMETHOD("remove_initializers", function() {
19377
- var decls = [];
19378
- this.definitions.forEach(function(def) {
19379
- if (def.name instanceof AST_SymbolDeclaration) {
19380
- def.value = null;
19381
- decls.push(def);
19382
- } else {
19383
- walk(def.name, node => {
19384
- if (node instanceof AST_SymbolDeclaration) {
19385
- decls.push(make_node(AST_VarDef, def, {
19386
- name: node,
19387
- value: null
19388
- }));
19389
- }
19390
- });
19391
- }
19392
- });
19393
- this.definitions = decls;
19394
- });
19395
-
19396
19419
  AST_Definitions.DEFMETHOD("to_assignments", function(compressor) {
19397
19420
  var reduce_vars = compressor.option("reduce_vars");
19398
19421
  var assignments = [];
package/lib/ast.js CHANGED
@@ -905,11 +905,14 @@ var AST_Destructuring = DEFNODE("Destructuring", "names is_array", function AST_
905
905
  },
906
906
  all_symbols: function() {
907
907
  var out = [];
908
- this.walk(new TreeWalker(function (node) {
909
- if (node instanceof AST_Symbol) {
908
+ walk(this, node => {
909
+ if (node instanceof AST_SymbolDeclaration) {
910
910
  out.push(node);
911
911
  }
912
- }));
912
+ if (node instanceof AST_Lambda) {
913
+ return true;
914
+ }
915
+ });
913
916
  return out;
914
917
  }
915
918
  });
@@ -1440,6 +1443,13 @@ var AST_VarDef = DEFNODE("VarDef", "name value", function AST_VarDef(props) {
1440
1443
  if (this.value) push(this.value);
1441
1444
  push(this.name);
1442
1445
  },
1446
+ declarations_as_names() {
1447
+ if (this.name instanceof AST_SymbolDeclaration) {
1448
+ return [this];
1449
+ } else {
1450
+ return this.name.all_symbols();
1451
+ }
1452
+ }
1443
1453
  });
1444
1454
 
1445
1455
  var AST_NameMapping = DEFNODE("NameMapping", "foreign_name name", function AST_NameMapping(props) {
@@ -359,24 +359,16 @@ def_eval(AST_PropAccess, function (compressor, depth) {
359
359
  return obj.length;
360
360
  }
361
361
 
362
- const is_spreadless_array = obj =>
362
+ const is_spreadless_array =
363
363
  obj instanceof AST_Array
364
364
  && obj.elements.every(el => !(el instanceof AST_Expansion));
365
365
 
366
366
  if (
367
- is_spreadless_array(obj)
367
+ is_spreadless_array
368
368
  && obj.elements.every(el => !el.has_side_effects(compressor))
369
369
  ) {
370
370
  return obj.elements.length;
371
371
  }
372
-
373
- let fixed;
374
- if (
375
- obj instanceof AST_SymbolRef
376
- && is_spreadless_array((fixed = obj.definition().fixed_value()))
377
- ) {
378
- return fixed.elements.length;
379
- }
380
372
  }
381
373
 
382
374
  if (compressor.option("unsafe")) {
@@ -1545,26 +1545,6 @@ def_optimize(AST_Try, function(self, compressor) {
1545
1545
  return self;
1546
1546
  });
1547
1547
 
1548
- AST_Definitions.DEFMETHOD("remove_initializers", function() {
1549
- var decls = [];
1550
- this.definitions.forEach(function(def) {
1551
- if (def.name instanceof AST_SymbolDeclaration) {
1552
- def.value = null;
1553
- decls.push(def);
1554
- } else {
1555
- walk(def.name, node => {
1556
- if (node instanceof AST_SymbolDeclaration) {
1557
- decls.push(make_node(AST_VarDef, def, {
1558
- name: node,
1559
- value: null
1560
- }));
1561
- }
1562
- });
1563
- }
1564
- });
1565
- this.definitions = decls;
1566
- });
1567
-
1568
1548
  AST_Definitions.DEFMETHOD("to_assignments", function(compressor) {
1569
1549
  var reduce_vars = compressor.option("reduce_vars");
1570
1550
  var assignments = [];
@@ -64,6 +64,7 @@ import {
64
64
  AST_EmptyStatement,
65
65
  AST_Expansion,
66
66
  AST_False,
67
+ AST_ForIn,
67
68
  AST_Function,
68
69
  AST_If,
69
70
  AST_Import,
@@ -712,6 +713,7 @@ export function is_nullish(node, compressor) {
712
713
  export function is_lhs(node, parent) {
713
714
  if (parent instanceof AST_Unary && unary_side_effects.has(parent.operator)) return parent.expression;
714
715
  if (parent instanceof AST_Assign && parent.left === node) return node;
716
+ if (parent instanceof AST_ForIn && parent.init === node) return node;
715
717
  }
716
718
 
717
719
  (function(def_find_defs) {
@@ -77,6 +77,7 @@ import {
77
77
  AST_Symbol,
78
78
  AST_SymbolCatch,
79
79
  AST_SymbolConst,
80
+ AST_SymbolDeclaration,
80
81
  AST_SymbolDefun,
81
82
  AST_SymbolFunarg,
82
83
  AST_SymbolLambda,
@@ -92,6 +93,7 @@ import {
92
93
  AST_Yield,
93
94
 
94
95
  walk,
96
+ walk_parent,
95
97
  walk_abort,
96
98
  walk_body,
97
99
 
@@ -101,7 +103,7 @@ import {
101
103
  } from "../ast.js";
102
104
  import { HOP, make_node, noop } from "../utils/index.js";
103
105
 
104
- import { lazy_op, is_modified } from "./inference.js";
106
+ import { lazy_op, is_modified, is_lhs } from "./inference.js";
105
107
  import { INLINED, clear_flag } from "./compressor-flags.js";
106
108
  import { read_property, has_break_or_continue, is_recursive_ref } from "./common.js";
107
109
 
@@ -540,20 +542,31 @@ function handle_defined_after_hoist(parent) {
540
542
  continue;
541
543
  }
542
544
 
543
- // Detect `call_defun(); var used_in_defun = ...`
544
- // Because `used_in_defun` can no longer be fixed
545
- let found_defun = false;
545
+ // Detect `call_defun(); var used_in_defun = X`
546
+ // Because `used_in_defun` is not certainly X when it's defined after.
547
+ let found_defun_ref = false;
546
548
  let found_def_after_defun = false;
547
- walk(parent, node => {
549
+ walk_parent(parent, (node, info) => {
548
550
  if (node === defun) return true;
549
551
 
550
- if (node instanceof AST_Symbol) {
551
- if (!found_defun && node.thedef === fname_def) {
552
- found_defun = true;
553
- } else if (found_defun && node.thedef === def) {
554
- found_def_after_defun = true;
555
- return walk_abort;
556
- }
552
+ // Step 1: find `call_defun()` or other refs to the defun
553
+ if (
554
+ !found_defun_ref
555
+ && node.thedef === fname_def
556
+ && node instanceof AST_Symbol
557
+ ) {
558
+ found_defun_ref = true;
559
+ }
560
+
561
+ // Step 2: if Step 1 occurred, find a var the defun uses
562
+ if (
563
+ found_defun_ref
564
+ && node.thedef === def
565
+ && (node instanceof AST_SymbolDeclaration
566
+ || is_lhs(node, info))
567
+ ) {
568
+ found_def_after_defun = true;
569
+ return walk_abort;
557
570
  }
558
571
  });
559
572
 
@@ -170,12 +170,31 @@ function is_lhs_read_only(lhs) {
170
170
  return false;
171
171
  }
172
172
 
173
- // Remove code which we know is unreachable.
173
+ /** var a = 1 --> var a*/
174
+ function remove_initializers(var_statement) {
175
+ var decls = [];
176
+ var_statement.definitions.forEach(function(def) {
177
+ if (def.name instanceof AST_SymbolDeclaration) {
178
+ def.value = null;
179
+ decls.push(def);
180
+ } else {
181
+ def.declarations_as_names().forEach(name => {
182
+ decls.push(make_node(AST_VarDef, def, {
183
+ name,
184
+ value: null
185
+ }));
186
+ });
187
+ }
188
+ });
189
+ return decls.length ? make_node(AST_Var, var_statement, { definitions: decls }) : null;
190
+ }
191
+
192
+ /** Called on code which we know is unreachable, to keep elements that affect outside of it. */
174
193
  export function trim_unreachable_code(compressor, stat, target) {
175
194
  walk(stat, node => {
176
195
  if (node instanceof AST_Var) {
177
- node.remove_initializers();
178
- target.push(node);
196
+ const no_initializers = remove_initializers(node);
197
+ if (no_initializers) target.push(no_initializers);
179
198
  return true;
180
199
  }
181
200
  if (
@@ -983,27 +1002,34 @@ export function tighten_body(statements, compressor) {
983
1002
  }
984
1003
 
985
1004
  if (stat instanceof AST_If) {
986
- var ab = aborts(stat.body);
987
- if (can_merge_flow(ab)) {
1005
+ let ab, new_else;
1006
+
1007
+ ab = aborts(stat.body);
1008
+ if (
1009
+ can_merge_flow(ab)
1010
+ && (new_else = as_statement_array_with_return(stat.body, ab))
1011
+ ) {
988
1012
  if (ab.label) {
989
1013
  remove(ab.label.thedef.references, ab);
990
1014
  }
991
1015
  CHANGED = true;
992
1016
  stat = stat.clone();
993
1017
  stat.condition = stat.condition.negate(compressor);
994
- var body = as_statement_array_with_return(stat.body, ab);
995
1018
  stat.body = make_node(AST_BlockStatement, stat, {
996
1019
  body: as_statement_array(stat.alternative).concat(extract_functions())
997
1020
  });
998
1021
  stat.alternative = make_node(AST_BlockStatement, stat, {
999
- body: body
1022
+ body: new_else
1000
1023
  });
1001
1024
  statements[i] = stat.transform(compressor);
1002
1025
  continue;
1003
1026
  }
1004
1027
 
1005
- var ab = aborts(stat.alternative);
1006
- if (can_merge_flow(ab)) {
1028
+ ab = aborts(stat.alternative);
1029
+ if (
1030
+ can_merge_flow(ab)
1031
+ && (new_else = as_statement_array_with_return(stat.alternative, ab))
1032
+ ) {
1007
1033
  if (ab.label) {
1008
1034
  remove(ab.label.thedef.references, ab);
1009
1035
  }
@@ -1012,9 +1038,8 @@ export function tighten_body(statements, compressor) {
1012
1038
  stat.body = make_node(AST_BlockStatement, stat.body, {
1013
1039
  body: as_statement_array(stat.body).concat(extract_functions())
1014
1040
  });
1015
- var body = as_statement_array_with_return(stat.alternative, ab);
1016
1041
  stat.alternative = make_node(AST_BlockStatement, stat.alternative, {
1017
- body: body
1042
+ body: new_else
1018
1043
  });
1019
1044
  statements[i] = stat.transform(compressor);
1020
1045
  continue;
@@ -1129,7 +1154,11 @@ export function tighten_body(statements, compressor) {
1129
1154
  }
1130
1155
 
1131
1156
  function as_statement_array_with_return(node, ab) {
1132
- var body = as_statement_array(node).slice(0, -1);
1157
+ var body = as_statement_array(node);
1158
+ if (ab !== body[body.length - 1]) {
1159
+ return undefined;
1160
+ }
1161
+ body = body.slice(0, -1);
1133
1162
  if (ab.value) {
1134
1163
  body.push(make_node(AST_SimpleStatement, ab.value, {
1135
1164
  body: ab.value.expression
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "homepage": "https://terser.org",
5
5
  "author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
6
6
  "license": "BSD-2-Clause",
7
- "version": "5.17.0",
7
+ "version": "5.17.2",
8
8
  "engines": {
9
9
  "node": ">=10"
10
10
  },