js-confuser 1.7.2 → 2.0.0-alpha.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.
Files changed (263) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +6 -4
  2. package/.github/workflows/node.js.yml +1 -1
  3. package/CHANGELOG.md +105 -0
  4. package/Migration.md +57 -0
  5. package/README.md +23 -913
  6. package/dist/constants.js +69 -13
  7. package/dist/index.js +108 -152
  8. package/dist/obfuscator.js +316 -118
  9. package/dist/options.js +1 -109
  10. package/dist/order.js +30 -30
  11. package/dist/presets.js +47 -45
  12. package/dist/probability.js +25 -32
  13. package/dist/templates/bufferToStringTemplate.js +9 -0
  14. package/dist/templates/deadCodeTemplates.js +9 -0
  15. package/dist/templates/getGlobalTemplate.js +19 -0
  16. package/dist/templates/integrityTemplate.js +30 -0
  17. package/dist/templates/setFunctionLengthTemplate.js +9 -0
  18. package/dist/templates/stringCompressionTemplate.js +10 -0
  19. package/dist/templates/tamperProtectionTemplates.js +21 -0
  20. package/dist/templates/template.js +213 -93
  21. package/dist/transforms/astScrambler.js +100 -0
  22. package/dist/transforms/calculator.js +70 -127
  23. package/dist/transforms/controlFlowFlattening.js +1182 -0
  24. package/dist/transforms/deadCode.js +62 -577
  25. package/dist/transforms/dispatcher.js +300 -309
  26. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +88 -189
  27. package/dist/transforms/extraction/objectExtraction.js +131 -215
  28. package/dist/transforms/finalizer.js +56 -59
  29. package/dist/transforms/flatten.js +275 -276
  30. package/dist/transforms/functionOutlining.js +230 -0
  31. package/dist/transforms/identifier/globalConcealing.js +217 -103
  32. package/dist/transforms/identifier/movedDeclarations.js +167 -91
  33. package/dist/transforms/identifier/renameVariables.js +240 -187
  34. package/dist/transforms/lock/integrity.js +61 -184
  35. package/dist/transforms/lock/lock.js +263 -303
  36. package/dist/transforms/minify.js +431 -436
  37. package/dist/transforms/opaquePredicates.js +65 -118
  38. package/dist/transforms/pack.js +160 -0
  39. package/dist/transforms/plugin.js +179 -0
  40. package/dist/transforms/preparation.js +263 -163
  41. package/dist/transforms/renameLabels.js +132 -56
  42. package/dist/transforms/rgf.js +142 -240
  43. package/dist/transforms/shuffle.js +52 -145
  44. package/dist/transforms/string/encoding.js +45 -173
  45. package/dist/transforms/string/stringCompression.js +81 -126
  46. package/dist/transforms/string/stringConcealing.js +189 -224
  47. package/dist/transforms/string/stringEncoding.js +32 -40
  48. package/dist/transforms/string/stringSplitting.js +54 -55
  49. package/dist/transforms/variableMasking.js +232 -0
  50. package/dist/utils/ControlObject.js +125 -0
  51. package/dist/utils/IntGen.js +46 -0
  52. package/dist/utils/NameGen.js +106 -0
  53. package/dist/utils/ast-utils.js +560 -0
  54. package/dist/utils/function-utils.js +56 -0
  55. package/dist/utils/gen-utils.js +48 -0
  56. package/dist/utils/node.js +77 -0
  57. package/dist/utils/object-utils.js +21 -0
  58. package/dist/utils/random-utils.js +91 -0
  59. package/dist/utils/static-utils.js +64 -0
  60. package/dist/validateOptions.js +122 -0
  61. package/index.d.ts +1 -17
  62. package/package.json +27 -22
  63. package/src/constants.ts +139 -77
  64. package/src/index.ts +70 -163
  65. package/src/obfuscationResult.ts +43 -0
  66. package/src/obfuscator.ts +328 -135
  67. package/src/options.ts +154 -623
  68. package/src/order.ts +14 -14
  69. package/src/presets.ts +39 -34
  70. package/src/probability.ts +21 -36
  71. package/src/templates/{bufferToString.ts → bufferToStringTemplate.ts} +5 -54
  72. package/src/templates/deadCodeTemplates.ts +1185 -0
  73. package/src/templates/getGlobalTemplate.ts +72 -0
  74. package/src/templates/integrityTemplate.ts +69 -0
  75. package/src/templates/setFunctionLengthTemplate.ts +11 -0
  76. package/src/templates/stringCompressionTemplate.ts +42 -0
  77. package/src/templates/tamperProtectionTemplates.ts +116 -0
  78. package/src/templates/template.ts +183 -92
  79. package/src/transforms/astScrambler.ts +99 -0
  80. package/src/transforms/calculator.ts +96 -224
  81. package/src/transforms/controlFlowFlattening.ts +1594 -0
  82. package/src/transforms/deadCode.ts +85 -628
  83. package/src/transforms/dispatcher.ts +431 -636
  84. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +147 -299
  85. package/src/transforms/extraction/objectExtraction.ts +160 -333
  86. package/src/transforms/finalizer.ts +63 -64
  87. package/src/transforms/flatten.ts +439 -557
  88. package/src/transforms/functionOutlining.ts +225 -0
  89. package/src/transforms/identifier/globalConcealing.ts +261 -189
  90. package/src/transforms/identifier/movedDeclarations.ts +228 -142
  91. package/src/transforms/identifier/renameVariables.ts +252 -258
  92. package/src/transforms/lock/integrity.ts +84 -260
  93. package/src/transforms/lock/lock.ts +342 -491
  94. package/src/transforms/minify.ts +523 -663
  95. package/src/transforms/opaquePredicates.ts +90 -229
  96. package/src/transforms/pack.ts +195 -0
  97. package/src/transforms/plugin.ts +185 -0
  98. package/src/transforms/preparation.ts +337 -215
  99. package/src/transforms/renameLabels.ts +176 -77
  100. package/src/transforms/rgf.ts +293 -386
  101. package/src/transforms/shuffle.ts +80 -254
  102. package/src/transforms/string/encoding.ts +26 -129
  103. package/src/transforms/string/stringCompression.ts +118 -236
  104. package/src/transforms/string/stringConcealing.ts +255 -339
  105. package/src/transforms/string/stringEncoding.ts +28 -47
  106. package/src/transforms/string/stringSplitting.ts +61 -75
  107. package/src/transforms/variableMasking.ts +257 -0
  108. package/src/utils/ControlObject.ts +141 -0
  109. package/src/utils/IntGen.ts +33 -0
  110. package/src/utils/NameGen.ts +106 -0
  111. package/src/utils/ast-utils.ts +667 -0
  112. package/src/utils/function-utils.ts +50 -0
  113. package/src/utils/gen-utils.ts +48 -0
  114. package/src/utils/node.ts +78 -0
  115. package/src/utils/object-utils.ts +21 -0
  116. package/src/utils/random-utils.ts +79 -0
  117. package/src/utils/static-utils.ts +66 -0
  118. package/src/validateOptions.ts +256 -0
  119. package/tsconfig.json +13 -8
  120. package/babel.config.js +0 -12
  121. package/dev.js +0 -8
  122. package/dist/compiler.js +0 -34
  123. package/dist/parser.js +0 -59
  124. package/dist/precedence.js +0 -66
  125. package/dist/templates/bufferToString.js +0 -108
  126. package/dist/templates/crash.js +0 -59
  127. package/dist/templates/es5.js +0 -137
  128. package/dist/templates/functionLength.js +0 -34
  129. package/dist/templates/globals.js +0 -9
  130. package/dist/transforms/antiTooling.js +0 -88
  131. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +0 -1281
  132. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +0 -131
  133. package/dist/transforms/es5/antiClass.js +0 -164
  134. package/dist/transforms/es5/antiDestructuring.js +0 -193
  135. package/dist/transforms/es5/antiES6Object.js +0 -185
  136. package/dist/transforms/es5/antiSpreadOperator.js +0 -35
  137. package/dist/transforms/es5/antiTemplate.js +0 -66
  138. package/dist/transforms/es5/es5.js +0 -123
  139. package/dist/transforms/extraction/classExtraction.js +0 -83
  140. package/dist/transforms/identifier/globalAnalysis.js +0 -70
  141. package/dist/transforms/identifier/variableAnalysis.js +0 -104
  142. package/dist/transforms/lock/antiDebug.js +0 -76
  143. package/dist/transforms/stack.js +0 -343
  144. package/dist/transforms/transform.js +0 -350
  145. package/dist/traverse.js +0 -110
  146. package/dist/util/compare.js +0 -145
  147. package/dist/util/gen.js +0 -564
  148. package/dist/util/guard.js +0 -9
  149. package/dist/util/identifiers.js +0 -355
  150. package/dist/util/insert.js +0 -362
  151. package/dist/util/math.js +0 -19
  152. package/dist/util/object.js +0 -40
  153. package/dist/util/random.js +0 -130
  154. package/dist/util/scope.js +0 -20
  155. package/docs/ControlFlowFlattening.md +0 -595
  156. package/docs/Countermeasures.md +0 -63
  157. package/docs/ES5.md +0 -197
  158. package/docs/Integrity.md +0 -75
  159. package/docs/RGF.md +0 -419
  160. package/samples/example.js +0 -15
  161. package/samples/high.js +0 -1
  162. package/samples/input.js +0 -3
  163. package/samples/javascriptobfuscator.com.js +0 -8
  164. package/samples/jscrambler_advanced.js +0 -1894
  165. package/samples/jscrambler_light.js +0 -1134
  166. package/samples/low.js +0 -1
  167. package/samples/medium.js +0 -1
  168. package/samples/obfuscator.io.js +0 -1686
  169. package/samples/preemptive.com.js +0 -16
  170. package/src/compiler.ts +0 -35
  171. package/src/parser.ts +0 -49
  172. package/src/precedence.ts +0 -61
  173. package/src/templates/crash.ts +0 -55
  174. package/src/templates/es5.ts +0 -131
  175. package/src/templates/functionLength.ts +0 -32
  176. package/src/templates/globals.ts +0 -3
  177. package/src/transforms/antiTooling.ts +0 -102
  178. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +0 -2146
  179. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +0 -179
  180. package/src/transforms/es5/antiClass.ts +0 -272
  181. package/src/transforms/es5/antiDestructuring.ts +0 -294
  182. package/src/transforms/es5/antiES6Object.ts +0 -267
  183. package/src/transforms/es5/antiSpreadOperator.ts +0 -56
  184. package/src/transforms/es5/antiTemplate.ts +0 -98
  185. package/src/transforms/es5/es5.ts +0 -149
  186. package/src/transforms/extraction/classExtraction.ts +0 -168
  187. package/src/transforms/identifier/globalAnalysis.ts +0 -85
  188. package/src/transforms/identifier/variableAnalysis.ts +0 -118
  189. package/src/transforms/lock/antiDebug.ts +0 -112
  190. package/src/transforms/stack.ts +0 -551
  191. package/src/transforms/transform.ts +0 -453
  192. package/src/traverse.ts +0 -120
  193. package/src/types.ts +0 -131
  194. package/src/util/compare.ts +0 -181
  195. package/src/util/gen.ts +0 -651
  196. package/src/util/guard.ts +0 -7
  197. package/src/util/identifiers.ts +0 -494
  198. package/src/util/insert.ts +0 -419
  199. package/src/util/math.ts +0 -15
  200. package/src/util/object.ts +0 -39
  201. package/src/util/random.ts +0 -141
  202. package/src/util/scope.ts +0 -21
  203. package/test/code/Cash.src.js +0 -1011
  204. package/test/code/Cash.test.ts +0 -49
  205. package/test/code/Dynamic.src.js +0 -118
  206. package/test/code/Dynamic.test.ts +0 -49
  207. package/test/code/ES6.src.js +0 -235
  208. package/test/code/ES6.test.ts +0 -42
  209. package/test/code/NewFeatures.test.ts +0 -19
  210. package/test/code/StrictMode.src.js +0 -65
  211. package/test/code/StrictMode.test.js +0 -37
  212. package/test/compare.test.ts +0 -104
  213. package/test/index.test.ts +0 -249
  214. package/test/options.test.ts +0 -132
  215. package/test/presets.test.ts +0 -22
  216. package/test/probability.test.ts +0 -44
  217. package/test/templates/template.test.ts +0 -14
  218. package/test/transforms/antiTooling.test.ts +0 -52
  219. package/test/transforms/calculator.test.ts +0 -78
  220. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +0 -1274
  221. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +0 -192
  222. package/test/transforms/deadCode.test.ts +0 -85
  223. package/test/transforms/dispatcher.test.ts +0 -457
  224. package/test/transforms/es5/antiClass.test.ts +0 -427
  225. package/test/transforms/es5/antiDestructuring.test.ts +0 -157
  226. package/test/transforms/es5/antiES6Object.test.ts +0 -245
  227. package/test/transforms/es5/antiTemplate.test.ts +0 -116
  228. package/test/transforms/es5/es5.test.ts +0 -110
  229. package/test/transforms/extraction/classExtraction.test.ts +0 -86
  230. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +0 -200
  231. package/test/transforms/extraction/objectExtraction.test.ts +0 -491
  232. package/test/transforms/flatten.test.ts +0 -721
  233. package/test/transforms/hexadecimalNumbers.test.ts +0 -62
  234. package/test/transforms/identifier/globalConcealing.test.ts +0 -72
  235. package/test/transforms/identifier/movedDeclarations.test.ts +0 -275
  236. package/test/transforms/identifier/renameVariables.test.ts +0 -621
  237. package/test/transforms/lock/antiDebug.test.ts +0 -66
  238. package/test/transforms/lock/browserLock.test.ts +0 -129
  239. package/test/transforms/lock/countermeasures.test.ts +0 -100
  240. package/test/transforms/lock/integrity.test.ts +0 -161
  241. package/test/transforms/lock/lock.test.ts +0 -204
  242. package/test/transforms/lock/osLock.test.ts +0 -312
  243. package/test/transforms/lock/selfDefending.test.ts +0 -68
  244. package/test/transforms/minify.test.ts +0 -575
  245. package/test/transforms/opaquePredicates.test.ts +0 -43
  246. package/test/transforms/preparation.test.ts +0 -157
  247. package/test/transforms/renameLabels.test.ts +0 -95
  248. package/test/transforms/rgf.test.ts +0 -378
  249. package/test/transforms/shuffle.test.ts +0 -135
  250. package/test/transforms/stack.test.ts +0 -573
  251. package/test/transforms/string/stringCompression.test.ts +0 -120
  252. package/test/transforms/string/stringConcealing.test.ts +0 -299
  253. package/test/transforms/string/stringEncoding.test.ts +0 -95
  254. package/test/transforms/string/stringSplitting.test.ts +0 -135
  255. package/test/transforms/transform.test.ts +0 -66
  256. package/test/traverse.test.ts +0 -139
  257. package/test/util/compare.test.ts +0 -34
  258. package/test/util/gen.test.ts +0 -121
  259. package/test/util/identifiers.test.ts +0 -253
  260. package/test/util/insert.test.ts +0 -142
  261. package/test/util/math.test.ts +0 -5
  262. package/test/util/random.test.ts +0 -71
  263. /package/dist/{types.js → obfuscationResult.js} +0 -0
@@ -1,491 +1,486 @@
1
1
  "use strict";
2
2
 
3
+ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
- exports.default = void 0;
7
- var _transform = _interopRequireDefault(require("./transform"));
7
+ exports["default"] = void 0;
8
+ var t = _interopRequireWildcard(require("@babel/types"));
8
9
  var _order = require("../order");
9
- var _gen = require("../util/gen");
10
- var _insert = require("../util/insert");
11
- var _compare = require("../util/compare");
12
- var _traverse = require("../traverse");
13
- var _assert = require("assert");
14
- var _scope = require("../util/scope");
15
- var _template = _interopRequireDefault(require("../templates/template"));
16
- var _globals = require("../templates/globals");
17
- var _identifiers = require("../util/identifiers");
18
- function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
19
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
20
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
21
- function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
10
+ var _astUtils = require("../utils/ast-utils");
11
+ var _constants = require("../constants");
12
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
13
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
14
+ function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
15
+ function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
16
+ function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
17
+ function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
18
+ function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
19
+ function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
20
+ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
21
+ var identifierMap = new Map();
22
+ identifierMap.set("undefined", function () {
23
+ return t.unaryExpression("void", t.numericLiteral(0));
24
+ });
25
+ identifierMap.set("Infinity", function () {
26
+ return t.binaryExpression("/", t.numericLiteral(1), t.numericLiteral(0));
27
+ });
28
+
22
29
  /**
23
- * Basic transformations to reduce code size.
30
+ * Minify removes unnecessary code and shortens the length for file size.
24
31
  *
25
- * Examples:
26
- * - `if(a) { b() }` **->** `a && b()`
27
- * - `if(a){b()}else{c()}` **->** `a?b():c()`
28
- * - `x['y']` **->** `x.y`
32
+ * - Dead code elimination
33
+ * - Variable grouping
34
+ * - Constant folding
35
+ * - Shorten literals: True to !0, False to !1, Infinity to 1/0, Undefined to void 0
36
+ * - Remove unused variables, functions
29
37
  */
30
- class Minify extends _transform.default {
31
- constructor(o) {
32
- super(o, _order.ObfuscateOrder.Minify);
33
- /**
34
- * A helper function that is introduced preserve function semantics
35
- */
36
- _defineProperty(this, "arrowFunctionName", void 0);
37
- }
38
- match(object, parents) {
39
- return object.hasOwnProperty("type");
40
- }
41
- transform(object, parents) {
42
- if ((0, _scope.isLexicalScope)(object)) {
43
- return () => {
44
- var body = object.type == "SwitchCase" ? object.consequent : (0, _insert.getBlockBody)(object);
45
- var earlyReturn = body.length;
46
- var fnDecs = [];
47
- body.forEach((stmt, i) => {
48
- if (stmt.type === "ReturnStatement" || stmt.type === "BreakStatement" || stmt.type === "ContinueStatement" || stmt.type === "ThrowStatement") {
49
- if (earlyReturn > i + 1) {
50
- earlyReturn = i + 1;
51
- }
52
- }
53
- if (stmt.type == "FunctionDeclaration") {
54
- fnDecs.push([stmt, i]);
55
- }
56
- });
57
- if (earlyReturn < body.length) {
58
- body.length = earlyReturn;
59
- body.push(...fnDecs.filter(x => x[1] >= earlyReturn).map(x => x[0]));
60
- }
61
-
62
- // Now combine ExpressionStatements
38
+ var _default = exports["default"] = function _default(_ref) {
39
+ var Plugin = _ref.Plugin;
40
+ var me = Plugin(_order.Order.Minify);
41
+ return {
42
+ visitor: {
43
+ Program: function Program(path) {
44
+ path.scope.crawl();
45
+ },
46
+ // var a; var b; -> var a,b;
47
+ VariableDeclaration: {
48
+ exit: function exit(path) {
49
+ if (typeof path.key !== "number") return;
50
+ var kind = path.node.kind;
63
51
 
64
- if (body.length > 1) {
65
- var exprs = [];
66
- var startIndex = -1;
67
- var sequences = [];
68
- body.forEach((stmt, i) => {
69
- if (stmt.type == "ExpressionStatement" && !stmt.directive) {
70
- exprs.push(stmt.expression);
71
- if (startIndex == -1) {
72
- startIndex = i;
52
+ // get declaration after this
53
+ var nextDeclaration = path.getSibling(path.key + 1);
54
+ if (nextDeclaration.isVariableDeclaration({
55
+ kind: kind
56
+ })) {
57
+ var _nextDeclaration$node;
58
+ // Add bindings back
59
+ var addBindingsToScope = function addBindingsToScope(scope) {
60
+ for (var name in bindings) {
61
+ var binding = bindings[name];
62
+ if (binding) {
63
+ binding.path = newBindingIdentifierPaths[name];
64
+ scope.bindings[name] = binding;
65
+ }
73
66
  }
74
- } else {
75
- if (exprs.length) {
76
- sequences.push({
77
- exprs: exprs,
78
- index: startIndex
79
- });
67
+ };
68
+ var declarations = path.get("declarations");
69
+
70
+ // Preserve bindings!
71
+ // This is important for dead code elimination
72
+ var bindings = Object.create(null);
73
+ var _iterator = _createForOfIteratorHelper(declarations),
74
+ _step;
75
+ try {
76
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
77
+ var declaration = _step.value;
78
+ for (var _i = 0, _Object$values = Object.values(declaration.getBindingIdentifierPaths()); _i < _Object$values.length; _i++) {
79
+ var idPath = _Object$values[_i];
80
+ bindings[idPath.node.name] = idPath.scope.getBinding(idPath.node.name);
81
+ }
80
82
  }
81
- exprs = [];
82
- startIndex = -1;
83
+ } catch (err) {
84
+ _iterator.e(err);
85
+ } finally {
86
+ _iterator.f();
83
87
  }
84
- });
85
- if (exprs.length) {
86
- sequences.push({
87
- exprs: exprs,
88
- index: startIndex
89
- });
90
- }
91
- sequences.reverse().forEach(seq => {
92
- (0, _assert.ok)(seq.index != -1);
93
- body.splice(seq.index, seq.exprs.length, (0, _gen.ExpressionStatement)(seq.exprs.length == 1 ? seq.exprs[0] : (0, _gen.SequenceExpression)(seq.exprs)));
94
- });
95
- }
88
+ (_nextDeclaration$node = nextDeclaration.node.declarations).unshift.apply(_nextDeclaration$node, _toConsumableArray(declarations.map(function (x) {
89
+ return x.node;
90
+ })));
91
+ var newBindingIdentifierPaths = nextDeclaration.getBindingIdentifierPaths();
96
92
 
97
- // Unnecessary return
98
- if (parents[0] && (0, _insert.isVarContext)(parents[0]) && body.length && body[body.length - 1]) {
99
- var last = body[body.length - 1];
100
- if (last.type == "ReturnStatement") {
101
- var isUndefined = last.argument == null;
102
- if (isUndefined) {
103
- body.pop();
93
+ // path.remove() unfortunately removes the bindings
94
+ // We must perverse the entire binding object (referencePaths, constantViolations, etc)
95
+ // and re-add them to the new scope
96
+ path.remove();
97
+ if (kind === "var") {
98
+ addBindingsToScope((0, _astUtils.getParentFunctionOrProgram)(path).scope);
104
99
  }
100
+ addBindingsToScope(path.scope);
105
101
  }
106
102
  }
107
-
108
- // Variable declaration grouping
109
- // var a = 1;
110
- // var b = 1;
111
- // var c = 1;
112
- //
113
- // var a=1,b=1,c=1;
114
- var lastDec = null;
115
- var remove = [];
116
- body.forEach((x, i) => {
117
- if (x.type === "VariableDeclaration") {
118
- if (!lastDec || lastDec.kind !== x.kind || !lastDec.declarations.length) {
119
- lastDec = x;
120
- } else {
121
- lastDec.declarations.push(...(0, _insert.clone)(x.declarations));
122
- remove.unshift(i);
123
- }
103
+ },
104
+ // true -> !0, false -> !1
105
+ BooleanLiteral: {
106
+ exit: function exit(path) {
107
+ if (path.node.value) {
108
+ path.replaceWith(t.unaryExpression("!", t.numericLiteral(0)));
124
109
  } else {
125
- lastDec = null;
110
+ path.replaceWith(t.unaryExpression("!", t.numericLiteral(1)));
126
111
  }
127
- });
128
- remove.forEach(x => {
129
- body.splice(x, 1);
130
- });
131
- };
132
- }
133
-
134
- /**
135
- * ES6 and higher only
136
- * - `function(){}` -> `()=>{}`
137
- * - `function abc(){}` -> `var abc = ()=>{}`
138
- */
139
- if (!this.options.es5 && (object.type == "FunctionExpression" || object.type == "FunctionDeclaration")) {
140
- return () => {
141
- // Don't touch `{get key(){...}}`
142
- var propIndex = parents.findIndex(x => x.type == "Property" || x.type == "MethodDefinition");
143
- if (propIndex !== -1) {
144
- if (parents[propIndex].value === (parents[propIndex - 1] || object)) {
145
- if (parents[propIndex].kind !== "init" || parents[propIndex].method) {
146
- return;
147
- }
112
+ }
113
+ },
114
+ // !"" -> !1
115
+ UnaryExpression: {
116
+ exit: function exit(path) {
117
+ if (path.node.operator === "!") {
118
+ var argument = path.get("argument");
119
+ if (argument.isNumericLiteral()) return;
120
+ var value = argument.evaluateTruthy();
121
+ var parent = (0, _astUtils.getParentFunctionOrProgram)(path);
122
+ if (parent && parent.node[_constants.UNSAFE]) return;
123
+ if (value === undefined) return;
124
+ path.replaceWith(t.unaryExpression("!", t.numericLiteral(value ? 1 : 0)));
148
125
  }
149
126
  }
150
- if (object.type === "FunctionDeclaration") {
151
- var body = parents[0];
152
- if (!Array.isArray(body)) {
153
- return;
127
+ },
128
+ // a["key"] -> a.key
129
+ MemberExpression: {
130
+ exit: function exit(path) {
131
+ if (!path.node.computed) return;
132
+ var property = path.get("property");
133
+ if (!property.isStringLiteral()) return;
134
+ var key = property.node.value;
135
+ if (!t.isValidIdentifier(key)) return;
136
+ path.node.computed = false;
137
+ path.node.property = t.identifier(key);
138
+ }
139
+ },
140
+ // {["key"]: 1} -> {key: 1}
141
+ // {"key": 1} -> {key: 1}
142
+ ObjectProperty: {
143
+ exit: function exit(path) {
144
+ var key = path.get("key");
145
+ if (path.node.computed && key.isStringLiteral()) {
146
+ path.node.computed = false;
154
147
  }
155
- var index = body.indexOf(object);
156
- if (index == -1) {
157
- return;
148
+ if (!path.node.computed && key.isStringLiteral() && t.isValidIdentifier(key.node.value)) {
149
+ if (identifierMap.has(key.node.value)) {
150
+ path.node.computed = true;
151
+ key.replaceWith(identifierMap.get(key.node.value)());
152
+ } else {
153
+ key.replaceWith(t.identifier(key.node.value));
154
+ }
158
155
  }
159
- var before = body.slice(0, index);
160
- (0, _assert.ok)(!before.includes(object));
161
- var beforeTypes = new Set(before.map(x => x.type));
162
- beforeTypes.delete("FunctionDeclaration");
163
- if (beforeTypes.size > 0) {
164
- return;
156
+ }
157
+ },
158
+ // (a); -> a;
159
+ SequenceExpression: {
160
+ exit: function exit(path) {
161
+ if (path.node.expressions.length === 1) {
162
+ path.replaceWith(path.node.expressions[0]);
165
163
  }
166
-
167
- // Test Variant #25: Don't break redefined function declaration
168
- if (object.id && body.find(x => x.type === "FunctionDeclaration" && x !== object && x.id && x.id.name === object.id.name)) {
169
- return;
164
+ }
165
+ },
166
+ // ; -> ()
167
+ EmptyStatement: {
168
+ exit: function exit(path) {
169
+ path.remove();
170
+ }
171
+ },
172
+ // console; -> ();
173
+ ExpressionStatement: {
174
+ exit: function exit(path) {
175
+ if (path.get("expression").isIdentifier()) {
176
+ var _path$parentPath, _path$parentPath2;
177
+ // Preserve last expression of program for RGF
178
+ if ((_path$parentPath = path.parentPath) !== null && _path$parentPath !== void 0 && _path$parentPath.isProgram() && ((_path$parentPath2 = path.parentPath) === null || _path$parentPath2 === void 0 ? void 0 : _path$parentPath2.get("body").at(-1)) === path) return;
179
+ path.remove();
170
180
  }
171
181
  }
172
- var canTransform = true;
173
- (0, _traverse.walk)(object.body, [], ($object, $parents) => {
174
- if ($object.type == "ThisExpression") {
175
- canTransform = false;
176
- } else if ($object.type == "Identifier") {
177
- if ($object.name == "arguments") {
178
- canTransform = false;
182
+ },
183
+ // undefined -> void 0
184
+ // Infinity -> 1/0
185
+ Identifier: {
186
+ exit: function exit(path) {
187
+ if (path.isReferencedIdentifier()) {
188
+ if (identifierMap.has(path.node.name)) {
189
+ (0, _astUtils.ensureComputedExpression)(path);
190
+ path.replaceWith(identifierMap.get(path.node.name)());
179
191
  }
180
- if ($object.name == "this") {
181
- this.error(new Error("Use ThisExpression instead"));
192
+ }
193
+ }
194
+ },
195
+ // true ? a : b -> a
196
+ ConditionalExpression: {
197
+ exit: function exit(path) {
198
+ var testValue = path.get("test").evaluateTruthy();
199
+ if (testValue === undefined) return;
200
+ path.replaceWith(testValue ? path.node.consequent : path.node.alternate);
201
+ }
202
+ },
203
+ // Remove unused functions
204
+ FunctionDeclaration: {
205
+ exit: function exit(path) {
206
+ var id = path.get("id");
207
+ if (id.isIdentifier() && !id.node.name.startsWith(_constants.placeholderVariablePrefix)) {
208
+ var binding = path.scope.getBinding(id.node.name);
209
+ if (binding && binding.constantViolations.length === 0 && binding.referencePaths.length === 0 && !binding.referenced) {
210
+ path.remove();
182
211
  }
183
212
  }
184
- });
185
- if (canTransform) {
186
- if (!this.arrowFunctionName) {
187
- this.arrowFunctionName = this.getPlaceholder();
188
- (0, _insert.append)(parents[parents.length - 1] || object, (0, _template.default)(`
189
- function ${this.arrowFunctionName}(arrowFn, functionLength = 0){
190
- var functionObject = function(){ return arrowFn(...arguments) };
213
+ }
214
+ },
215
+ // var x=undefined -> var x
216
+ // Remove unused variables
217
+ // Simple destructuring
218
+ VariableDeclarator: {
219
+ exit: function exit(path) {
220
+ if ((0, _astUtils.isUndefined)(path.get("init"))) {
221
+ path.node.init = null;
222
+ }
223
+ var id = path.get("id");
224
+ var init = path.get("init");
191
225
 
192
- ${this.options.preserveFunctionLength ? `return {ObjectDefineProperty}(functionObject, "length", {
193
- "value": functionLength,
194
- "configurable": true
195
- });` : `return functionObject`}
196
-
226
+ // Simple array/object destructuring
227
+ if (id.isArrayPattern() && init.isArrayExpression()) {
228
+ var elements = id.get("elements");
229
+ var initElements = init.get("elements");
230
+ if (elements.length === 1 && initElements.length === 1) {
231
+ id.replaceWith(elements[0]);
232
+ init.replaceWith(initElements[0]);
197
233
  }
198
- `).single({
199
- ObjectDefineProperty: this.options.preserveFunctionLength ? this.createInitVariable(_globals.ObjectDefineProperty, parents) : undefined
200
- }));
201
234
  }
202
- const wrap = object => {
203
- var args = [(0, _insert.clone)(object)];
204
- var fnLength = (0, _insert.computeFunctionLength)(object.params);
205
- if (this.options.preserveFunctionLength && fnLength != 0) {
206
- args.push((0, _gen.Literal)(fnLength));
207
- }
208
- return (0, _gen.CallExpression)((0, _gen.Identifier)(this.arrowFunctionName), args);
209
- };
210
- if (object.type == "FunctionExpression") {
211
- object.type = "ArrowFunctionExpression";
212
- this.replace(object, wrap((0, _insert.clone)(object)));
213
- } else {
214
- var arrow = {
215
- ...(0, _insert.clone)(object),
216
- type: "ArrowFunctionExpression"
217
- };
218
- this.replace(object, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(object.id.name, wrap(arrow))));
219
- var x = this.transform(arrow, []);
220
- if (typeof x === "function") {
221
- x();
235
+ if (id.isObjectPattern() && init.isObjectExpression()) {
236
+ var properties = id.get("properties");
237
+ var initProperties = init.get("properties");
238
+ if (properties.length === 1 && initProperties.length === 1) {
239
+ var firstProperty = properties[0];
240
+ var firstInitProperty = initProperties[0];
241
+ if (firstProperty.isObjectProperty() && firstInitProperty.isObjectProperty()) {
242
+ var firstKey = firstProperty.get("key");
243
+ var firstInitKey = firstInitProperty.get("key");
244
+ if (firstKey.isIdentifier() && firstInitKey.isIdentifier() && firstKey.node.name === firstInitKey.node.name) {
245
+ id.replaceWith(firstProperty.node.value);
246
+ init.replaceWith(firstInitProperty.node.value);
247
+ }
248
+ }
222
249
  }
223
250
  }
224
- }
225
- };
226
- }
227
251
 
228
- /**
229
- * ()=>{ expr } -> ()=>expr
230
- */
231
- if (object.type == "ArrowFunctionExpression" && object.body.type == "BlockStatement") {
232
- return () => {
233
- var body = (0, _insert.getBlockBody)(object.body);
234
- var stmt1 = body[0];
235
- if (body.length == 1 && stmt1.type == "ReturnStatement") {
236
- // x=>{a: 1} // Invalid syntax
237
- if (stmt1.argument && stmt1.argument.type != "ObjectExpression") {
238
- object.body = stmt1.argument;
239
- object.expression = true;
240
- }
241
- } else {
242
- // ()=>{exprStmt;exprStmt;} -> ()=>(expr, expr, expr, undefined)
243
- var exprs = body.filter(x => x.type == "ExpressionStatement");
244
- if (exprs.length == body.length) {
245
- var array = [];
246
- function flatten(expr) {
247
- if (expr.type == "SequenceExpression") {
248
- expr.expressions.forEach(flatten);
249
- } else if (expr.type == "ExpressionStatement") {
250
- flatten(expr.expression);
251
- } else {
252
- array.push(expr);
252
+ // Remove unused variables
253
+ // Can only remove if it's pure
254
+ if (id.isIdentifier()) {
255
+ // Do not remove variables in unsafe functions
256
+ var fn = (0, _astUtils.getParentFunctionOrProgram)(path);
257
+ if (fn.node[_constants.UNSAFE]) return;
258
+ var binding = path.scope.getBinding(id.node.name);
259
+ if (binding && binding.constantViolations.length === 0 && binding.referencePaths.length === 0) {
260
+ if (!init.node || init.isPure()) {
261
+ path.remove();
262
+ } else if (path.parentPath.isVariableDeclaration() && path.parentPath.node.declarations.length === 1) {
263
+ path.parentPath.replaceWith(t.expressionStatement(init.node));
253
264
  }
254
265
  }
255
- body.forEach(flatten);
256
- object.body = (0, _gen.SequenceExpression)([...(0, _insert.clone)(array), (0, _gen.UnaryExpression)("void", (0, _gen.Literal)(0))]);
257
266
  }
258
267
  }
259
- };
260
- }
261
-
262
- // (a()) -> a()
263
- if (object.type == "SequenceExpression") {
264
- return () => {
265
- if (object.expressions.length == 1) {
266
- this.replace(object, (0, _insert.clone)(object.expressions[0]));
267
- }
268
- };
269
- }
270
-
271
- // a += -1 -> a -= 1
272
- if (object.type == "AssignmentExpression") {
273
- return () => {
274
- if (object.operator == "+=" && object.right.type == "UnaryExpression" && object.right.operator == "-") {
275
- object.operator = "-=";
276
- object.right = object.right.argument;
277
- } else if (
278
- // a -= -1 -> a += 1
279
- object.operator == "-=" && object.right.type == "UnaryExpression" && object.right.operator == "-") {
280
- object.operator = "+=";
281
- object.right = object.right.argument;
282
- }
283
- };
284
- }
285
-
286
- // a + -b -> a - b
287
- if (object.type == "BinaryExpression") {
288
- return () => {
289
- if (object.operator == "+" && object.right.type == "UnaryExpression" && object.right.operator == "-") {
290
- object.operator = "-";
291
- object.right = object.right.argument;
292
- } else if (
293
- // a - -1 -> a + 1
294
- object.operator == "-" && object.right.type == "UnaryExpression" && object.right.operator == "-") {
295
- object.operator = "+";
296
- object.right = object.right.argument;
297
- }
298
- };
299
- }
300
- if (object.type == "ForStatement" || object.type == "ForInStatement" || object.type == "ForOfStatement" || object.type == "WhileStatement") {
301
- if (object.body.type == "BlockStatement") {
302
- return () => {
303
- if (object.body.body.length === 1) {
304
- object.body = object.body.body[0];
268
+ },
269
+ // return undefined->return
270
+ ReturnStatement: {
271
+ exit: function exit(path) {
272
+ if ((0, _astUtils.isUndefined)(path.get("argument"))) {
273
+ path.node.argument = null;
305
274
  }
306
- };
307
- }
308
- }
309
-
310
- // Last switch case does not need break
311
- if (object.type == "SwitchStatement") {
312
- var last = object.cases[object.cases.length - 1];
313
- if (last) {
314
- var lastStatement = last.consequent[last.consequent.length - 1];
315
- if (lastStatement && lastStatement.type == "BreakStatement" && lastStatement.label == null) {
316
- last.consequent.pop();
317
275
  }
318
- } else {
319
- if (object.cases.length == 0 && (object.discriminant.type == "Literal" || object.discriminant.type == "Identifier")) {
320
- if (parents[0].type == "LabeledStatement" && Array.isArray(parents[1])) {
321
- return () => {
322
- parents[1].splice(parents[1].indexOf(parents[0]), 1);
323
- };
324
- } else if (Array.isArray(parents[0])) {
325
- return () => {
326
- parents[0].splice(parents[0].indexOf(object), 1);
327
- };
276
+ },
277
+ // while(true) {a();} -> while(true) a();
278
+ // for(;;) {a();} -> for(;;) a();
279
+ // with(a) {a();} -> with(a) a();
280
+ "While|For|WithStatement": {
281
+ exit: function exit(_path) {
282
+ var path = _path;
283
+ var body = path.get("body");
284
+ if (body.isBlock() && body.node.body.length === 1) {
285
+ body.replaceWith(body.node.body[0]);
328
286
  }
329
287
  }
330
- }
331
- }
332
-
333
- // if ( x ) { y() } -> x && y()
334
- // Todo Make this shit readable
335
- if (object.type == "IfStatement") {
336
- if (object.consequent.type != "BlockStatement") {
337
- this.replace(object.consequent, (0, _gen.BlockStatement)([(0, _insert.clone)(object.consequent)]));
338
- }
339
- if (object.alternate && object.alternate.type != "BlockStatement") {
340
- this.replace(object.alternate, (0, _gen.BlockStatement)([(0, _insert.clone)(object.alternate)]));
341
- }
342
- var body = (0, _insert.getBlockBody)(object.consequent);
343
-
344
- // Check for hard-coded if statements
345
- if (object.test.type == "Literal") {
346
- if (object.test.value || object.test.regex) {
347
- // Why would anyone test just a regex literal
348
- object.alternate = null;
349
- } else {
350
- object.consequent = (0, _gen.BlockStatement)([]);
351
- }
352
- }
353
- return () => {
354
- // if ( a ) { } else {b()} -> if ( !a ) b();
355
- if (body.length == 0 && object.alternate) {
356
- object.test = (0, _gen.UnaryExpression)("!", (0, _insert.clone)(object.test));
357
- if (object.alternate.type == "BlockStatement" && object.alternate.body.length == 1) {
358
- object.alternate = (0, _insert.clone)(object.alternate.body[0]);
288
+ },
289
+ // if(a) a(); -> a && a();
290
+ // if(a) { return b; } -> if(a) return b;
291
+ // if(a) { a(); } else { b(); } -> a ? a() : b();
292
+ // if(a) { return b; } else { return c; } -> return a ? b : c;
293
+ IfStatement: {
294
+ exit: function exit(path) {
295
+ // BlockStatement to single statement
296
+ var consequent = path.get("consequent");
297
+ var alternate = path.get("alternate");
298
+ var isMoveable = function isMoveable(node) {
299
+ if (t.isDeclaration(node)) return false;
300
+ return true;
301
+ };
302
+ var testValue = path.get("test").evaluateTruthy();
303
+ var parent = (0, _astUtils.getParentFunctionOrProgram)(path);
304
+ if (parent && parent.node[_constants.UNSAFE]) {
305
+ testValue = undefined;
359
306
  }
360
- object.consequent = object.alternate;
361
- object.alternate = null;
362
- }
363
- if (object.consequent && object.consequent.body && object.consequent.body.length == 1 && object.alternate && object.alternate.body.length == 1) {
364
- var stmt1 = (0, _insert.clone)(object.consequent.body[0]);
365
- var stmt2 = (0, _insert.clone)(object.alternate.body[0]);
366
-
367
- // if (a) {return b;} else {return c;} -> return a ? b : c;
368
- if (stmt1.type == "ReturnStatement" && stmt2.type == "ReturnStatement") {
369
- this.replace(object, (0, _gen.ReturnStatement)((0, _gen.ConditionalExpression)((0, _insert.clone)(object.test), stmt1.argument || (0, _gen.Identifier)("undefined"), stmt2.argument || (0, _gen.Identifier)("undefined"))));
307
+ if (typeof testValue !== "undefined") {
308
+ if (!alternate.node && consequent.isBlock() && consequent.node.body.length === 1 && isMoveable(consequent.node.body[0])) {
309
+ consequent.replaceWith(consequent.node.body[0]);
310
+ }
311
+ if (alternate.node && alternate.isBlock() && alternate.node.body.length === 1 && isMoveable(alternate.node.body[0])) {
312
+ alternate.replaceWith(alternate.node.body[0]);
313
+ }
370
314
  }
315
+ if (testValue === false) {
316
+ // if(false){} -> ()
317
+ if (!alternate.node) {
318
+ path.remove();
319
+ return;
371
320
 
372
- // if (a) {b = 0} else {b = 1} -> b = a ? 0 : 1;
373
- if (stmt1.type == "ExpressionStatement" && stmt2.type == "ExpressionStatement") {
374
- var e1 = stmt1.expression;
375
- var e2 = stmt2.expression;
376
- if (e1.type == "AssignmentExpression" && e2.type == "AssignmentExpression") {
377
- if (e1.operator === e2.operator && (0, _compare.isEquivalent)(e1.left, e2.left)) {
378
- this.replace(object, (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)(e1.operator, e1.left, (0, _gen.ConditionalExpression)((0, _insert.clone)(object.test), e1.right, e2.right))));
321
+ // if(false){a()}else{b()} -> b()
322
+ } else {
323
+ path.replaceWith(alternate.node);
324
+ return;
325
+ }
326
+
327
+ // if(true){a()} -> {a()}
328
+ } else if (testValue === true) {
329
+ path.replaceWith(consequent.node);
330
+ return;
331
+ }
332
+ function getResult(path) {
333
+ if (!path.node) return null;
334
+ if (path.isReturnStatement()) {
335
+ return {
336
+ returnPath: path,
337
+ expressions: []
338
+ };
339
+ }
340
+ if (path.isExpressionStatement()) {
341
+ return {
342
+ returnPath: null,
343
+ expressions: [path.get("expression").node]
344
+ };
345
+ }
346
+ if (path.isBlockStatement()) {
347
+ var expressions = [];
348
+ var _iterator2 = _createForOfIteratorHelper(path.get("body")),
349
+ _step2;
350
+ try {
351
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
352
+ var statement = _step2.value;
353
+ if (statement.isReturnStatement()) {
354
+ return {
355
+ returnPath: statement,
356
+ expressions: expressions
357
+ };
358
+ } else if (statement.isExpressionStatement()) {
359
+ expressions.push(statement.get("expression").node);
360
+ } else {
361
+ return null;
362
+ }
363
+ }
364
+ } catch (err) {
365
+ _iterator2.e(err);
366
+ } finally {
367
+ _iterator2.f();
379
368
  }
369
+ return {
370
+ returnPath: null,
371
+ expressions: expressions
372
+ };
380
373
  }
374
+ return null;
381
375
  }
382
- }
383
- };
384
- }
376
+ var consequentReturn = getResult(consequent);
377
+ var alternateReturn = getResult(alternate);
378
+ if (consequentReturn && alternateReturn) {
379
+ if (consequentReturn.returnPath && alternateReturn.returnPath) {
380
+ var createReturnArgument = function createReturnArgument(resultInfo) {
381
+ return t.sequenceExpression([].concat(_toConsumableArray(resultInfo.expressions), [resultInfo.returnPath.node.argument || t.identifier("undefined")]));
382
+ };
383
+ path.replaceWith(t.returnStatement(t.conditionalExpression(path.node.test, createReturnArgument(consequentReturn), createReturnArgument(alternateReturn))));
384
+ } else if (!consequentReturn.returnPath && !alternateReturn.returnPath) {
385
+ var joinExpressions = function joinExpressions(expressions) {
386
+ // condition?():() is invalid syntax
387
+ // Just use 0 as a placeholder
388
+ if (expressions.length === 0) return t.numericLiteral(0);
385
389
 
386
- // x["abc"] -> x.abc
387
- if (object.type == "MemberExpression") {
388
- var {
389
- object: obj,
390
- property
391
- } = object;
392
- if (property.type == "Literal" && (0, _compare.isValidIdentifier)(property.value)) {
393
- object.computed = false;
394
- object.property.type = "Identifier";
395
- object.property.name = (0, _insert.clone)(object.property.value);
396
-
397
- // obj.name &&
398
- // this.log(
399
- // obj.name +
400
- // "['" +
401
- // object.property.name +
402
- // "'] -> " +
403
- // obj.name +
404
- // "." +
405
- // object.property.name
406
- // );
407
- }
408
- }
409
- if (object.type == "CallExpression") {
410
- if (object.callee.type == "MemberExpression") {
411
- var key = object.callee.computed ? object.callee.property.value : object.callee.property.name;
412
- if (key == "toString" && object.arguments.length == 0) {
413
- this.replace(object, (0, _gen.BinaryExpression)("+", (0, _gen.Literal)(""), (0, _insert.clone)(object.callee.object)));
414
- }
415
- }
416
- }
417
-
418
- // { "x": 1 } -> {x: 1}
419
- if (object.type === "Property" || object.type === "MethodDefinition") {
420
- if (object.key.type == "SequenceExpression" && object.key.expressions.length == 1) {
421
- object.key = object.key.expressions[0];
422
- object.computed = true;
423
- }
424
- if (object.key.type == "Literal" && typeof object.key.value === "string" && (0, _compare.isValidIdentifier)(object.key.value)) {
425
- object.key.type = "Identifier";
426
- object.key.name = object.key.value;
427
- object.computed = false;
428
- }
429
- }
430
- if (object.type == "VariableDeclarator") {
431
- // undefined is not necessary
432
- if (object.init && object.init.type == "Identifier") {
433
- if (object.init.name == "undefined") {
434
- object.init = null;
435
- }
436
- }
437
- if (object.id.type == "ObjectPattern" && object.init && object.init.type == "ObjectExpression") {
438
- if (object.id.properties.length === 1 && object.init.properties.length === 1) {
439
- var key1 = object.id.properties[0].computed ? object.id.properties[0].key.value : object.id.properties[0].key.name;
440
- var key2 = object.init.properties[0].computed ? object.init.properties[0].key.value : object.init.properties[0].key.name;
441
-
442
- // console.log(key1, key2);
443
-
444
- if (key1 && key2 && key1 === key2) {
445
- object.id = object.id.properties[0].value;
446
- object.init = object.init.properties[0].value;
390
+ // No need for sequence expression if there's only one expression
391
+ if (expressions.length === 1) return expressions[0];
392
+ return t.sequenceExpression(expressions);
393
+ };
394
+ path.replaceWith(t.conditionalExpression(path.node.test, joinExpressions(consequentReturn.expressions), joinExpressions(alternateReturn.expressions)));
395
+ }
447
396
  }
448
397
  }
449
- }
450
-
451
- // check for redundant patterns
452
- if (object.id.type == "ArrayPattern" && object.init && typeof object.init === "object" && object.init.type == "ArrayExpression") {
453
- if (object.id.elements.length == 1 && object.init.elements.length == 1) {
454
- object.id = object.id.elements[0];
455
- object.init = object.init.elements[0];
456
- }
457
- }
458
- }
459
- if (object.type == "Literal") {
460
- return () => {
461
- switch (typeof object.value) {
462
- case "boolean":
463
- this.replaceIdentifierOrLiteral(object, (0, _gen.UnaryExpression)("!", (0, _gen.Literal)(object.value ? 0 : 1)), parents);
464
- break;
465
- }
466
- };
467
- }
468
- if (object.type == "Identifier") {
469
- return () => {
470
- var info = (0, _identifiers.getIdentifierInfo)(object, parents);
471
- if (info.spec.isDefined || info.spec.isModified) return;
472
- if (object.name == "undefined" && !(0, _insert.isForInitialize)(object, parents)) {
473
- this.replaceIdentifierOrLiteral(object, (0, _gen.UnaryExpression)("void", (0, _gen.Literal)(0)), parents);
474
- } else if (object.name == "Infinity") {
475
- this.replaceIdentifierOrLiteral(object, (0, _gen.BinaryExpression)("/", (0, _gen.Literal)(1), (0, _gen.Literal)(0)), parents);
398
+ },
399
+ // Remove unreachable code
400
+ // Code after a return/throw/break/continue is unreachable
401
+ // Remove implied returns
402
+ // Remove code after if all branches are unreachable
403
+ "Block|SwitchCase": {
404
+ enter: function enter(path) {
405
+ if (path.isProgram()) {
406
+ path.scope.crawl();
407
+ }
408
+ },
409
+ exit: function exit(path) {
410
+ var statementList = path.isBlock() ? path.get("body") : path.get("consequent");
411
+ var impliedReturn;
412
+ function isUnreachable(statementList) {
413
+ var topLevel = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
414
+ var unreachableState = false;
415
+ var _iterator3 = _createForOfIteratorHelper(statementList),
416
+ _step3;
417
+ try {
418
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
419
+ var statement = _step3.value;
420
+ if (unreachableState) {
421
+ statement.remove();
422
+ continue;
423
+ }
424
+ if (statement.isIfStatement()) {
425
+ var consequent = statement.get("consequent");
426
+ var alternate = statement.get("alternate");
427
+ if ([consequent, alternate].every(function (x) {
428
+ return x.node && x.isBlockStatement() && isUnreachable(x.get("body"));
429
+ })) {
430
+ unreachableState = true;
431
+ if (!topLevel) {
432
+ return true;
433
+ } else {
434
+ continue;
435
+ }
436
+ }
437
+ }
438
+ if (statement.isSwitchStatement()) {
439
+ // Can only remove switch statements if all cases are unreachable
440
+ // And all paths are exhausted
441
+ var cases = statement.get("cases");
442
+ var hasDefaultCase = cases.some(function (x) {
443
+ return !x.node.test;
444
+ });
445
+ if (hasDefaultCase && cases.every(function (x) {
446
+ return isUnreachable(x.get("consequent"));
447
+ })) {
448
+ unreachableState = true;
449
+ if (!topLevel) {
450
+ return true;
451
+ } else {
452
+ continue;
453
+ }
454
+ }
455
+ }
456
+ if (statement.isReturnStatement() || statement.isThrowStatement() || statement.isBreakStatement() || statement.isContinueStatement()) {
457
+ unreachableState = true;
458
+ if (!topLevel) {
459
+ return true;
460
+ }
461
+ }
462
+ if (topLevel) {
463
+ if (statement == statementList.at(-1) && statement.isReturnStatement() && !statement.node.argument) {
464
+ impliedReturn = statement;
465
+ }
466
+ }
467
+ }
468
+ } catch (err) {
469
+ _iterator3.e(err);
470
+ } finally {
471
+ _iterator3.f();
472
+ }
473
+ return false;
474
+ }
475
+ isUnreachable(statementList, true);
476
+ if (impliedReturn) {
477
+ var functionParent = path.getFunctionParent();
478
+ if (functionParent && t.isBlockStatement(functionParent.node.body) && functionParent.node.body === path.node) {
479
+ impliedReturn.remove();
480
+ }
481
+ }
476
482
  }
477
- };
478
- }
479
- if (object.type == "UnaryExpression" && object.operator == "!") {
480
- if (object.argument.type == "Literal" && !object.argument.regex) {
481
- this.replace(object, (0, _gen.Literal)(!object.argument.value));
482
- }
483
- }
484
- if (object.type == "ConditionalExpression") {
485
- if (object.test.type == "Literal" && !object.test.regex) {
486
- this.replace(object, object.test.value ? object.consequent : object.alternate);
487
483
  }
488
484
  }
489
- }
490
- }
491
- exports.default = Minify;
485
+ };
486
+ };