js-confuser 1.6.0 → 1.7.1

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 (67) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +215 -172
  3. package/dist/constants.js +6 -2
  4. package/dist/obfuscator.js +0 -6
  5. package/dist/options.js +4 -4
  6. package/dist/presets.js +6 -7
  7. package/dist/templates/crash.js +2 -2
  8. package/dist/templates/functionLength.js +16 -0
  9. package/dist/transforms/dispatcher.js +10 -1
  10. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +95 -59
  11. package/dist/transforms/extraction/objectExtraction.js +6 -1
  12. package/dist/transforms/flatten.js +224 -147
  13. package/dist/transforms/identifier/movedDeclarations.js +38 -85
  14. package/dist/transforms/identifier/renameVariables.js +94 -41
  15. package/dist/transforms/lock/lock.js +0 -37
  16. package/dist/transforms/minify.js +2 -2
  17. package/dist/transforms/rgf.js +145 -244
  18. package/dist/transforms/stack.js +42 -1
  19. package/dist/transforms/transform.js +1 -1
  20. package/dist/util/gen.js +2 -1
  21. package/dist/util/identifiers.js +38 -4
  22. package/dist/util/insert.js +24 -3
  23. package/docs/ControlFlowFlattening.md +595 -0
  24. package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
  25. package/docs/ES5.md +197 -0
  26. package/{Integrity.md → docs/Integrity.md} +2 -2
  27. package/docs/RGF.md +419 -0
  28. package/package.json +2 -2
  29. package/src/constants.ts +3 -0
  30. package/src/obfuscator.ts +0 -4
  31. package/src/options.ts +9 -86
  32. package/src/presets.ts +6 -7
  33. package/src/templates/crash.ts +10 -10
  34. package/src/templates/functionLength.ts +14 -0
  35. package/src/transforms/dispatcher.ts +15 -1
  36. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +135 -130
  37. package/src/transforms/extraction/objectExtraction.ts +4 -0
  38. package/src/transforms/flatten.ts +357 -290
  39. package/src/transforms/identifier/movedDeclarations.ts +50 -96
  40. package/src/transforms/identifier/renameVariables.ts +120 -56
  41. package/src/transforms/lock/lock.ts +1 -42
  42. package/src/transforms/minify.ts +11 -2
  43. package/src/transforms/rgf.ts +221 -402
  44. package/src/transforms/stack.ts +62 -0
  45. package/src/transforms/transform.ts +6 -2
  46. package/src/util/gen.ts +7 -2
  47. package/src/util/identifiers.ts +48 -4
  48. package/src/util/insert.ts +26 -2
  49. package/test/code/ES6.src.js +24 -0
  50. package/test/transforms/dispatcher.test.ts +27 -0
  51. package/test/transforms/extraction/duplicateLiteralsRemoval.test.ts +21 -8
  52. package/test/transforms/extraction/objectExtraction.test.ts +35 -15
  53. package/test/transforms/flatten.test.ts +352 -88
  54. package/test/transforms/identifier/globalConcealing.test.ts +23 -2
  55. package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
  56. package/test/transforms/identifier/renameVariables.test.ts +37 -0
  57. package/test/transforms/lock/integrity.test.ts +24 -0
  58. package/test/transforms/lock/lock.test.ts +1 -48
  59. package/test/transforms/minify.test.ts +19 -0
  60. package/test/transforms/rgf.test.ts +262 -353
  61. package/test/transforms/stack.test.ts +52 -0
  62. package/test/util/identifiers.test.ts +134 -1
  63. package/test/util/insert.test.ts +57 -3
  64. package/src/transforms/eval.ts +0 -89
  65. package/src/transforms/identifier/nameRecycling.ts +0 -280
  66. package/test/transforms/eval.test.ts +0 -131
  67. package/test/transforms/identifier/nameRecycling.test.ts +0 -205
package/dist/options.js CHANGED
@@ -12,7 +12,7 @@ var _presets = _interopRequireDefault(require("./presets"));
12
12
 
13
13
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
14
 
15
- const validProperties = new Set(["preset", "target", "indent", "compact", "hexadecimalNumbers", "minify", "es5", "renameVariables", "renameGlobals", "identifierGenerator", "nameRecycling", "controlFlowFlattening", "globalConcealing", "stringCompression", "stringConcealing", "stringEncoding", "stringSplitting", "duplicateLiteralsRemoval", "dispatcher", "eval", "rgf", "objectExtraction", "flatten", "deadCode", "calculator", "lock", "movedDeclarations", "opaquePredicates", "shuffle", "stack", "verbose", "globalVariables", "debugComments"]);
15
+ const validProperties = new Set(["preset", "target", "indent", "compact", "hexadecimalNumbers", "minify", "es5", "renameVariables", "renameGlobals", "identifierGenerator", "controlFlowFlattening", "globalConcealing", "stringCompression", "stringConcealing", "stringEncoding", "stringSplitting", "duplicateLiteralsRemoval", "dispatcher", "rgf", "objectExtraction", "flatten", "deadCode", "calculator", "lock", "movedDeclarations", "opaquePredicates", "shuffle", "stack", "verbose", "globalVariables", "debugComments"]);
16
16
  const validOses = new Set(["windows", "linux", "osx", "ios", "android"]);
17
17
  const validBrowsers = new Set(["firefox", "chrome", "iexplorer", "edge", "safari", "opera"]);
18
18
 
@@ -117,13 +117,13 @@ async function correctOptions(options) {
117
117
 
118
118
  if (options.target == "browser") {
119
119
  // browser
120
- ["window", "document", "postMessage", "alert", "confirm", "location"].forEach(x => options.globalVariables.add(x));
120
+ ["window", "document", "postMessage", "alert", "confirm", "location", "btoa", "atob", "unescape", "encodeURIComponent"].forEach(x => options.globalVariables.add(x));
121
121
  } else {
122
122
  // node
123
- ["global", "Buffer", "require", "process", "__dirname", "__filename"].forEach(x => options.globalVariables.add(x));
123
+ ["global", "Buffer", "require", "process", "exports", "module", "__dirname", "__filename"].forEach(x => options.globalVariables.add(x));
124
124
  }
125
125
 
126
- ["globalThis", "console", "parseInt", "parseFloat", "Math", "Promise", "String", "Boolean", "Function", "Object", "Array", "Proxy", "Error", "TypeError", "ReferenceError", "RangeError", "EvalError", "setTimeout", "clearTimeout", "setInterval", "clearInterval", "setImmediate", "clearImmediate", "queueMicrotask", "exports", "module", "isNaN", "isFinite", "Set", "Map", "WeakSet", "WeakMap", "Symbol"].forEach(x => options.globalVariables.add(x));
126
+ ["globalThis", "console", "parseInt", "parseFloat", "Math", "JSON", "Promise", "String", "Boolean", "Function", "Object", "Array", "Proxy", "Error", "TypeError", "ReferenceError", "RangeError", "EvalError", "setTimeout", "clearTimeout", "setInterval", "clearInterval", "setImmediate", "clearImmediate", "queueMicrotask", "isNaN", "isFinite", "Set", "Map", "WeakSet", "WeakMap", "Symbol"].forEach(x => options.globalVariables.add(x));
127
127
  }
128
128
 
129
129
  return options;
package/dist/presets.js CHANGED
@@ -22,7 +22,7 @@ exports.default = void 0;
22
22
  * 10. Minified output
23
23
  *
24
24
  * ## **`Disabled features`**
25
- * - `eval` Use at your own risk!
25
+ * - `rgf` Use at your own risk!
26
26
  *
27
27
  * ### Potential Issues
28
28
  * 1. *String Encoding* can corrupt files. Disable `stringEncoding` manually if this happens.
@@ -57,7 +57,6 @@ const highPreset = {
57
57
  stringEncoding: true,
58
58
  stringSplitting: 0.75,
59
59
  // Use at own risk
60
- eval: false,
61
60
  rgf: false
62
61
  };
63
62
  /**
@@ -71,9 +70,9 @@ const mediumPreset = {
71
70
  calculator: true,
72
71
  compact: true,
73
72
  hexadecimalNumbers: true,
74
- controlFlowFlattening: 0.5,
73
+ controlFlowFlattening: 0.25,
75
74
  deadCode: 0.025,
76
- dispatcher: 0.75,
75
+ dispatcher: 0.5,
77
76
  duplicateLiteralsRemoval: 0.5,
78
77
  globalConcealing: true,
79
78
  identifierGenerator: "randomized",
@@ -99,10 +98,10 @@ const lowPreset = {
99
98
  calculator: true,
100
99
  compact: true,
101
100
  hexadecimalNumbers: true,
102
- controlFlowFlattening: 0.25,
101
+ controlFlowFlattening: 0.1,
103
102
  deadCode: 0.01,
104
- dispatcher: 0.5,
105
- duplicateLiteralsRemoval: true,
103
+ dispatcher: 0.25,
104
+ duplicateLiteralsRemoval: 0.5,
106
105
  identifierGenerator: "randomized",
107
106
  minify: true,
108
107
  movedDeclarations: true,
@@ -9,9 +9,9 @@ var _template = _interopRequireDefault(require("./template"));
9
9
 
10
10
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
11
 
12
- const CrashTemplate1 = (0, _template.default)("\nvar {var} = \"a\";\nwhile(1){\n {var} = {var} += \"a\"; //add as much as the browser can handle\n}\n");
12
+ const CrashTemplate1 = (0, _template.default)("\nvar {var} = \"a\";\nwhile(1){\n {var} = {var} += \"a\";\n}\n");
13
13
  exports.CrashTemplate1 = CrashTemplate1;
14
14
  const CrashTemplate2 = (0, _template.default)("\nwhile(true) {\n var {var} = 99;\n for({var} = 99; {var} == {var}; {var} *= {var}) {\n !{var} && console.log({var});\n if ({var} <= 10){\n break;\n }\n };\n if({var} === 100) {\n {var}--\n }\n };");
15
15
  exports.CrashTemplate2 = CrashTemplate2;
16
- const CrashTemplate3 = (0, _template.default)("\ntry {\n function {$2}(y, x){\n return x;\n }\n \n var {$1} = {$2}(this, function () {\n var {$3} = function () {\n var regExp = {$3}\n .constructor('return /\" + this + \"/')()\n .constructor('^([^ ]+( +[^ ]+)+)+[^ ]}');\n \n return !regExp.call({$1});\n };\n \n return {$3}();\n });\n \n {$1}();\n} catch ( e ) {\n while(e ? e : !e){\n var b;\n var c = 0;\n (e ? !e : e) ? (function(e){\n c = e ? 0 : !e ? 1 : 0;\n })(e) : b = 1;\n\n if(b&&c){break;}\n if(b){continue;}\n }\n}\n");
16
+ const CrashTemplate3 = (0, _template.default)("\ntry {\n function {$2}(y, x){\n return x;\n }\n \n var {$1} = {$2}(this, function () {\n var {$3} = function () {\n var regExp = {$3}\n .constructor('return /\" + this + \"/')()\n .constructor('^([^ ]+( +[^ ]+)+)+[^ ]}');\n \n return !regExp.call({$1});\n };\n \n return {$3}();\n });\n \n {$1}();\n} catch ( {$1}e ) {\n while({$1}e ? {$1}e : !{$1}e){\n var {$1}b;\n var {$1}c = 0;\n ({$1}e ? !{$1}e : {$1}e) ? (function({$1}e){\n {$1}c = {$1}e ? 0 : !{$1}e ? 1 : 0;\n })({$1}e) : {$1}b = 1;\n\n if({$1}b&&{$1}c){break;}\n if({$1}b){continue;}\n }\n}\n");
17
17
  exports.CrashTemplate3 = CrashTemplate3;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.FunctionLengthTemplate = void 0;
7
+
8
+ var _template = _interopRequireDefault(require("./template"));
9
+
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+
12
+ /**
13
+ * Helper function to set `function.length` property.
14
+ */
15
+ const FunctionLengthTemplate = (0, _template.default)("\nfunction {name}(functionObject, functionLength){\n Object[\"defineProperty\"](functionObject, \"length\", {\n \"value\": functionLength,\n \"configurable\": true\n });\n return functionObject;\n}\n");
16
+ exports.FunctionLengthTemplate = FunctionLengthTemplate;
@@ -56,9 +56,12 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
56
56
  * 3. using `this`
57
57
  */
58
58
  class Dispatcher extends _transform.default {
59
+ // Debug mode preserves function names
59
60
  constructor(o) {
60
61
  super(o, _order.ObfuscateOrder.Dispatcher);
61
62
 
63
+ _defineProperty(this, "isDebug", false);
64
+
62
65
  _defineProperty(this, "count", void 0);
63
66
 
64
67
  this.count = 0;
@@ -120,6 +123,12 @@ class Dispatcher extends _transform.default {
120
123
  illegalFnNames.add(name);
121
124
  return "EXIT";
122
125
  }
126
+ } // Avoid functions with function expressions as they have a different scope
127
+
128
+
129
+ if ((oo.type === "FunctionExpression" || oo.type === "ArrowFunctionExpression") && pp.find(x => x == o.params)) {
130
+ illegalFnNames.add(name);
131
+ return "EXIT";
123
132
  }
124
133
  });
125
134
  functionDeclarations[name] = [o, p];
@@ -158,7 +167,7 @@ class Dispatcher extends _transform.default {
158
167
 
159
168
  var gen = this.getGenerator();
160
169
  Object.keys(functionDeclarations).forEach(name => {
161
- newFnNames[name] = gen.generate();
170
+ newFnNames[name] = this.isDebug ? "_dispatcher_" + this.count + "_" + name : gen.generate();
162
171
  }); // set containing new name
163
172
 
164
173
  var set = new Set(Object.keys(newFnNames)); // Only make a dispatcher function if it caught any functions
@@ -23,6 +23,10 @@ var _assert = require("assert");
23
23
 
24
24
  var _random = require("../../util/random");
25
25
 
26
+ var _traverse = require("../../traverse");
27
+
28
+ var _identifiers = require("../../util/identifiers");
29
+
26
30
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
27
31
 
28
32
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -46,12 +50,19 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
46
50
  * ```
47
51
  */
48
52
  class DuplicateLiteralsRemoval extends _transform.default {
53
+ // The array holding all the duplicate literals
54
+ // The array expression node to be inserted into the program
55
+
56
+ /**
57
+ * Literals in the array
58
+ */
59
+
49
60
  /**
50
- * getter fn name -> accumulative shift
61
+ * Literals are saved here the first time they are seen.
51
62
  */
52
63
 
53
64
  /**
54
- * lex context -> getter fn name
65
+ * Block -> { functionName, indexShift }
55
66
  */
56
67
  constructor(o) {
57
68
  super(o, _order.ObfuscateOrder.DuplicateLiteralsRemoval);
@@ -64,23 +75,45 @@ class DuplicateLiteralsRemoval extends _transform.default {
64
75
 
65
76
  _defineProperty(this, "first", void 0);
66
77
 
67
- _defineProperty(this, "fnShifts", void 0);
68
-
69
- _defineProperty(this, "fnGetters", void 0);
78
+ _defineProperty(this, "functions", void 0);
70
79
 
71
80
  this.map = new Map();
72
81
  this.first = new Map();
73
- this.fnShifts = new Map();
74
- this.fnGetters = new Map();
82
+ this.functions = new Map();
75
83
  }
76
84
 
77
85
  apply(tree) {
78
86
  super.apply(tree);
79
87
 
80
- if (this.arrayName && this.arrayExpression.elements.length) {
88
+ if (this.arrayName && this.arrayExpression.elements.length > 0) {
89
+ // This function simply returns the array
81
90
  var getArrayFn = this.getPlaceholder();
82
- (0, _insert.append)(tree, (0, _gen.FunctionDeclaration)(getArrayFn, [], [(0, _gen.ReturnStatement)(this.arrayExpression)]));
83
- (0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(getArrayFn), (0, _gen.Identifier)("call"), false), [(0, _gen.ThisExpression)()]))));
91
+ (0, _insert.append)(tree, (0, _gen.FunctionDeclaration)(getArrayFn, [], [(0, _gen.ReturnStatement)(this.arrayExpression)])); // This variable holds the array
92
+
93
+ (0, _insert.prepend)(tree, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(this.arrayName, (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(getArrayFn), (0, _gen.Literal)("call"), true), [(0, _gen.ThisExpression)()])))); // Create all the functions needed
94
+
95
+ for (var blockNode of this.functions.keys()) {
96
+ var {
97
+ functionName,
98
+ indexShift
99
+ } = this.functions.get(blockNode);
100
+ var propertyNode = (0, _gen.BinaryExpression)("-", (0, _gen.Identifier)("index_param"), (0, _gen.Literal)(indexShift));
101
+ var indexRangeInclusive = [0 + indexShift - 1, this.map.size + indexShift]; // The function uses mangling to hide the index being accessed
102
+
103
+ var mangleCount = (0, _random.getRandomInteger)(1, 10);
104
+
105
+ for (var i = 0; i < mangleCount; i++) {
106
+ var operator = (0, _random.choice)([">", "<"]);
107
+ var compareValue = (0, _random.choice)(indexRangeInclusive);
108
+ var test = (0, _gen.BinaryExpression)(operator, (0, _gen.Identifier)("index_param"), (0, _gen.Literal)(compareValue));
109
+ var alternate = (0, _gen.BinaryExpression)("-", (0, _gen.Identifier)("index_param"), (0, _gen.Literal)((0, _random.getRandomInteger)(-100, 100)));
110
+ var testValue = operator === ">" && compareValue === indexRangeInclusive[0] || operator === "<" && compareValue === indexRangeInclusive[1];
111
+ propertyNode = (0, _gen.ConditionalExpression)(test, testValue ? propertyNode : alternate, !testValue ? propertyNode : alternate);
112
+ }
113
+
114
+ var returnArgument = (0, _gen.MemberExpression)((0, _gen.Identifier)(this.arrayName), propertyNode, true);
115
+ (0, _insert.prepend)(blockNode, (0, _gen.FunctionDeclaration)(functionName, [(0, _gen.Identifier)("index_param")], [(0, _gen.ReturnStatement)(returnArgument)]));
116
+ }
84
117
  }
85
118
  }
86
119
 
@@ -95,48 +128,47 @@ class DuplicateLiteralsRemoval extends _transform.default {
95
128
  */
96
129
 
97
130
 
98
- toCaller(object, parents, index) {
99
- // get all the getters defined here or higher
100
- var getterNames = [object, ...parents].map(x => this.fnGetters.get(x)).filter(x => x); // use random getter function
101
-
102
- var getterName = (0, _random.choice)(getterNames); // get this literals context
131
+ transformLiteral(object, parents, index) {
132
+ var blockNode = (0, _random.choice)(parents.filter(x => this.functions.has(x))); // Create initial function if none exist
103
133
 
104
- var lexContext = (0, _insert.getLexContext)(object, parents);
105
- var hasGetterHere = this.fnGetters.has(lexContext); // create one if none are available (or by random chance if none are here locally)
134
+ if (this.functions.size === 0) {
135
+ var root = parents[parents.length - 1];
136
+ var rootFunctionName = this.getPlaceholder() + "_dLR_0";
137
+ this.functions.set(root, {
138
+ functionName: rootFunctionName,
139
+ indexShift: (0, _random.getRandomInteger)(-100, 100)
140
+ });
141
+ blockNode = root;
142
+ } // If no function here exist, possibly create new chained function
106
143
 
107
- var shouldCreateNew = !getterName || !hasGetterHere && Math.random() > 0.9;
108
144
 
109
- if (shouldCreateNew) {
110
- (0, _assert.ok)(!this.fnGetters.has(lexContext));
111
- var lexContextIndex = parents.findIndex(x => x !== lexContext && (0, _insert.isLexContext)(x));
112
- var basedOn = lexContextIndex !== -1 ? (0, _random.choice)(parents.slice(lexContextIndex + 1).map(x => this.fnGetters.get(x)).filter(x => x)) : null;
113
- var body = [];
114
- var thisShift = (0, _random.getRandomInteger)(-250, 250); // the name of the getter
145
+ var block = (0, _traverse.getBlock)(object, parents);
115
146
 
116
- getterName = this.getPlaceholder() + "_dLR_" + this.fnGetters.size;
147
+ if (!this.functions.has(block) && (0, _random.chance)(50 - this.functions.size)) {
148
+ var newFunctionName = this.getPlaceholder() + "_dLR_" + this.functions.size;
149
+ this.functions.set(block, {
150
+ functionName: newFunctionName,
151
+ indexShift: (0, _random.getRandomInteger)(-100, 100)
152
+ });
153
+ blockNode = block;
154
+ } // Derive the function to call from the selected blockNode
117
155
 
118
- if (basedOn) {
119
- var shift = this.fnShifts.get(basedOn);
120
- (0, _assert.ok)(typeof shift === "number");
121
- body = [(0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.Identifier)(basedOn), [(0, _gen.BinaryExpression)("+", (0, _gen.Identifier)("index"), (0, _gen.Literal)(thisShift))]))];
122
- this.fnShifts.set(getterName, shift + thisShift);
123
- } else {
124
- // from scratch
125
- body = [(0, _gen.ReturnStatement)((0, _gen.MemberExpression)((0, _gen.Identifier)(this.arrayName), (0, _gen.BinaryExpression)("+", (0, _gen.Identifier)("index"), (0, _gen.Literal)(thisShift)), true))];
126
- this.fnShifts.set(getterName, thisShift);
127
- }
128
156
 
129
- this.fnGetters.set(lexContext, getterName);
130
- (0, _insert.prepend)(lexContext, (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(getterName, (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.FunctionExpression)([(0, _gen.Identifier)("index")], body))]), []))));
131
- }
157
+ var {
158
+ functionName,
159
+ indexShift
160
+ } = this.functions.get(blockNode); // Call the function given it's indexShift
132
161
 
133
- var theShift = this.fnShifts.get(getterName);
134
- this.replaceIdentifierOrLiteral(object, (0, _gen.CallExpression)((0, _gen.Identifier)(getterName), [(0, _gen.Literal)(index - theShift)]), parents);
162
+ var callExpression = (0, _gen.CallExpression)((0, _gen.Identifier)(functionName), [(0, _gen.Literal)(index + indexShift)]);
163
+ this.replaceIdentifierOrLiteral(object, callExpression, parents);
135
164
  }
136
165
 
137
166
  transform(object, parents) {
138
167
  return () => {
139
- var value = object.value;
168
+ if (object.type === "Identifier") {
169
+ var info = (0, _identifiers.getIdentifierInfo)(object, parents);
170
+ if (info.isLabel || info.spec.isDefined) return;
171
+ }
140
172
 
141
173
  if (object.regex) {
142
174
  return;
@@ -147,19 +179,19 @@ class DuplicateLiteralsRemoval extends _transform.default {
147
179
  } // HARD CODED LIMIT of 10,000 (after 1,000 elements)
148
180
 
149
181
 
150
- if (this.map.size > 1000 && !(0, _random.chance)(this.map.size / 100)) return;
182
+ if (this.map.size > 1000 && (0, _random.chance)(this.map.size / 100)) return;
151
183
 
152
184
  if (this.arrayName && parents[0].object && parents[0].object.name == this.arrayName) {
153
185
  return;
154
186
  }
155
187
 
156
- var value;
188
+ var stringValue;
157
189
 
158
190
  if (object.type == "Literal") {
159
- value = typeof object.value + ":" + object.value;
191
+ stringValue = typeof object.value + ":" + object.value;
160
192
 
161
193
  if (object.value === null) {
162
- value = "null:null";
194
+ stringValue = "null:null";
163
195
  } else {
164
196
  // Skip empty strings
165
197
  if (typeof object.value === "string" && !object.value) {
@@ -167,38 +199,42 @@ class DuplicateLiteralsRemoval extends _transform.default {
167
199
  }
168
200
  }
169
201
  } else if (object.type == "Identifier") {
170
- value = "identifier:" + object.name;
202
+ stringValue = "identifier:" + object.name;
171
203
  } else {
172
204
  throw new Error("Unsupported primitive type: " + object.type);
173
205
  }
174
206
 
175
- (0, _assert.ok)(value);
207
+ (0, _assert.ok)(stringValue);
176
208
 
177
- if (!this.first.has(value) && !this.map.has(value)) {
178
- this.first.set(value, [object, parents]);
179
- } else {
209
+ if (this.map.has(stringValue) || this.first.has(stringValue)) {
210
+ // Create the array if not already made
180
211
  if (!this.arrayName) {
181
212
  this.arrayName = this.getPlaceholder();
182
213
  this.arrayExpression = (0, _gen.ArrayExpression)([]);
183
- }
214
+ } // Delete with first location
184
215
 
185
- var firstLocation = this.first.get(value);
216
+
217
+ var firstLocation = this.first.get(stringValue);
186
218
 
187
219
  if (firstLocation) {
188
- this.first.set(value, null);
189
220
  var index = this.map.size;
190
- (0, _assert.ok)(!this.map.has(value));
191
- this.map.set(value, index);
221
+ (0, _assert.ok)(!this.map.has(stringValue));
222
+ this.map.set(stringValue, index);
223
+ this.first.delete(stringValue);
192
224
  var pushing = (0, _insert.clone)(object);
193
225
  this.arrayExpression.elements.push(pushing);
194
226
  (0, _assert.ok)(this.arrayExpression.elements[index] === pushing);
195
- this.toCaller(firstLocation[0], firstLocation[1], index);
227
+ this.transformLiteral(firstLocation[0], firstLocation[1], index);
196
228
  }
197
229
 
198
- var index = this.map.get(value);
230
+ var index = this.map.get(stringValue);
199
231
  (0, _assert.ok)(typeof index === "number");
200
- this.toCaller(object, parents, index);
201
- }
232
+ this.transformLiteral(object, parents, index);
233
+ return;
234
+ } // Save this, maybe a duplicate will be found.
235
+
236
+
237
+ this.first.set(stringValue, [object, parents]);
202
238
  };
203
239
  }
204
240
 
@@ -236,7 +236,12 @@ class ObjectExtraction extends _transform.default {
236
236
  var v = property.value;
237
237
  variableDeclarators.push((0, _gen.VariableDeclarator)(nn, this.addComment(v, "".concat(name, ".").concat(keyName))));
238
238
  });
239
- declaration.declarations.splice(declaration.declarations.indexOf(declarator), 1, ...variableDeclarators); // update all identifiers that pointed to the old object
239
+ declaration.declarations.splice(declaration.declarations.indexOf(declarator), 1, ...variableDeclarators);
240
+
241
+ if (declaration.kind === "const") {
242
+ declaration.kind = "var";
243
+ } // update all identifiers that pointed to the old object
244
+
240
245
 
241
246
  objectDefChanges[name] && objectDefChanges[name].forEach(change => {
242
247
  if (!change.key) {