js-confuser 1.4.1 → 1.5.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 (58) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +7 -0
  3. package/dev.js +4 -5
  4. package/dist/compiler.js +3 -2
  5. package/dist/constants.js +1 -1
  6. package/dist/index.js +5 -5
  7. package/dist/obfuscator.js +14 -3
  8. package/dist/options.js +2 -2
  9. package/dist/order.js +1 -0
  10. package/dist/parser.js +2 -2
  11. package/dist/precedence.js +1 -1
  12. package/dist/presets.js +3 -0
  13. package/dist/probability.js +7 -1
  14. package/dist/transforms/calculator.js +15 -3
  15. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +93 -19
  16. package/dist/transforms/deadCode.js +1 -1
  17. package/dist/transforms/dispatcher.js +3 -1
  18. package/dist/transforms/eval.js +1 -1
  19. package/dist/transforms/flatten.js +2 -2
  20. package/dist/transforms/hexadecimalNumbers.js +38 -0
  21. package/dist/transforms/hideInitializingCode.js +8 -3
  22. package/dist/transforms/identifier/globalConcealing.js +3 -1
  23. package/dist/transforms/identifier/nameRecycling.js +2 -1
  24. package/dist/transforms/lock/integrity.js +2 -1
  25. package/dist/transforms/lock/lock.js +2 -2
  26. package/dist/transforms/minify.js +8 -3
  27. package/dist/transforms/rgf.js +11 -8
  28. package/dist/transforms/stack.js +2 -1
  29. package/dist/transforms/string/stringConcealing.js +3 -3
  30. package/dist/transforms/transform.js +32 -8
  31. package/dist/traverse.js +4 -2
  32. package/dist/types.js +5 -1
  33. package/dist/util/compare.js +4 -4
  34. package/dist/util/gen.js +66 -47
  35. package/dist/util/identifiers.js +4 -4
  36. package/dist/util/insert.js +25 -16
  37. package/dist/util/object.js +3 -1
  38. package/dist/util/random.js +5 -5
  39. package/dist/util/scope.js +1 -1
  40. package/package.json +11 -11
  41. package/samples/high.js +1 -198
  42. package/samples/low.js +1 -26
  43. package/samples/medium.js +1 -40
  44. package/src/compiler.ts +5 -1
  45. package/src/obfuscator.ts +2 -0
  46. package/src/options.ts +8 -0
  47. package/src/order.ts +2 -0
  48. package/src/presets.ts +3 -0
  49. package/src/transforms/calculator.ts +36 -5
  50. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +119 -2
  51. package/src/transforms/deadCode.ts +102 -0
  52. package/src/transforms/eval.ts +2 -1
  53. package/src/transforms/hexadecimalNumbers.ts +31 -0
  54. package/src/transforms/minify.ts +9 -3
  55. package/src/transforms/transform.ts +10 -2
  56. package/test/transforms/eval.test.ts +28 -0
  57. package/test/transforms/hexadecimalNumbers.test.ts +62 -0
  58. package/test/transforms/minify.test.ts +38 -0
@@ -10,19 +10,27 @@ import {
10
10
  LogicalExpression,
11
11
  SwitchCase,
12
12
  SwitchStatement,
13
+ SequenceExpression,
14
+ AssignmentExpression,
15
+ VariableDeclaration,
16
+ VariableDeclarator,
17
+ ExpressionStatement,
13
18
  } from "../util/gen";
14
19
  import { prepend } from "../util/insert";
15
20
  import { getBlock } from "../traverse";
16
- import { getRandomInteger } from "../util/random";
21
+ import { choice, getRandomInteger } from "../util/random";
17
22
  import { ObfuscateOrder } from "../order";
18
23
  import { ok } from "assert";
19
24
  import { OPERATOR_PRECEDENCE } from "../precedence";
25
+ import Template from "../templates/template";
20
26
 
21
27
  export default class Calculator extends Transform {
22
28
  gen: any;
23
29
  ops: { [operator: string]: number };
24
30
  statesUsed: Set<string>;
25
31
  calculatorFn: string;
32
+ calculatorOpVar: string;
33
+ calculatorSetOpFn: string;
26
34
 
27
35
  constructor(o) {
28
36
  super(o, ObfuscateOrder.Calculator);
@@ -30,6 +38,8 @@ export default class Calculator extends Transform {
30
38
  this.ops = Object.create(null);
31
39
  this.statesUsed = new Set();
32
40
  this.calculatorFn = this.getPlaceholder();
41
+ this.calculatorOpVar = this.getPlaceholder();
42
+ this.calculatorSetOpFn = this.getPlaceholder();
33
43
 
34
44
  this.gen = this.getGenerator();
35
45
  }
@@ -41,7 +51,6 @@ export default class Calculator extends Transform {
41
51
  return;
42
52
  }
43
53
 
44
- var opArg = this.getPlaceholder();
45
54
  var leftArg = this.getPlaceholder();
46
55
  var rightArg = this.getPlaceholder();
47
56
  var switchCases = [];
@@ -65,8 +74,21 @@ export default class Calculator extends Transform {
65
74
 
66
75
  var func = FunctionDeclaration(
67
76
  this.calculatorFn,
68
- [opArg, leftArg, rightArg].map((x) => Identifier(x)),
69
- [SwitchStatement(Identifier(opArg), switchCases)]
77
+ [leftArg, rightArg].map((x) => Identifier(x)),
78
+ [SwitchStatement(Identifier(this.calculatorOpVar), switchCases)]
79
+ );
80
+
81
+ prepend(
82
+ tree,
83
+ VariableDeclaration(VariableDeclarator(this.calculatorOpVar))
84
+ );
85
+
86
+ prepend(
87
+ tree,
88
+ Template(`function {name}(a){
89
+ a = {b} + ({b}=a, 0);
90
+ return a;
91
+ }`).single({ name: this.calculatorSetOpFn, b: this.calculatorOpVar })
70
92
  );
71
93
 
72
94
  prepend(tree, func);
@@ -123,9 +145,18 @@ export default class Calculator extends Transform {
123
145
  this.replace(
124
146
  object,
125
147
  CallExpression(Identifier(this.calculatorFn), [
126
- Literal(this.ops[operator]),
127
148
  object.left,
128
149
  object.right,
150
+ choice([
151
+ AssignmentExpression(
152
+ "=",
153
+ Identifier(this.calculatorOpVar),
154
+ Literal(this.ops[operator])
155
+ ),
156
+ CallExpression(Identifier(this.calculatorSetOpFn), [
157
+ Literal(this.ops[operator]),
158
+ ]),
159
+ ]),
129
160
  ])
130
161
  );
131
162
  };
@@ -18,7 +18,10 @@ import {
18
18
  IfStatement,
19
19
  LabeledStatement,
20
20
  Literal,
21
+ MemberExpression,
21
22
  Node,
23
+ ObjectExpression,
24
+ Property,
22
25
  ReturnStatement,
23
26
  SequenceExpression,
24
27
  SwitchCase,
@@ -234,7 +237,16 @@ export default class ControlFlowFlattening extends Transform {
234
237
 
235
238
  var resultVar = this.getPlaceholder();
236
239
  var argVar = this.getPlaceholder();
240
+ var testVar = this.getPlaceholder();
241
+ var stringBankVar = this.getPlaceholder();
242
+ var stringBank: { [strValue: string]: string } = Object.create(null);
243
+ var stringBankByLabels: { [label: string]: Set<string> } =
244
+ Object.create(null);
245
+ let stringBankGen = this.getGenerator();
246
+
247
+ var needsTestVar = false;
237
248
  var needsResultAndArgVar = false;
249
+ var needsStringBankVar = false;
238
250
  var fnToLabel: { [fnName: string]: string } = Object.create(null);
239
251
 
240
252
  fnNames.forEach((fnName) => {
@@ -269,6 +281,38 @@ export default class ControlFlowFlattening extends Transform {
269
281
  body: [...currentBody],
270
282
  });
271
283
 
284
+ walk(currentBody, [], (o, p) => {
285
+ if (
286
+ o.type == "Literal" &&
287
+ typeof o.value == "string" &&
288
+ !o.regex &&
289
+ Math.random() / (Object.keys(stringBank).length / 2 + 1) > 0.5
290
+ ) {
291
+ needsStringBankVar = true;
292
+ if (!stringBankByLabels[currentLabel]) {
293
+ stringBankByLabels[currentLabel] = new Set();
294
+ }
295
+
296
+ stringBankByLabels[currentLabel].add(o.value);
297
+
298
+ if (typeof stringBank[o.value] === "undefined") {
299
+ stringBank[o.value] = stringBankGen.generate();
300
+ }
301
+
302
+ return () => {
303
+ this.replaceIdentifierOrLiteral(
304
+ o,
305
+ MemberExpression(
306
+ Identifier(stringBankVar),
307
+ Literal(stringBank[o.value]),
308
+ true
309
+ ),
310
+ p
311
+ );
312
+ };
313
+ }
314
+ });
315
+
272
316
  currentLabel = newLabel;
273
317
  currentBody = [];
274
318
  };
@@ -478,7 +522,21 @@ export default class ControlFlowFlattening extends Transform {
478
522
  finishCurrentChunk(isPostTest ? bodyPath : testPath, testPath);
479
523
 
480
524
  currentBody.push(
481
- IfStatement(control.test || Literal(true), [
525
+ ExpressionStatement(
526
+ AssignmentExpression(
527
+ "=",
528
+ Identifier(testVar),
529
+ control.test || Literal(true)
530
+ )
531
+ )
532
+ );
533
+
534
+ needsTestVar = true;
535
+
536
+ finishCurrentChunk();
537
+
538
+ currentBody.push(
539
+ IfStatement(Identifier(testVar), [
482
540
  {
483
541
  type: "GotoStatement",
484
542
  label: bodyPath,
@@ -522,6 +580,16 @@ export default class ControlFlowFlattening extends Transform {
522
580
  ) {
523
581
  finishCurrentChunk();
524
582
 
583
+ currentBody.push(
584
+ ExpressionStatement(
585
+ AssignmentExpression("=", Identifier(testVar), stmt.test)
586
+ )
587
+ );
588
+
589
+ needsTestVar = true;
590
+
591
+ finishCurrentChunk();
592
+
525
593
  var hasAlternate = !!stmt.alternate;
526
594
  ok(!(hasAlternate && stmt.alternate.type !== "BlockStatement"));
527
595
 
@@ -530,7 +598,7 @@ export default class ControlFlowFlattening extends Transform {
530
598
  var afterPath = this.getPlaceholder();
531
599
 
532
600
  currentBody.push(
533
- IfStatement(stmt.test, [
601
+ IfStatement(Identifier(testVar), [
534
602
  {
535
603
  type: "GotoStatement",
536
604
  label: yesPath,
@@ -845,6 +913,7 @@ export default class ControlFlowFlattening extends Transform {
845
913
 
846
914
  var breaksInsertion = [];
847
915
  var staticStateValues = [...labelToStates[chunk.label]];
916
+ var potentialBranches = new Set<string>();
848
917
 
849
918
  chunk.body.forEach((stmt, stmtIndex) => {
850
919
  var addBreak = false;
@@ -894,6 +963,8 @@ export default class ControlFlowFlattening extends Transform {
894
963
  );
895
964
  }
896
965
 
966
+ potentialBranches.add(o.label);
967
+
897
968
  var mutatingStateValues = [...labelToStates[chunk.label]];
898
969
  var nextStateValues = labelToStates[o.label];
899
970
  ok(nextStateValues, o.label);
@@ -930,6 +1001,29 @@ export default class ControlFlowFlattening extends Transform {
930
1001
  chunk.body.splice(index + 1, 0, BreakStatement(switchLabel));
931
1002
  });
932
1003
 
1004
+ for (var branch of Array.from(potentialBranches)) {
1005
+ var strings = stringBankByLabels[branch];
1006
+ if (strings) {
1007
+ chunk.body.unshift(
1008
+ ExpressionStatement(
1009
+ SequenceExpression(
1010
+ Array.from(strings).map((strValue) => {
1011
+ return AssignmentExpression(
1012
+ "=",
1013
+ MemberExpression(
1014
+ Identifier(stringBankVar),
1015
+ Literal(stringBank[strValue]),
1016
+ true
1017
+ ),
1018
+ Literal(strValue)
1019
+ );
1020
+ })
1021
+ )
1022
+ )
1023
+ );
1024
+ }
1025
+ }
1026
+
933
1027
  // var c = Identifier("undefined");
934
1028
  // this.addComment(c, stateValues.join(", "));
935
1029
  // transitionStatements.push(c);
@@ -975,11 +1069,34 @@ export default class ControlFlowFlattening extends Transform {
975
1069
 
976
1070
  var declarations = [];
977
1071
 
1072
+ if (needsTestVar) {
1073
+ declarations.push(VariableDeclarator(testVar));
1074
+ }
1075
+
978
1076
  if (needsResultAndArgVar) {
979
1077
  declarations.push(VariableDeclarator(resultVar));
980
1078
  declarations.push(VariableDeclarator(argVar));
981
1079
  }
982
1080
 
1081
+ if (needsStringBankVar) {
1082
+ declarations.push(
1083
+ VariableDeclarator(
1084
+ stringBankVar,
1085
+ ObjectExpression(
1086
+ stringBankByLabels[startLabel]
1087
+ ? Array.from(stringBankByLabels[startLabel]).map((strValue) =>
1088
+ Property(
1089
+ Literal(stringBank[strValue]),
1090
+ Literal(strValue),
1091
+ false
1092
+ )
1093
+ )
1094
+ : []
1095
+ )
1096
+ )
1097
+ );
1098
+ }
1099
+
983
1100
  declarations.push(
984
1101
  ...stateVars.map((stateVar, i) => {
985
1102
  return VariableDeclarator(stateVar, Literal(initStateValues[i]));
@@ -124,6 +124,108 @@ function setCookie(cname, cvalue, exdays) {
124
124
 
125
125
  __.match(s + g);
126
126
  `),
127
+ Template(`
128
+ function vec_pack(vec) {
129
+ return vec[1] * 67108864 + (vec[0] < 0 ? 33554432 | vec[0] : vec[0]);
130
+ }
131
+
132
+ function vec_unpack(number) {
133
+ switch (((number & 33554432) !== 0) * 1 + (number < 0) * 2) {
134
+ case 0:
135
+ return [number % 33554432, Math.trunc(number / 67108864)];
136
+ case 1:
137
+ return [
138
+ (number % 33554432) - 33554432,
139
+ Math.trunc(number / 67108864) + 1,
140
+ ];
141
+ case 2:
142
+ return [
143
+ (((number + 33554432) % 33554432) + 33554432) % 33554432,
144
+ Math.round(number / 67108864),
145
+ ];
146
+ case 3:
147
+ return [number % 33554432, Math.trunc(number / 67108864)];
148
+ }
149
+ }
150
+
151
+ let a = vec_pack([2, 4]);
152
+ let b = vec_pack([1, 2]);
153
+
154
+ let c = a + b; // Vector addition
155
+ let d = c - b; // Vector subtraction
156
+ let e = d * 2; // Scalar multiplication
157
+ let f = e / 2; // Scalar division
158
+
159
+ console.log(vec_unpack(c)); // [3, 6]
160
+ console.log(vec_unpack(d)); // [2, 4]
161
+ console.log(vec_unpack(e)); // [4, 8]
162
+ console.log(vec_unpack(f)); // [2, 4]
163
+ `),
164
+ Template(`
165
+ function buildCharacterMap(str) {
166
+ const characterMap = {};
167
+
168
+ for (let char of str.replace(/[^\w]/g, "").toLowerCase())
169
+ characterMap[char] = characterMap[char] + 1 || 1;
170
+
171
+ return characterMap;
172
+ }
173
+
174
+ function isAnagrams(stringA, stringB) {
175
+ const stringAMap = buildCharMap(stringA);
176
+ const stringBMap = buildCharMap(stringB);
177
+
178
+ for (let char in stringAMap) {
179
+ if (stringAMap[char] !== stringBMap[char]) {
180
+ return false;
181
+ }
182
+ }
183
+
184
+ if (Object.keys(stringAMap).length !== Object.keys(stringBMap).length) {
185
+ return false;
186
+ }
187
+
188
+ return true;
189
+ }
190
+
191
+ /**
192
+ * @param {TreeNode} root
193
+ * @return {boolean}
194
+ */
195
+ function isBalanced(root) {
196
+ const height = getHeightBalanced(root);
197
+ return height !== Infinity;
198
+ }
199
+
200
+ function getHeightBalanced(node) {
201
+ if (!node) {
202
+ return -1;
203
+ }
204
+
205
+ const leftTreeHeight = getHeightBalanced(node.left);
206
+ const rightTreeHeight = getHeightBalanced(node.right);
207
+
208
+ const heightDiff = Math.abs(leftTreeHeight - rightTreeHeight);
209
+
210
+ if (
211
+ leftTreeHeight === Infinity ||
212
+ rightTreeHeight === Infinity ||
213
+ heightDiff > 1
214
+ ) {
215
+ return Infinity;
216
+ }
217
+
218
+ const currentHeight = Math.max(leftTreeHeight, rightTreeHeight) + 1;
219
+ return currentHeight;
220
+ }
221
+
222
+ window["__GLOBAL__HELPERS__"] = {
223
+ buildCharacterMap,
224
+ isAnagrams,
225
+ isBalanced,
226
+ getHeightBalanced,
227
+ };
228
+ `),
127
229
  ];
128
230
 
129
231
  /**
@@ -22,7 +22,8 @@ export default class Eval extends Transform {
22
22
  return (
23
23
  isFunction(object) &&
24
24
  object.type != "ArrowFunctionExpression" &&
25
- !object.$eval
25
+ !object.$eval &&
26
+ !object.$dispatcherSkip
26
27
  );
27
28
  }
28
29
 
@@ -0,0 +1,31 @@
1
+ import Transform from "./transform";
2
+ import { ObfuscateOrder } from "../order";
3
+ import { ExitCallback } from "../traverse";
4
+ import { Identifier, Node } from "../util/gen";
5
+
6
+ export default class HexadecimalNumbers extends Transform {
7
+ constructor(o) {
8
+ super(o, ObfuscateOrder.HexadecimalNumbers);
9
+ }
10
+
11
+ match(object: Node, parents: Node[]): boolean {
12
+ return (
13
+ object.type === "Literal" &&
14
+ typeof object.value === "number" &&
15
+ Math.floor(object.value) === object.value
16
+ );
17
+ }
18
+
19
+ transform(object: Node, parents: Node[]): void | ExitCallback {
20
+ return () => {
21
+ // Technically, a Literal will never be negative because it's supposed to be inside a UnaryExpression with a "-" operator.
22
+ // This code handles it regardless
23
+ var isNegative = object.value < 0;
24
+ var hex = Math.abs(object.value).toString(16);
25
+
26
+ var newStr = (isNegative ? "-" : "") + "0x" + hex;
27
+
28
+ this.replace(object, Identifier(newStr));
29
+ };
30
+ }
31
+ }
@@ -21,6 +21,7 @@ import {
21
21
  clone,
22
22
  isForInitialize,
23
23
  isLexContext,
24
+ getFunction,
24
25
  } from "../util/insert";
25
26
  import { isValidIdentifier, isEquivalent } from "../util/compare";
26
27
  import { walk, isBlock } from "../traverse";
@@ -126,9 +127,14 @@ export default class Minify extends Transform {
126
127
  // Unnecessary return
127
128
  if (body.length && body[body.length - 1]) {
128
129
  var last = body[body.length - 1];
129
- var isUndefined = last.argument == null;
130
- if (last.type == "ReturnStatement" && isUndefined) {
131
- body.pop();
130
+ if (last.type == "ReturnStatement") {
131
+ var isUndefined = last.argument == null;
132
+
133
+ if (isUndefined) {
134
+ if (getFunction(object, parents).body == object) {
135
+ body.pop();
136
+ }
137
+ }
132
138
  }
133
139
  }
134
140
 
@@ -196,10 +196,18 @@ export default class Transform {
196
196
  */
197
197
  getGenerator(offset = 0) {
198
198
  var count = offset;
199
+ var identifiers = new Set();
199
200
  return {
200
201
  generate: () => {
201
- count++;
202
- return this.generateIdentifier(-1, count);
202
+ var retValue;
203
+ do {
204
+ count++;
205
+ retValue = this.generateIdentifier(-1, count);
206
+ } while (identifiers.has(retValue));
207
+
208
+ identifiers.add(retValue);
209
+
210
+ return retValue;
203
211
  },
204
212
  };
205
213
  }
@@ -48,3 +48,31 @@ it("should move function declarations to the top of the block", async () => {
48
48
 
49
49
  expect(value).toStrictEqual(100);
50
50
  });
51
+
52
+ it("should work with Integrity also enabled", async () => {
53
+ var code = `
54
+ input("Hello World")
55
+ `;
56
+
57
+ var output = await JsConfuser(code, {
58
+ target: "node",
59
+ compact: false,
60
+ eval: true,
61
+ lock: {
62
+ integrity: true,
63
+ },
64
+ });
65
+
66
+ expect(output).toContain("eval(");
67
+
68
+ var value = "never_called",
69
+ input = (valueIn) => (value = valueIn);
70
+
71
+ try {
72
+ eval(output);
73
+ } catch (e) {
74
+ expect(e).toStrictEqual(undefined);
75
+ }
76
+
77
+ expect(value).toStrictEqual("Hello World");
78
+ });
@@ -0,0 +1,62 @@
1
+ import JsConfuser from "../../src/index";
2
+
3
+ test("Variant #1: Positive integer to hexadecimal", async () => {
4
+ var output = await JsConfuser.obfuscate(`TEST_VAR = 10;`, {
5
+ target: "node",
6
+ hexadecimalNumbers: true,
7
+ });
8
+
9
+ expect(output).toContain("0xa");
10
+ expect(output).not.toContain("10");
11
+
12
+ var TEST_VAR;
13
+
14
+ eval(output);
15
+
16
+ expect(TEST_VAR).toStrictEqual(10);
17
+ });
18
+
19
+ test("Variant #2: Negative integer to hexadecimal", async () => {
20
+ var output = await JsConfuser.obfuscate(`TEST_VAR = -10;`, {
21
+ target: "node",
22
+ hexadecimalNumbers: true,
23
+ });
24
+
25
+ expect(output).toContain("-0xa");
26
+ expect(output).not.toContain("-10");
27
+
28
+ var TEST_VAR;
29
+
30
+ eval(output);
31
+
32
+ expect(TEST_VAR).toStrictEqual(-10);
33
+ });
34
+
35
+ test("Variant #3: Don't change floats", async () => {
36
+ var output = await JsConfuser.obfuscate(`var TEST_VAR = [15.5, -75.9];`, {
37
+ target: "node",
38
+ hexadecimalNumbers: true,
39
+ });
40
+
41
+ expect(output).toContain("15.5");
42
+ expect(output).toContain("-75.9");
43
+ expect(output).not.toContain("0x");
44
+ });
45
+
46
+ test("Variant #4: Work even on large numbers", async () => {
47
+ var output = await JsConfuser.obfuscate(
48
+ `TEST_VAR = 10000000000000000000000000000;`,
49
+ {
50
+ target: "node",
51
+ hexadecimalNumbers: true,
52
+ }
53
+ );
54
+
55
+ expect(output).toContain("0x204fce5e3e25020000000000");
56
+
57
+ var TEST_VAR;
58
+
59
+ eval(output);
60
+
61
+ expect(TEST_VAR).toStrictEqual(10000000000000000000000000000);
62
+ });
@@ -261,3 +261,41 @@ test("Variant #14: Shorten 'var x = undefined' to 'var x'", async () => {
261
261
  expect(output).toContain("var x");
262
262
  expect(output).not.toContain("var x=");
263
263
  });
264
+
265
+ test("Variant #15: Removing implied 'return'", async () => {
266
+ // Valid
267
+ var output = await JsConfuser(
268
+ `
269
+ function MyFunction(){
270
+ var output = "Hello World";
271
+ console.log(output);
272
+ return;
273
+ }
274
+
275
+ MyFunction();
276
+ `,
277
+ { target: "node", minify: true }
278
+ );
279
+
280
+ expect(output).not.toContain("return");
281
+
282
+ // Invalid
283
+ // https://github.com/MichaelXF/js-confuser/issues/34
284
+ var output2 = await JsConfuser(
285
+ `
286
+ function greet(){
287
+ if(true){
288
+ console.log("return");
289
+ return;
290
+ }
291
+
292
+ var output = "should not show!"; console.log(output);
293
+ }
294
+
295
+ greet();
296
+ `,
297
+ { target: "browser", minify: true }
298
+ );
299
+
300
+ expect(output2).toContain("return");
301
+ });