terser 5.7.2 → 5.8.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.
- package/CHANGELOG.md +9 -0
- package/README.md +15 -2
- package/bin/terser.mjs +21 -0
- package/dist/bundle.min.js +422 -110
- package/lib/ast.js +1 -1
- package/lib/compress/index.js +293 -44
- package/lib/compress/tighten-body.js +29 -7
- package/lib/minify.js +2 -2
- package/lib/output.js +9 -4
- package/lib/propmangle.js +54 -31
- package/lib/scope.js +37 -17
- package/lib/transform.js +2 -2
- package/package.json +9 -9
- package/tools/terser.d.ts +36 -0
package/lib/ast.js
CHANGED
@@ -1330,7 +1330,7 @@ var AST_ClassProperty = DEFNODE("ClassProperty", "static quote", {
|
|
1330
1330
|
}
|
1331
1331
|
}, AST_ObjectProperty);
|
1332
1332
|
|
1333
|
-
var AST_ClassPrivateProperty = DEFNODE("
|
1333
|
+
var AST_ClassPrivateProperty = DEFNODE("ClassPrivateProperty", "", {
|
1334
1334
|
$documentation: "A class property for a private property",
|
1335
1335
|
}, AST_ClassProperty);
|
1336
1336
|
|
package/lib/compress/index.js
CHANGED
@@ -53,7 +53,6 @@ import {
|
|
53
53
|
AST_Boolean,
|
54
54
|
AST_Break,
|
55
55
|
AST_Call,
|
56
|
-
AST_Case,
|
57
56
|
AST_Catch,
|
58
57
|
AST_Chain,
|
59
58
|
AST_Class,
|
@@ -109,6 +108,7 @@ import {
|
|
109
108
|
AST_String,
|
110
109
|
AST_Sub,
|
111
110
|
AST_Switch,
|
111
|
+
AST_SwitchBranch,
|
112
112
|
AST_Symbol,
|
113
113
|
AST_SymbolBlockDeclaration,
|
114
114
|
AST_SymbolCatch,
|
@@ -390,7 +390,8 @@ class Compressor extends TreeWalker {
|
|
390
390
|
var passes = +this.options.passes || 1;
|
391
391
|
var min_count = 1 / 0;
|
392
392
|
var stopping = false;
|
393
|
-
var
|
393
|
+
var nth_identifier = this.mangle_options && this.mangle_options.nth_identifier || base54;
|
394
|
+
var mangle = { ie8: this.option("ie8"), nth_identifier: nth_identifier };
|
394
395
|
for (var pass = 0; pass < passes; pass++) {
|
395
396
|
this._toplevel.figure_out_scope(mangle);
|
396
397
|
if (pass === 0 && this.option("drop_console")) {
|
@@ -1592,58 +1593,251 @@ def_optimize(AST_Switch, function(self, compressor) {
|
|
1592
1593
|
}
|
1593
1594
|
}
|
1594
1595
|
}
|
1595
|
-
if (aborts(branch)) {
|
1596
|
-
var prev = body[body.length - 1];
|
1597
|
-
if (aborts(prev) && prev.body.length == branch.body.length
|
1598
|
-
&& make_node(AST_BlockStatement, prev, prev).equivalent_to(make_node(AST_BlockStatement, branch, branch))) {
|
1599
|
-
prev.body = [];
|
1600
|
-
}
|
1601
|
-
}
|
1602
1596
|
body.push(branch);
|
1603
1597
|
}
|
1604
1598
|
while (i < len) eliminate_branch(self.body[i++], body[body.length - 1]);
|
1599
|
+
self.body = body;
|
1600
|
+
|
1601
|
+
for (let i = 0; i < body.length; i++) {
|
1602
|
+
let branch = body[i];
|
1603
|
+
if (branch.body.length === 0) continue;
|
1604
|
+
if (!aborts(branch)) continue;
|
1605
|
+
|
1606
|
+
for (let j = i + 1; j < body.length; i++, j++) {
|
1607
|
+
let next = body[j];
|
1608
|
+
if (next.body.length === 0) continue;
|
1609
|
+
if (
|
1610
|
+
branches_equivalent(next, branch, false)
|
1611
|
+
|| (j === body.length - 1 && branches_equivalent(next, branch, true))
|
1612
|
+
) {
|
1613
|
+
branch.body = [];
|
1614
|
+
branch = next;
|
1615
|
+
continue;
|
1616
|
+
}
|
1617
|
+
break;
|
1618
|
+
}
|
1619
|
+
}
|
1620
|
+
|
1621
|
+
let default_or_exact = default_branch || exact_match;
|
1622
|
+
default_branch = null;
|
1623
|
+
exact_match = null;
|
1624
|
+
|
1625
|
+
// Prune any empty branches at the end of the switch statement.
|
1626
|
+
{
|
1627
|
+
let i = body.length - 1;
|
1628
|
+
for (; i >= 0; i--) {
|
1629
|
+
let bbody = body[i].body;
|
1630
|
+
if (is_break(bbody[bbody.length - 1], compressor)) bbody.pop();
|
1631
|
+
if (!is_inert_body(body[i])) break;
|
1632
|
+
}
|
1633
|
+
// i now points to the index of a branch that contains a body. By incrementing, it's
|
1634
|
+
// pointing to the first branch that's empty.
|
1635
|
+
i++;
|
1636
|
+
if (!default_or_exact || body.indexOf(default_or_exact) >= i) {
|
1637
|
+
// The default behavior is to do nothing. We can take advantage of that to
|
1638
|
+
// remove all case expressions that are side-effect free that also do
|
1639
|
+
// nothing, since they'll default to doing nothing. But we can't remove any
|
1640
|
+
// case expressions before one that would side-effect, since they may cause
|
1641
|
+
// the side-effect to be skipped.
|
1642
|
+
for (let j = body.length - 1; j >= i; j--) {
|
1643
|
+
let branch = body[j];
|
1644
|
+
if (branch === default_or_exact) {
|
1645
|
+
default_or_exact = null;
|
1646
|
+
body.pop();
|
1647
|
+
} else if (!branch.expression.has_side_effects(compressor)) {
|
1648
|
+
body.pop();
|
1649
|
+
} else {
|
1650
|
+
break;
|
1651
|
+
}
|
1652
|
+
}
|
1653
|
+
}
|
1654
|
+
}
|
1655
|
+
|
1656
|
+
|
1657
|
+
// Prune side-effect free branches that fall into default.
|
1658
|
+
if (default_or_exact) {
|
1659
|
+
let default_index = body.indexOf(default_or_exact);
|
1660
|
+
let default_body_index = default_index;
|
1661
|
+
for (; default_body_index < body.length - 1; default_body_index++) {
|
1662
|
+
if (!is_inert_body(body[default_body_index])) break;
|
1663
|
+
}
|
1664
|
+
let side_effect_index = body.length - 1;
|
1665
|
+
for (; side_effect_index >= 0; side_effect_index--) {
|
1666
|
+
let branch = body[side_effect_index];
|
1667
|
+
if (branch === default_or_exact) continue;
|
1668
|
+
if (branch.expression.has_side_effects(compressor)) break;
|
1669
|
+
}
|
1670
|
+
// If the default behavior comes after any side-effect case expressions,
|
1671
|
+
// then we can fold all side-effect free cases into the default branch.
|
1672
|
+
// If the side-effect case is after the default, then any side-effect
|
1673
|
+
// free cases could prevent the side-effect from occurring.
|
1674
|
+
if (default_body_index > side_effect_index) {
|
1675
|
+
let prev_body_index = default_index - 1;
|
1676
|
+
for (; prev_body_index >= 0; prev_body_index--) {
|
1677
|
+
if (!is_inert_body(body[prev_body_index])) break;
|
1678
|
+
}
|
1679
|
+
let before = Math.max(side_effect_index, prev_body_index) + 1;
|
1680
|
+
let after = default_index;
|
1681
|
+
if (side_effect_index > default_index) {
|
1682
|
+
// If the default falls into the same body as a side-effect
|
1683
|
+
// case, then we need preserve that case and only prune the
|
1684
|
+
// cases after it.
|
1685
|
+
after = side_effect_index;
|
1686
|
+
body[side_effect_index].body = body[default_body_index].body;
|
1687
|
+
} else {
|
1688
|
+
// The default will be the last branch.
|
1689
|
+
default_or_exact.body = body[default_body_index].body;
|
1690
|
+
}
|
1691
|
+
|
1692
|
+
// Prune everything after the default (or last side-effect case)
|
1693
|
+
// until the next case with a body.
|
1694
|
+
body.splice(after + 1, default_body_index - after);
|
1695
|
+
// Prune everything before the default that falls into it.
|
1696
|
+
body.splice(before, default_index - before);
|
1697
|
+
}
|
1698
|
+
}
|
1699
|
+
|
1700
|
+
// See if we can remove the switch entirely if all cases (the default) fall into the same case body.
|
1701
|
+
DEFAULT: if (default_or_exact) {
|
1702
|
+
let i = body.findIndex(branch => !is_inert_body(branch));
|
1703
|
+
let caseBody;
|
1704
|
+
// `i` is equal to one of the following:
|
1705
|
+
// - `-1`, there is no body in the switch statement.
|
1706
|
+
// - `body.length - 1`, all cases fall into the same body.
|
1707
|
+
// - anything else, there are multiple bodies in the switch.
|
1708
|
+
if (i === body.length - 1) {
|
1709
|
+
// All cases fall into the case body.
|
1710
|
+
let branch = body[i];
|
1711
|
+
if (has_nested_break(self)) break DEFAULT;
|
1712
|
+
|
1713
|
+
// This is the last case body, and we've already pruned any breaks, so it's
|
1714
|
+
// safe to hoist.
|
1715
|
+
caseBody = make_node(AST_BlockStatement, branch, {
|
1716
|
+
body: branch.body
|
1717
|
+
});
|
1718
|
+
branch.body = [];
|
1719
|
+
} else if (i !== -1) {
|
1720
|
+
// If there are multiple bodies, then we cannot optimize anything.
|
1721
|
+
break DEFAULT;
|
1722
|
+
}
|
1723
|
+
|
1724
|
+
let sideEffect = body.find(branch => {
|
1725
|
+
return (
|
1726
|
+
branch !== default_or_exact
|
1727
|
+
&& branch.expression.has_side_effects(compressor)
|
1728
|
+
);
|
1729
|
+
});
|
1730
|
+
// If no cases cause a side-effect, we can eliminate the switch entirely.
|
1731
|
+
if (!sideEffect) {
|
1732
|
+
return make_node(AST_BlockStatement, self, {
|
1733
|
+
body: decl.concat(
|
1734
|
+
statement(self.expression),
|
1735
|
+
default_or_exact.expression ? statement(default_or_exact.expression) : [],
|
1736
|
+
caseBody || []
|
1737
|
+
)
|
1738
|
+
}).optimize(compressor);
|
1739
|
+
}
|
1740
|
+
|
1741
|
+
// If we're this far, either there was no body or all cases fell into the same body.
|
1742
|
+
// If there was no body, then we don't need a default branch (because the default is
|
1743
|
+
// do nothing). If there was a body, we'll extract it to after the switch, so the
|
1744
|
+
// switch's new default is to do nothing and we can still prune it.
|
1745
|
+
const default_index = body.indexOf(default_or_exact);
|
1746
|
+
body.splice(default_index, 1);
|
1747
|
+
default_or_exact = null;
|
1748
|
+
|
1749
|
+
if (caseBody) {
|
1750
|
+
// Recurse into switch statement one more time so that we can append the case body
|
1751
|
+
// outside of the switch. This recursion will only happen once since we've pruned
|
1752
|
+
// the default case.
|
1753
|
+
return make_node(AST_BlockStatement, self, {
|
1754
|
+
body: decl.concat(self, caseBody)
|
1755
|
+
}).optimize(compressor);
|
1756
|
+
}
|
1757
|
+
// If we fall here, there is a default branch somewhere, there are no case bodies,
|
1758
|
+
// and there's a side-effect somewhere. Just let the below paths take care of it.
|
1759
|
+
}
|
1760
|
+
|
1605
1761
|
if (body.length > 0) {
|
1606
1762
|
body[0].body = decl.concat(body[0].body);
|
1607
1763
|
}
|
1608
|
-
|
1609
|
-
while (branch = body[body.length - 1]) {
|
1610
|
-
var stat = branch.body[branch.body.length - 1];
|
1611
|
-
if (stat instanceof AST_Break && compressor.loopcontrol_target(stat) === self)
|
1612
|
-
branch.body.pop();
|
1613
|
-
if (branch.body.length || branch instanceof AST_Case
|
1614
|
-
&& (default_branch || branch.expression.has_side_effects(compressor))) break;
|
1615
|
-
if (body.pop() === default_branch) default_branch = null;
|
1616
|
-
}
|
1764
|
+
|
1617
1765
|
if (body.length == 0) {
|
1618
1766
|
return make_node(AST_BlockStatement, self, {
|
1619
|
-
body: decl.concat(
|
1620
|
-
body: self.expression
|
1621
|
-
}))
|
1767
|
+
body: decl.concat(statement(self.expression))
|
1622
1768
|
}).optimize(compressor);
|
1623
1769
|
}
|
1624
|
-
if (body.length == 1 && (
|
1625
|
-
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1630
|
-
|
1631
|
-
|
1632
|
-
|
1633
|
-
|
1634
|
-
|
1635
|
-
|
1636
|
-
|
1637
|
-
|
1638
|
-
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
1642
|
-
|
1643
|
-
|
1644
|
-
|
1770
|
+
if (body.length == 1 && !has_nested_break(self)) {
|
1771
|
+
// This is the last case body, and we've already pruned any breaks, so it's
|
1772
|
+
// safe to hoist.
|
1773
|
+
let branch = body[0];
|
1774
|
+
return make_node(AST_If, self, {
|
1775
|
+
condition: make_node(AST_Binary, self, {
|
1776
|
+
operator: "===",
|
1777
|
+
left: self.expression,
|
1778
|
+
right: branch.expression,
|
1779
|
+
}),
|
1780
|
+
body: make_node(AST_BlockStatement, branch, {
|
1781
|
+
body: branch.body
|
1782
|
+
}),
|
1783
|
+
alternative: null
|
1784
|
+
}).optimize(compressor);
|
1785
|
+
}
|
1786
|
+
if (body.length === 2 && default_or_exact && !has_nested_break(self)) {
|
1787
|
+
let branch = body[0] === default_or_exact ? body[1] : body[0];
|
1788
|
+
let exact_exp = default_or_exact.expression && statement(default_or_exact.expression);
|
1789
|
+
if (aborts(body[0])) {
|
1790
|
+
// Only the first branch body could have a break (at the last statement)
|
1791
|
+
let first = body[0];
|
1792
|
+
if (is_break(first.body[first.body.length - 1], compressor)) {
|
1793
|
+
first.body.pop();
|
1794
|
+
}
|
1795
|
+
return make_node(AST_If, self, {
|
1796
|
+
condition: make_node(AST_Binary, self, {
|
1797
|
+
operator: "===",
|
1798
|
+
left: self.expression,
|
1799
|
+
right: branch.expression,
|
1800
|
+
}),
|
1801
|
+
body: make_node(AST_BlockStatement, branch, {
|
1802
|
+
body: branch.body
|
1803
|
+
}),
|
1804
|
+
alternative: make_node(AST_BlockStatement, default_or_exact, {
|
1805
|
+
body: [].concat(
|
1806
|
+
exact_exp || [],
|
1807
|
+
default_or_exact.body
|
1808
|
+
)
|
1809
|
+
})
|
1645
1810
|
}).optimize(compressor);
|
1646
1811
|
}
|
1812
|
+
let operator = "===";
|
1813
|
+
let consequent = make_node(AST_BlockStatement, branch, {
|
1814
|
+
body: branch.body,
|
1815
|
+
});
|
1816
|
+
let always = make_node(AST_BlockStatement, default_or_exact, {
|
1817
|
+
body: [].concat(
|
1818
|
+
exact_exp || [],
|
1819
|
+
default_or_exact.body
|
1820
|
+
)
|
1821
|
+
});
|
1822
|
+
if (body[0] === default_or_exact) {
|
1823
|
+
operator = "!==";
|
1824
|
+
let tmp = always;
|
1825
|
+
always = consequent;
|
1826
|
+
consequent = tmp;
|
1827
|
+
}
|
1828
|
+
return make_node(AST_BlockStatement, self, {
|
1829
|
+
body: [
|
1830
|
+
make_node(AST_If, self, {
|
1831
|
+
condition: make_node(AST_Binary, self, {
|
1832
|
+
operator: operator,
|
1833
|
+
left: self.expression,
|
1834
|
+
right: branch.expression,
|
1835
|
+
}),
|
1836
|
+
body: consequent,
|
1837
|
+
alternative: null
|
1838
|
+
})
|
1839
|
+
].concat(always)
|
1840
|
+
}).optimize(compressor);
|
1647
1841
|
}
|
1648
1842
|
return self;
|
1649
1843
|
|
@@ -1654,6 +1848,50 @@ def_optimize(AST_Switch, function(self, compressor) {
|
|
1654
1848
|
trim_unreachable_code(compressor, branch, decl);
|
1655
1849
|
}
|
1656
1850
|
}
|
1851
|
+
function branches_equivalent(branch, prev, insertBreak) {
|
1852
|
+
let bbody = branch.body;
|
1853
|
+
let pbody = prev.body;
|
1854
|
+
if (insertBreak) {
|
1855
|
+
bbody = bbody.concat(make_node(AST_Break));
|
1856
|
+
}
|
1857
|
+
if (bbody.length !== pbody.length) return false;
|
1858
|
+
let bblock = make_node(AST_BlockStatement, branch, { body: bbody });
|
1859
|
+
let pblock = make_node(AST_BlockStatement, prev, { body: pbody });
|
1860
|
+
return bblock.equivalent_to(pblock);
|
1861
|
+
}
|
1862
|
+
function statement(expression) {
|
1863
|
+
return make_node(AST_SimpleStatement, expression, {
|
1864
|
+
body: expression
|
1865
|
+
});
|
1866
|
+
}
|
1867
|
+
function has_nested_break(root) {
|
1868
|
+
let has_break = false;
|
1869
|
+
let tw = new TreeWalker(node => {
|
1870
|
+
if (has_break) return true;
|
1871
|
+
if (node instanceof AST_Lambda) return true;
|
1872
|
+
if (node instanceof AST_SimpleStatement) return true;
|
1873
|
+
if (!is_break(node, tw)) return;
|
1874
|
+
let parent = tw.parent();
|
1875
|
+
if (
|
1876
|
+
parent instanceof AST_SwitchBranch
|
1877
|
+
&& parent.body[parent.body.length - 1] === node
|
1878
|
+
) {
|
1879
|
+
return;
|
1880
|
+
}
|
1881
|
+
has_break = true;
|
1882
|
+
});
|
1883
|
+
root.walk(tw);
|
1884
|
+
return has_break;
|
1885
|
+
}
|
1886
|
+
function is_break(node, stack) {
|
1887
|
+
return node instanceof AST_Break
|
1888
|
+
&& stack.loopcontrol_target(node) === self;
|
1889
|
+
}
|
1890
|
+
function is_inert_body(branch) {
|
1891
|
+
return !aborts(branch) && !make_node(AST_BlockStatement, branch, {
|
1892
|
+
body: branch.body
|
1893
|
+
}).has_side_effects(compressor);
|
1894
|
+
}
|
1657
1895
|
});
|
1658
1896
|
|
1659
1897
|
def_optimize(AST_Try, function(self, compressor) {
|
@@ -1820,6 +2058,14 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
1820
2058
|
}
|
1821
2059
|
|
1822
2060
|
if (compressor.option("unsafe")) {
|
2061
|
+
if (exp instanceof AST_Dot && exp.start.value === "Array" && exp.property === "from" && self.args.length === 1) {
|
2062
|
+
const [argument] = self.args;
|
2063
|
+
if (argument instanceof AST_Array) {
|
2064
|
+
return make_node(AST_Array, argument, {
|
2065
|
+
elements: argument.elements
|
2066
|
+
}).optimize(compressor);
|
2067
|
+
}
|
2068
|
+
}
|
1823
2069
|
if (is_undeclared_ref(exp)) switch (exp.name) {
|
1824
2070
|
case "Array":
|
1825
2071
|
if (self.args.length != 1) {
|
@@ -2025,6 +2271,7 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
2025
2271
|
argnames: [],
|
2026
2272
|
body: []
|
2027
2273
|
}).optimize(compressor);
|
2274
|
+
var nth_identifier = compressor.mangle_options && compressor.mangle_options.nth_identifier || base54;
|
2028
2275
|
if (self.args.every((x) => x instanceof AST_String)) {
|
2029
2276
|
// quite a corner-case, but we can handle it:
|
2030
2277
|
// https://github.com/mishoo/UglifyJS2/issues/203
|
@@ -2034,14 +2281,13 @@ def_optimize(AST_Call, function(self, compressor) {
|
|
2034
2281
|
return arg.value;
|
2035
2282
|
}).join(",") + "){" + self.args[self.args.length - 1].value + "})";
|
2036
2283
|
var ast = parse(code);
|
2037
|
-
var mangle = { ie8: compressor.option("ie8") };
|
2284
|
+
var mangle = { ie8: compressor.option("ie8"), nth_identifier: nth_identifier };
|
2038
2285
|
ast.figure_out_scope(mangle);
|
2039
2286
|
var comp = new Compressor(compressor.options, {
|
2040
2287
|
mangle_options: compressor.mangle_options
|
2041
2288
|
});
|
2042
2289
|
ast = ast.transform(comp);
|
2043
2290
|
ast.figure_out_scope(mangle);
|
2044
|
-
base54.reset();
|
2045
2291
|
ast.compute_char_frequency(mangle);
|
2046
2292
|
ast.mangle_names(mangle);
|
2047
2293
|
var fun;
|
@@ -4239,12 +4485,15 @@ function lift_key(self, compressor) {
|
|
4239
4485
|
if (self.key.value == "constructor"
|
4240
4486
|
&& compressor.parent() instanceof AST_Class) return self;
|
4241
4487
|
if (self instanceof AST_ObjectKeyVal) {
|
4488
|
+
self.quote = self.key.quote;
|
4242
4489
|
self.key = self.key.value;
|
4243
4490
|
} else if (self instanceof AST_ClassProperty) {
|
4491
|
+
self.quote = self.key.quote;
|
4244
4492
|
self.key = make_node(AST_SymbolClassProperty, self.key, {
|
4245
4493
|
name: self.key.value
|
4246
4494
|
});
|
4247
4495
|
} else {
|
4496
|
+
self.quote = self.key.quote;
|
4248
4497
|
self.key = make_node(AST_SymbolMethod, self.key, {
|
4249
4498
|
name: self.key.value
|
4250
4499
|
});
|
@@ -321,7 +321,9 @@ export function tighten_body(statements, compressor) {
|
|
321
321
|
// Replace variable with assignment when found
|
322
322
|
if (can_replace
|
323
323
|
&& !(node instanceof AST_SymbolDeclaration)
|
324
|
-
&& lhs.equivalent_to(node)
|
324
|
+
&& lhs.equivalent_to(node)
|
325
|
+
&& !shadows(node.scope, lvalues)
|
326
|
+
) {
|
325
327
|
if (stop_if_hit) {
|
326
328
|
abort = true;
|
327
329
|
return node;
|
@@ -369,7 +371,7 @@ export function tighten_body(statements, compressor) {
|
|
369
371
|
|| node instanceof AST_PropAccess
|
370
372
|
&& (side_effects || node.expression.may_throw_on_access(compressor))
|
371
373
|
|| node instanceof AST_SymbolRef
|
372
|
-
&& (lvalues.get(node.name) || side_effects && may_modify(node))
|
374
|
+
&& ((lvalues.has(node.name) && lvalues.get(node.name).modified) || side_effects && may_modify(node))
|
373
375
|
|| node instanceof AST_VarDef && node.value
|
374
376
|
&& (lvalues.has(node.name.name) || side_effects && may_modify(node.name))
|
375
377
|
|| (sym = is_lhs(node.left, node))
|
@@ -442,8 +444,9 @@ export function tighten_body(statements, compressor) {
|
|
442
444
|
// Locate symbols which may execute code outside of scanning range
|
443
445
|
var lvalues = get_lvalues(candidate);
|
444
446
|
var lhs_local = is_lhs_local(lhs);
|
445
|
-
if (lhs instanceof AST_SymbolRef)
|
446
|
-
lvalues.set(lhs.name, false);
|
447
|
+
if (lhs instanceof AST_SymbolRef) {
|
448
|
+
lvalues.set(lhs.name, { def: lhs.definition(), modified: false });
|
449
|
+
}
|
447
450
|
var side_effects = value_has_side_effects(candidate);
|
448
451
|
var replace_all = replace_all_symbols();
|
449
452
|
var may_throw = candidate.may_throw(compressor);
|
@@ -509,8 +512,9 @@ export function tighten_body(statements, compressor) {
|
|
509
512
|
return false;
|
510
513
|
let cur_scope = def.scope;
|
511
514
|
while (cur_scope && cur_scope !== scope) {
|
512
|
-
if (cur_scope.variables.has(def.name))
|
515
|
+
if (cur_scope.variables.has(def.name)) {
|
513
516
|
return true;
|
517
|
+
}
|
514
518
|
cur_scope = cur_scope.parent_scope;
|
515
519
|
}
|
516
520
|
return false;
|
@@ -791,8 +795,14 @@ export function tighten_body(statements, compressor) {
|
|
791
795
|
var sym = node;
|
792
796
|
while (sym instanceof AST_PropAccess)
|
793
797
|
sym = sym.expression;
|
794
|
-
if (sym instanceof AST_SymbolRef
|
795
|
-
|
798
|
+
if (sym instanceof AST_SymbolRef) {
|
799
|
+
const prev = lvalues.get(sym.name);
|
800
|
+
if (!prev || !prev.modified) {
|
801
|
+
lvalues.set(sym.name, {
|
802
|
+
def: sym.definition(),
|
803
|
+
modified: is_modified(compressor, tw, node, node, 0)
|
804
|
+
});
|
805
|
+
}
|
796
806
|
}
|
797
807
|
});
|
798
808
|
get_rvalue(expr).walk(tw);
|
@@ -904,6 +914,18 @@ export function tighten_body(statements, compressor) {
|
|
904
914
|
}
|
905
915
|
return false;
|
906
916
|
}
|
917
|
+
|
918
|
+
function shadows(newScope, lvalues) {
|
919
|
+
for (const {def} of lvalues.values()) {
|
920
|
+
let current = newScope;
|
921
|
+
while (current && current !== def.scope) {
|
922
|
+
let nested_def = current.variables.get(def.name);
|
923
|
+
if (nested_def && nested_def !== def) return true;
|
924
|
+
current = current.parent_scope;
|
925
|
+
}
|
926
|
+
}
|
927
|
+
return false;
|
928
|
+
}
|
907
929
|
}
|
908
930
|
|
909
931
|
function eliminate_spurious_blocks(statements) {
|
package/lib/minify.js
CHANGED
@@ -11,10 +11,10 @@ import { AST_Toplevel, AST_Node } from "./ast.js";
|
|
11
11
|
import { parse } from "./parse.js";
|
12
12
|
import { OutputStream } from "./output.js";
|
13
13
|
import { Compressor } from "./compress/index.js";
|
14
|
-
import { base54 } from "./scope.js";
|
15
14
|
import { SourceMap } from "./sourcemap.js";
|
16
15
|
import {
|
17
16
|
mangle_properties,
|
17
|
+
mangle_private_properties,
|
18
18
|
reserve_quoted_keys,
|
19
19
|
} from "./propmangle.js";
|
20
20
|
|
@@ -203,9 +203,9 @@ async function minify(files, options) {
|
|
203
203
|
if (options.mangle) toplevel.figure_out_scope(options.mangle);
|
204
204
|
if (timings) timings.mangle = Date.now();
|
205
205
|
if (options.mangle) {
|
206
|
-
base54.reset();
|
207
206
|
toplevel.compute_char_frequency(options.mangle);
|
208
207
|
toplevel.mangle_names(options.mangle);
|
208
|
+
toplevel = mangle_private_properties(toplevel, options.mangle);
|
209
209
|
}
|
210
210
|
if (timings) timings.properties = Date.now();
|
211
211
|
if (options.mangle && options.mangle.properties) {
|
package/lib/output.js
CHANGED
@@ -349,9 +349,11 @@ function OutputStream(options) {
|
|
349
349
|
var do_add_mapping = mappings ? function() {
|
350
350
|
mappings.forEach(function(mapping) {
|
351
351
|
try {
|
352
|
-
let
|
353
|
-
if (name
|
354
|
-
name =
|
352
|
+
let { name, token } = mapping;
|
353
|
+
if (token.type == "name" || token.type === "privatename") {
|
354
|
+
name = token.value;
|
355
|
+
} else if (name instanceof AST_Symbol) {
|
356
|
+
name = token.type === "string" ? token.value : name.name;
|
355
357
|
}
|
356
358
|
options.source_map.add(
|
357
359
|
mapping.token.file,
|
@@ -1824,6 +1826,7 @@ function OutputStream(options) {
|
|
1824
1826
|
|
1825
1827
|
if (self.optional) output.print("?");
|
1826
1828
|
output.print(".#");
|
1829
|
+
output.add_mapping(self.end);
|
1827
1830
|
output.print_name(prop);
|
1828
1831
|
});
|
1829
1832
|
DEFPRINT(AST_Sub, function(self, output) {
|
@@ -2277,8 +2280,10 @@ function OutputStream(options) {
|
|
2277
2280
|
DEFMAP([
|
2278
2281
|
AST_ObjectGetter,
|
2279
2282
|
AST_ObjectSetter,
|
2283
|
+
AST_PrivateGetter,
|
2284
|
+
AST_PrivateSetter,
|
2280
2285
|
], function(output) {
|
2281
|
-
output.add_mapping(this.
|
2286
|
+
output.add_mapping(this.key.end, this.key.name);
|
2282
2287
|
});
|
2283
2288
|
|
2284
2289
|
DEFMAP([ AST_ObjectProperty ], function(output) {
|