js-confuser 1.7.3 → 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 (269) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +6 -4
  2. package/CHANGELOG.md +70 -0
  3. package/Migration.md +57 -0
  4. package/README.md +23 -929
  5. package/dist/constants.js +65 -14
  6. package/dist/index.js +108 -160
  7. package/dist/obfuscator.js +316 -118
  8. package/dist/options.js +1 -119
  9. package/dist/order.js +30 -30
  10. package/dist/presets.js +47 -45
  11. package/dist/probability.js +25 -32
  12. package/dist/templates/bufferToStringTemplate.js +9 -0
  13. package/dist/templates/deadCodeTemplates.js +9 -0
  14. package/dist/templates/getGlobalTemplate.js +19 -0
  15. package/dist/templates/integrityTemplate.js +30 -0
  16. package/dist/templates/setFunctionLengthTemplate.js +9 -0
  17. package/dist/templates/stringCompressionTemplate.js +10 -0
  18. package/dist/templates/tamperProtectionTemplates.js +21 -0
  19. package/dist/templates/template.js +199 -184
  20. package/dist/transforms/astScrambler.js +100 -0
  21. package/dist/transforms/calculator.js +70 -127
  22. package/dist/transforms/controlFlowFlattening.js +1182 -0
  23. package/dist/transforms/deadCode.js +62 -587
  24. package/dist/transforms/dispatcher.js +300 -313
  25. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +88 -189
  26. package/dist/transforms/extraction/objectExtraction.js +131 -215
  27. package/dist/transforms/finalizer.js +56 -59
  28. package/dist/transforms/flatten.js +275 -276
  29. package/dist/transforms/functionOutlining.js +230 -0
  30. package/dist/transforms/identifier/globalConcealing.js +214 -135
  31. package/dist/transforms/identifier/movedDeclarations.js +167 -91
  32. package/dist/transforms/identifier/renameVariables.js +239 -193
  33. package/dist/transforms/lock/integrity.js +61 -184
  34. package/dist/transforms/lock/lock.js +261 -387
  35. package/dist/transforms/minify.js +431 -436
  36. package/dist/transforms/opaquePredicates.js +65 -118
  37. package/dist/transforms/pack.js +160 -0
  38. package/dist/transforms/plugin.js +179 -0
  39. package/dist/transforms/preparation.js +261 -173
  40. package/dist/transforms/renameLabels.js +132 -56
  41. package/dist/transforms/rgf.js +140 -267
  42. package/dist/transforms/shuffle.js +52 -145
  43. package/dist/transforms/string/encoding.js +44 -175
  44. package/dist/transforms/string/stringCompression.js +79 -155
  45. package/dist/transforms/string/stringConcealing.js +189 -225
  46. package/dist/transforms/string/stringEncoding.js +32 -40
  47. package/dist/transforms/string/stringSplitting.js +54 -55
  48. package/dist/transforms/variableMasking.js +232 -0
  49. package/dist/utils/ControlObject.js +125 -0
  50. package/dist/utils/IntGen.js +46 -0
  51. package/dist/utils/NameGen.js +106 -0
  52. package/dist/utils/ast-utils.js +560 -0
  53. package/dist/utils/function-utils.js +56 -0
  54. package/dist/utils/gen-utils.js +48 -0
  55. package/dist/utils/node.js +77 -0
  56. package/dist/utils/object-utils.js +21 -0
  57. package/dist/utils/random-utils.js +91 -0
  58. package/dist/utils/static-utils.js +64 -0
  59. package/dist/validateOptions.js +122 -0
  60. package/index.d.ts +1 -17
  61. package/package.json +27 -22
  62. package/src/constants.ts +139 -82
  63. package/src/index.ts +70 -165
  64. package/src/obfuscationResult.ts +43 -0
  65. package/src/obfuscator.ts +328 -135
  66. package/src/options.ts +149 -658
  67. package/src/order.ts +14 -14
  68. package/src/presets.ts +39 -34
  69. package/src/probability.ts +21 -36
  70. package/src/templates/bufferToStringTemplate.ts +57 -0
  71. package/src/templates/deadCodeTemplates.ts +1185 -0
  72. package/src/templates/getGlobalTemplate.ts +72 -0
  73. package/src/templates/integrityTemplate.ts +69 -0
  74. package/src/templates/setFunctionLengthTemplate.ts +11 -0
  75. package/src/templates/stringCompressionTemplate.ts +42 -0
  76. package/src/templates/tamperProtectionTemplates.ts +116 -0
  77. package/src/templates/template.ts +149 -157
  78. package/src/transforms/astScrambler.ts +99 -0
  79. package/src/transforms/calculator.ts +96 -226
  80. package/src/transforms/controlFlowFlattening.ts +1594 -0
  81. package/src/transforms/deadCode.ts +85 -676
  82. package/src/transforms/dispatcher.ts +431 -640
  83. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +147 -295
  84. package/src/transforms/extraction/objectExtraction.ts +160 -333
  85. package/src/transforms/finalizer.ts +63 -64
  86. package/src/transforms/flatten.ts +439 -557
  87. package/src/transforms/functionOutlining.ts +225 -0
  88. package/src/transforms/identifier/globalConcealing.ts +255 -266
  89. package/src/transforms/identifier/movedDeclarations.ts +228 -142
  90. package/src/transforms/identifier/renameVariables.ts +250 -271
  91. package/src/transforms/lock/integrity.ts +85 -263
  92. package/src/transforms/lock/lock.ts +338 -579
  93. package/src/transforms/minify.ts +523 -663
  94. package/src/transforms/opaquePredicates.ts +90 -229
  95. package/src/transforms/pack.ts +195 -0
  96. package/src/transforms/plugin.ts +185 -0
  97. package/src/transforms/preparation.ts +337 -231
  98. package/src/transforms/renameLabels.ts +176 -77
  99. package/src/transforms/rgf.ts +293 -424
  100. package/src/transforms/shuffle.ts +80 -254
  101. package/src/transforms/string/encoding.ts +20 -126
  102. package/src/transforms/string/stringCompression.ts +117 -307
  103. package/src/transforms/string/stringConcealing.ts +254 -342
  104. package/src/transforms/string/stringEncoding.ts +28 -47
  105. package/src/transforms/string/stringSplitting.ts +61 -75
  106. package/src/transforms/variableMasking.ts +257 -0
  107. package/src/utils/ControlObject.ts +141 -0
  108. package/src/utils/IntGen.ts +33 -0
  109. package/src/utils/NameGen.ts +106 -0
  110. package/src/utils/ast-utils.ts +667 -0
  111. package/src/utils/function-utils.ts +50 -0
  112. package/src/utils/gen-utils.ts +48 -0
  113. package/src/utils/node.ts +78 -0
  114. package/src/utils/object-utils.ts +21 -0
  115. package/src/utils/random-utils.ts +79 -0
  116. package/src/utils/static-utils.ts +66 -0
  117. package/src/validateOptions.ts +256 -0
  118. package/tsconfig.json +13 -8
  119. package/babel.config.js +0 -12
  120. package/dev.js +0 -8
  121. package/dist/compiler.js +0 -34
  122. package/dist/parser.js +0 -59
  123. package/dist/precedence.js +0 -66
  124. package/dist/templates/bufferToString.js +0 -129
  125. package/dist/templates/core.js +0 -35
  126. package/dist/templates/crash.js +0 -28
  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 -1287
  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 -83
  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 -349
  144. package/dist/transforms/transform.js +0 -372
  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 -14
  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 -156
  154. package/dist/util/scope.js +0 -20
  155. package/docs/ControlFlowFlattening.md +0 -595
  156. package/docs/Countermeasures.md +0 -70
  157. package/docs/ES5.md +0 -197
  158. package/docs/Integrity.md +0 -82
  159. package/docs/RGF.md +0 -424
  160. package/docs/RenameVariables.md +0 -116
  161. package/docs/TamperProtection.md +0 -100
  162. package/docs/Template.md +0 -117
  163. package/samples/example.js +0 -15
  164. package/samples/high.js +0 -1
  165. package/samples/input.js +0 -3
  166. package/samples/javascriptobfuscator.com.js +0 -8
  167. package/samples/jscrambler_advanced.js +0 -1894
  168. package/samples/jscrambler_light.js +0 -1134
  169. package/samples/low.js +0 -1
  170. package/samples/medium.js +0 -1
  171. package/samples/obfuscator.io.js +0 -1686
  172. package/samples/preemptive.com.js +0 -16
  173. package/src/compiler.ts +0 -35
  174. package/src/parser.ts +0 -49
  175. package/src/precedence.ts +0 -61
  176. package/src/templates/bufferToString.ts +0 -136
  177. package/src/templates/core.ts +0 -29
  178. package/src/templates/crash.ts +0 -23
  179. package/src/templates/es5.ts +0 -131
  180. package/src/templates/functionLength.ts +0 -32
  181. package/src/templates/globals.ts +0 -3
  182. package/src/transforms/antiTooling.ts +0 -102
  183. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +0 -2153
  184. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +0 -179
  185. package/src/transforms/es5/antiClass.ts +0 -276
  186. package/src/transforms/es5/antiDestructuring.ts +0 -294
  187. package/src/transforms/es5/antiES6Object.ts +0 -267
  188. package/src/transforms/es5/antiSpreadOperator.ts +0 -56
  189. package/src/transforms/es5/antiTemplate.ts +0 -98
  190. package/src/transforms/es5/es5.ts +0 -149
  191. package/src/transforms/extraction/classExtraction.ts +0 -168
  192. package/src/transforms/identifier/globalAnalysis.ts +0 -102
  193. package/src/transforms/identifier/variableAnalysis.ts +0 -118
  194. package/src/transforms/lock/antiDebug.ts +0 -112
  195. package/src/transforms/stack.ts +0 -557
  196. package/src/transforms/transform.ts +0 -441
  197. package/src/traverse.ts +0 -120
  198. package/src/types.ts +0 -133
  199. package/src/util/compare.ts +0 -181
  200. package/src/util/gen.ts +0 -651
  201. package/src/util/guard.ts +0 -17
  202. package/src/util/identifiers.ts +0 -494
  203. package/src/util/insert.ts +0 -419
  204. package/src/util/math.ts +0 -15
  205. package/src/util/object.ts +0 -39
  206. package/src/util/random.ts +0 -221
  207. package/src/util/scope.ts +0 -21
  208. package/test/code/Cash.src.js +0 -1011
  209. package/test/code/Cash.test.ts +0 -132
  210. package/test/code/Dynamic.src.js +0 -118
  211. package/test/code/Dynamic.test.ts +0 -49
  212. package/test/code/ES6.src.js +0 -235
  213. package/test/code/ES6.test.ts +0 -42
  214. package/test/code/NewFeatures.test.ts +0 -19
  215. package/test/code/StrictMode.src.js +0 -65
  216. package/test/code/StrictMode.test.js +0 -37
  217. package/test/compare.test.ts +0 -104
  218. package/test/index.test.ts +0 -249
  219. package/test/options.test.ts +0 -150
  220. package/test/presets.test.ts +0 -22
  221. package/test/probability.test.ts +0 -44
  222. package/test/templates/template.test.ts +0 -224
  223. package/test/transforms/antiTooling.test.ts +0 -52
  224. package/test/transforms/calculator.test.ts +0 -78
  225. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +0 -1274
  226. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +0 -192
  227. package/test/transforms/deadCode.test.ts +0 -85
  228. package/test/transforms/dispatcher.test.ts +0 -457
  229. package/test/transforms/es5/antiClass.test.ts +0 -427
  230. package/test/transforms/es5/antiDestructuring.test.ts +0 -157
  231. package/test/transforms/es5/antiES6Object.test.ts +0 -245
  232. package/test/transforms/es5/antiTemplate.test.ts +0 -116
  233. package/test/transforms/es5/es5.test.ts +0 -110
  234. package/test/transforms/extraction/classExtraction.test.ts +0 -86
  235. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +0 -200
  236. package/test/transforms/extraction/objectExtraction.test.ts +0 -491
  237. package/test/transforms/flatten.test.ts +0 -721
  238. package/test/transforms/hexadecimalNumbers.test.ts +0 -62
  239. package/test/transforms/identifier/globalConcealing.test.ts +0 -142
  240. package/test/transforms/identifier/movedDeclarations.test.ts +0 -275
  241. package/test/transforms/identifier/renameVariables.test.ts +0 -695
  242. package/test/transforms/lock/antiDebug.test.ts +0 -66
  243. package/test/transforms/lock/browserLock.test.ts +0 -129
  244. package/test/transforms/lock/countermeasures.test.ts +0 -100
  245. package/test/transforms/lock/integrity.test.ts +0 -161
  246. package/test/transforms/lock/lock.test.ts +0 -204
  247. package/test/transforms/lock/osLock.test.ts +0 -312
  248. package/test/transforms/lock/selfDefending.test.ts +0 -68
  249. package/test/transforms/lock/tamperProtection.test.ts +0 -336
  250. package/test/transforms/minify.test.ts +0 -575
  251. package/test/transforms/opaquePredicates.test.ts +0 -43
  252. package/test/transforms/preparation.test.ts +0 -157
  253. package/test/transforms/renameLabels.test.ts +0 -95
  254. package/test/transforms/rgf.test.ts +0 -378
  255. package/test/transforms/shuffle.test.ts +0 -135
  256. package/test/transforms/stack.test.ts +0 -573
  257. package/test/transforms/string/stringCompression.test.ts +0 -120
  258. package/test/transforms/string/stringConcealing.test.ts +0 -299
  259. package/test/transforms/string/stringEncoding.test.ts +0 -95
  260. package/test/transforms/string/stringSplitting.test.ts +0 -135
  261. package/test/transforms/transform.test.ts +0 -66
  262. package/test/traverse.test.ts +0 -139
  263. package/test/util/compare.test.ts +0 -34
  264. package/test/util/gen.test.ts +0 -121
  265. package/test/util/identifiers.test.ts +0 -253
  266. package/test/util/insert.test.ts +0 -142
  267. package/test/util/math.test.ts +0 -5
  268. package/test/util/random.test.ts +0 -71
  269. /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, new _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
+ };