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,82 +1,33 @@
1
- import { Node } from "../util/gen";
2
- import { parseSnippet, parseSync } from "../parser";
1
+ import * as babelTypes from "@babel/types";
2
+ import { parse } from "@babel/parser";
3
+ import traverse from "@babel/traverse";
4
+ import { NodePath } from "@babel/traverse";
3
5
  import { ok } from "assert";
4
- import { choice } from "../util/random";
5
- import { placeholderVariablePrefix } from "../constants";
6
- import traverse from "../traverse";
6
+ import { getRandomString } from "../utils/random-utils";
7
+ import { NodeSymbol } from "../constants";
8
+
9
+ // Create a union type of the symbol keys in NodeSymbol
10
+ type NodeSymbolKeys = keyof {
11
+ [K in keyof NodeSymbol as K extends symbol ? K : never]: NodeSymbol[K];
12
+ };
7
13
 
8
14
  export interface TemplateVariables {
9
15
  [varName: string]:
10
16
  | string
11
- | (() => Node | Node[] | Template)
12
- | Node
13
- | Node[]
17
+ | number
18
+ | (() => babelTypes.Node | babelTypes.Node[] | Template)
19
+ | babelTypes.Node
20
+ | babelTypes.Node[]
14
21
  | Template;
15
22
  }
16
23
 
17
- /**
18
- * Templates provides an easy way to parse code snippets into AST subtrees.
19
- *
20
- * These AST subtrees can added to the obfuscated code, tailored with variable names.
21
- *
22
- * 1. Basic string interpolation
23
- *
24
- * ```js
25
- * var Base64Template = new Template(`
26
- * function {name}(str){
27
- * return btoa(str)
28
- * }
29
- * `);
30
- *
31
- * var functionDeclaration = Base64Template.single({ name: "atob" });
32
- * ```
33
- *
34
- * 2. AST subtree insertion
35
- *
36
- * ```js
37
- * var Base64Template = new Template(`
38
- * function {name}(str){
39
- * {getWindow}
40
- *
41
- * return {getWindowName}btoa(str)
42
- * }`)
43
- *
44
- * var functionDeclaration = Base64Template.single({
45
- * name: "atob",
46
- * getWindowName: "newWindow",
47
- * getWindow: () => {
48
- * return acorn.parse("var newWindow = {}").body[0];
49
- * }
50
- * });
51
- * ```
52
- *
53
- * Here, the `getWindow` variable is a function that returns an AST subtree. This must be a `Node[]` array or Template.
54
- * Optionally, the function can be replaced with just the `Node[]` array or Template if it's already computed.
55
- *
56
- * 3. Template subtree insertion
57
- *
58
- * ```js
59
- * var NewWindowTemplate = new Template(`
60
- * var newWindow = {};
61
- * `);
62
- *
63
- * var Base64Template = new Template(`
64
- * function {name}(str){
65
- * {NewWindowTemplate}
66
- *
67
- * return newWindow.btoa(str)
68
- * }`)
69
- *
70
- * var functionDeclaration = Base64Template.single({
71
- * name: "atob",
72
- * NewWindowTemplate: NewWindowTemplate
73
- * });
74
- * ```
75
- */
76
24
  export default class Template {
77
- templates: string[];
78
- defaultVariables: TemplateVariables;
79
- requiredVariables: Set<string>;
25
+ private templates: string[];
26
+ private defaultVariables: TemplateVariables;
27
+ private requiredVariables: Set<string>;
28
+ private astVariableMappings: Map<string, string>;
29
+ private astIdentifierPrefix = "__t_" + getRandomString(6);
30
+ private symbols: NodeSymbolKeys[] = [];
80
31
 
81
32
  constructor(...templates: string[]) {
82
33
  this.templates = templates;
@@ -86,145 +37,186 @@ export default class Template {
86
37
  this.findRequiredVariables();
87
38
  }
88
39
 
40
+ addSymbols(...symbols: NodeSymbolKeys[]): this {
41
+ this.symbols.push(...symbols);
42
+ return this;
43
+ }
44
+
89
45
  setDefaultVariables(defaultVariables: TemplateVariables): this {
90
46
  this.defaultVariables = defaultVariables;
91
47
  return this;
92
48
  }
93
49
 
94
50
  private findRequiredVariables() {
95
- var matches = this.templates[0].match(/{[$A-z0-9_]+}/g);
51
+ const matches = this.templates[0].match(/{[$A-Za-z0-9_]+}/g);
96
52
  if (matches !== null) {
97
53
  matches.forEach((variable) => {
98
- var name = variable.slice(1, -1);
54
+ const name = variable.slice(1, -1);
99
55
 
100
- // $ variables are for default variables
101
- if (name.startsWith("$")) {
102
- throw new Error("Default variables are no longer supported.");
103
- } else {
104
- this.requiredVariables.add(name);
105
- }
56
+ this.requiredVariables.add(name);
106
57
  });
107
58
  }
108
59
  }
109
60
 
110
- /**
111
- * Interpolates the template with the given variables.
112
- *
113
- * Prepares the template string for AST parsing.
114
- *
115
- * @param variables
116
- */
117
- private interpolateTemplate(variables: TemplateVariables = {}) {
118
- var allVariables = { ...this.defaultVariables, ...variables };
119
-
120
- // Validate all variables were passed in
121
- for (var requiredVariable of this.requiredVariables) {
61
+ private interpolateTemplate(variables: TemplateVariables) {
62
+ const allVariables = { ...this.defaultVariables, ...variables };
63
+
64
+ for (const requiredVariable of this.requiredVariables) {
122
65
  if (typeof allVariables[requiredVariable] === "undefined") {
123
66
  throw new Error(
124
- this.templates[0] +
125
- " missing variable: " +
126
- requiredVariable +
127
- " from " +
128
- JSON.stringify(allVariables)
67
+ `${
68
+ this.templates[0]
69
+ } missing variable: ${requiredVariable} from ${JSON.stringify(
70
+ allVariables
71
+ )}`
129
72
  );
130
73
  }
131
74
  }
132
75
 
133
- var template = choice(this.templates);
134
- var output = template;
76
+ const template =
77
+ this.templates[Math.floor(Math.random() * this.templates.length)];
78
+ let output = template;
79
+
80
+ this.astVariableMappings = new Map();
135
81
 
136
82
  Object.keys(allVariables).forEach((name) => {
137
- var bracketName = "{" + name.replace("$", "\\$") + "}";
83
+ const bracketName = `{${name.replace("$", "\\$")}}`;
84
+ let value = allVariables[name] as string;
138
85
 
139
- var value = allVariables[name] + "";
140
- if (typeof allVariables[name] !== "string") {
141
- value = name;
142
- }
86
+ if (this.isASTVariable(value)) {
87
+ let astIdentifierName = this.astIdentifierPrefix + name;
88
+ this.astVariableMappings.set(name, astIdentifierName);
143
89
 
144
- var reg = new RegExp(bracketName, "g");
90
+ value = astIdentifierName;
91
+ }
145
92
 
93
+ const reg = new RegExp(bracketName, "g");
146
94
  output = output.replace(reg, value);
147
95
  });
148
96
 
149
97
  return { output, template };
150
98
  }
151
99
 
152
- /**
153
- * Finds the variables in the AST and replaces them with the given values.
154
- *
155
- * Note: Mutates the AST.
156
- * @param ast
157
- * @param variables
158
- */
159
- private interpolateAST(ast: Node, variables: TemplateVariables) {
160
- var allVariables = { ...this.defaultVariables, ...variables };
161
-
162
- var astNames = new Set(
163
- Object.keys(allVariables).filter((name) => {
164
- return typeof allVariables[name] !== "string";
165
- })
166
- );
167
-
168
- if (astNames.size === 0) return;
169
-
170
- traverse(ast, (o, p) => {
171
- if (o.type === "Identifier" && allVariables[o.name]) {
172
- return () => {
173
- var value = allVariables[o.name];
174
- ok(typeof value !== "string");
175
-
176
- var insertNodes = typeof value === "function" ? value() : value;
177
- if (insertNodes instanceof Template) {
178
- insertNodes = insertNodes.compile(allVariables);
179
- }
100
+ private isASTVariable(variable: any): boolean {
101
+ return typeof variable !== "string" && typeof variable !== "number";
102
+ }
103
+
104
+ private interpolateAST(ast: babelTypes.Node, variables: TemplateVariables) {
105
+ if (this.astVariableMappings.size === 0) return;
106
+
107
+ const allVariables = { ...this.defaultVariables, ...variables };
108
+ const template = this;
109
+
110
+ // Reverse the lookup map
111
+ // Before {name -> __t_m4H6nk_name}
112
+ // After {__t_m4H6nk_name -> name}
113
+ const reverseMappings = new Map<string, string>();
114
+ this.astVariableMappings.forEach((value, key) => {
115
+ reverseMappings.set(value, key);
116
+ });
180
117
 
181
- if (!Array.isArray(insertNodes)) {
182
- // Replace with expression
118
+ const insertedVariables = new Set<string>();
183
119
 
184
- Object.assign(o, insertNodes);
185
- } else {
186
- // Insert multiple statements/declarations
187
- var expressionStatement: Node = p[0];
188
- var body: Node[] = p[1] as any;
120
+ traverse(ast, {
121
+ Identifier(path: NodePath<babelTypes.Identifier>) {
122
+ const idName = path.node.name;
123
+ if (!idName.startsWith(template.astIdentifierPrefix)) return;
189
124
 
190
- ok(expressionStatement.type === "ExpressionStatement");
191
- ok(Array.isArray(body));
125
+ const variableName = reverseMappings.get(idName);
126
+ ok(variableName, `Variable ${idName} not found in mappings`);
192
127
 
193
- var index = body.indexOf(expressionStatement);
128
+ let value = allVariables[variableName];
129
+ let isSingleUse = true; // Hard-coded nodes are deemed 'single use'
130
+ if (typeof value === "function") {
131
+ value = value();
132
+ isSingleUse = false;
133
+ }
194
134
 
195
- body.splice(index, 1, ...insertNodes);
135
+ if (value instanceof Template) {
136
+ value = value.compile(allVariables);
137
+ isSingleUse = false;
138
+ }
139
+
140
+ // Duplicate node check
141
+ if (isSingleUse) {
142
+ if (insertedVariables.has(variableName)) {
143
+ ok(false, "Duplicate node inserted for variable: " + variableName);
196
144
  }
197
- };
198
- }
145
+ insertedVariables.add(variableName);
146
+ }
147
+
148
+ // Insert new nodes
149
+ if (!Array.isArray(value)) {
150
+ path.replaceWith(value as babelTypes.Node);
151
+ } else {
152
+ path.replaceWithMultiple(value as babelTypes.Node[]);
153
+ }
154
+
155
+ path.skip();
156
+ },
199
157
  });
200
158
  }
201
159
 
202
- compile(variables: TemplateVariables = {}): Node[] {
203
- var { output, template } = this.interpolateTemplate(variables);
160
+ file(variables: TemplateVariables = {}): babelTypes.File {
161
+ const { output } = this.interpolateTemplate(variables);
204
162
 
205
- var program: Node;
163
+ let file: babelTypes.File;
206
164
  try {
207
- program = parseSnippet(output);
165
+ file = parse(output, {
166
+ sourceType: "module",
167
+ allowReturnOutsideFunction: true,
168
+ });
208
169
  } catch (e) {
209
- throw new Error(output + "\n" + "Template failed to parse: " + e.message);
170
+ throw new Error(
171
+ output + "\n" + "Template failed to parse: " + (e as Error).message
172
+ );
210
173
  }
211
174
 
212
- this.interpolateAST(program, variables);
175
+ this.interpolateAST(file, variables);
213
176
 
214
- return program.body;
177
+ if (this.symbols.length > 0) {
178
+ file.program.body.forEach((node) => {
179
+ for (const symbol of this.symbols) {
180
+ node[symbol] = true;
181
+ }
182
+ });
183
+ }
184
+
185
+ return file;
215
186
  }
216
187
 
217
- single(variables: TemplateVariables = {}): Node {
218
- var nodes = this.compile(variables);
188
+ compile(variables: TemplateVariables = {}): babelTypes.Statement[] {
189
+ const file = this.file(variables);
190
+
191
+ return file.program.body;
192
+ }
193
+
194
+ single<T extends babelTypes.Statement>(variables: TemplateVariables = {}): T {
195
+ const nodes = this.compile(variables);
219
196
 
220
197
  if (nodes.length !== 1) {
221
- nodes = nodes.filter((node) => node.type !== "EmptyStatement");
198
+ const filteredNodes = nodes.filter(
199
+ (node) => node.type !== "EmptyStatement"
200
+ );
222
201
  ok(
223
- nodes.length === 1,
224
- `Expected single node, got ${nodes.map((node) => node.type).join(", ")}`
202
+ filteredNodes.length === 1,
203
+ `Expected single node, got ${filteredNodes
204
+ .map((node) => node.type)
205
+ .join(", ")}`
225
206
  );
207
+ return filteredNodes[0] as T;
226
208
  }
227
209
 
228
- return nodes[0];
210
+ return nodes[0] as T;
211
+ }
212
+
213
+ expression<T extends babelTypes.Expression>(
214
+ variables: TemplateVariables = {}
215
+ ): T {
216
+ const statement = this.single(variables);
217
+
218
+ babelTypes.assertExpressionStatement(statement);
219
+
220
+ return statement.expression as T;
229
221
  }
230
222
  }
@@ -0,0 +1,99 @@
1
+ import { NodePath } from "@babel/core";
2
+ import { PluginArg, PluginObject } from "./plugin";
3
+ import * as t from "@babel/types";
4
+ import { ok } from "assert";
5
+ import { Order } from "../order";
6
+ import { NodeSymbol, SKIP } from "../constants";
7
+ import Template from "../templates/template";
8
+ import { append } from "../utils/ast-utils";
9
+
10
+ export default ({ Plugin }: PluginArg): PluginObject => {
11
+ const me = Plugin(Order.AstScrambler, {
12
+ changeData: {
13
+ expressions: 0,
14
+ },
15
+ });
16
+ var callExprName: string;
17
+
18
+ return {
19
+ visitor: {
20
+ "Block|SwitchCase": {
21
+ exit(_path) {
22
+ const path = _path as NodePath<t.Block | t.SwitchCase>;
23
+ const isProgram = path.isProgram();
24
+
25
+ let containerKey: string;
26
+ if (path.isSwitchCase()) {
27
+ containerKey = "consequent";
28
+ } else if (path.isBlock()) {
29
+ containerKey = "body";
30
+ }
31
+ var container: t.Statement[] = path.node[containerKey];
32
+ var newContainer: t.Statement[] = [];
33
+
34
+ ok(Array.isArray(container));
35
+
36
+ var expressions: t.Expression[] = [];
37
+
38
+ const flushExpressions = () => {
39
+ if (!expressions.length) return;
40
+
41
+ // Not enough expressions to require a call expression
42
+ if (expressions.length === 1) {
43
+ newContainer.push(t.expressionStatement(expressions[0]));
44
+ expressions = [];
45
+ return;
46
+ }
47
+
48
+ if (!callExprName) {
49
+ callExprName = me.getPlaceholder() + "_ast";
50
+ }
51
+
52
+ me.changeData.expressions += expressions.length;
53
+
54
+ newContainer.push(
55
+ t.expressionStatement(
56
+ t.callExpression(t.identifier(callExprName), expressions)
57
+ )
58
+ );
59
+ expressions = [];
60
+ };
61
+
62
+ for (var statement of container) {
63
+ if (
64
+ // Preserve last expression at the top level
65
+ (isProgram ? statement !== container.at(-1) : true) &&
66
+ t.isExpressionStatement(statement) &&
67
+ !(statement as NodeSymbol)[SKIP]
68
+ ) {
69
+ if (t.isSequenceExpression(statement.expression)) {
70
+ expressions.push(...statement.expression.expressions);
71
+ } else {
72
+ expressions.push(statement.expression);
73
+ }
74
+ } else {
75
+ flushExpressions();
76
+ newContainer.push(statement);
77
+ }
78
+ }
79
+
80
+ flushExpressions();
81
+
82
+ path.node[containerKey] = newContainer;
83
+
84
+ if (path.isProgram()) {
85
+ if (callExprName) {
86
+ var functionDeclaration = new Template(`
87
+ function ${callExprName}(){
88
+ ${callExprName} = function(){};
89
+ }
90
+ `).single();
91
+
92
+ append(path, functionDeclaration);
93
+ }
94
+ }
95
+ },
96
+ },
97
+ },
98
+ };
99
+ };