webfuscator 0.9.4

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 (289) hide show
  1. package/DISCLOSURE +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +83 -0
  4. package/dist/analysis/constant.d.ts +11 -0
  5. package/dist/analysis/constant.d.ts.map +1 -0
  6. package/dist/analysis/constant.js +261 -0
  7. package/dist/analysis/constant.js.map +1 -0
  8. package/dist/analysis/document-order.d.ts +5 -0
  9. package/dist/analysis/document-order.d.ts.map +1 -0
  10. package/dist/analysis/document-order.js +124 -0
  11. package/dist/analysis/document-order.js.map +1 -0
  12. package/dist/analysis/inlineability.d.ts +13 -0
  13. package/dist/analysis/inlineability.d.ts.map +1 -0
  14. package/dist/analysis/inlineability.js +270 -0
  15. package/dist/analysis/inlineability.js.map +1 -0
  16. package/dist/analysis/purity.d.ts +5 -0
  17. package/dist/analysis/purity.d.ts.map +1 -0
  18. package/dist/analysis/purity.js +281 -0
  19. package/dist/analysis/purity.js.map +1 -0
  20. package/dist/index.d.ts +3 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +2 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/obfuscator.d.ts +4 -0
  25. package/dist/obfuscator.d.ts.map +1 -0
  26. package/dist/obfuscator.js +222 -0
  27. package/dist/obfuscator.js.map +1 -0
  28. package/dist/options.d.ts +73 -0
  29. package/dist/options.d.ts.map +1 -0
  30. package/dist/options.js +2 -0
  31. package/dist/options.js.map +1 -0
  32. package/dist/preparation/dot-to-bracket.d.ts +16 -0
  33. package/dist/preparation/dot-to-bracket.d.ts.map +1 -0
  34. package/dist/preparation/dot-to-bracket.js +36 -0
  35. package/dist/preparation/dot-to-bracket.js.map +1 -0
  36. package/dist/preparation/expand-destructuring.d.ts +26 -0
  37. package/dist/preparation/expand-destructuring.d.ts.map +1 -0
  38. package/dist/preparation/expand-destructuring.js +154 -0
  39. package/dist/preparation/expand-destructuring.js.map +1 -0
  40. package/dist/preparation/expand-template-literals.d.ts +23 -0
  41. package/dist/preparation/expand-template-literals.d.ts.map +1 -0
  42. package/dist/preparation/expand-template-literals.js +78 -0
  43. package/dist/preparation/expand-template-literals.js.map +1 -0
  44. package/dist/preparation/rewrite-class-fields.d.ts +43 -0
  45. package/dist/preparation/rewrite-class-fields.d.ts.map +1 -0
  46. package/dist/preparation/rewrite-class-fields.js +270 -0
  47. package/dist/preparation/rewrite-class-fields.js.map +1 -0
  48. package/dist/preparation/split-sequence-expressions.d.ts +15 -0
  49. package/dist/preparation/split-sequence-expressions.d.ts.map +1 -0
  50. package/dist/preparation/split-sequence-expressions.js +36 -0
  51. package/dist/preparation/split-sequence-expressions.js.map +1 -0
  52. package/dist/preparation/split-variable-declarations.d.ts +20 -0
  53. package/dist/preparation/split-variable-declarations.d.ts.map +1 -0
  54. package/dist/preparation/split-variable-declarations.js +36 -0
  55. package/dist/preparation/split-variable-declarations.js.map +1 -0
  56. package/dist/preparation/stringify-object-keys.d.ts +21 -0
  57. package/dist/preparation/stringify-object-keys.d.ts.map +1 -0
  58. package/dist/preparation/stringify-object-keys.js +96 -0
  59. package/dist/preparation/stringify-object-keys.js.map +1 -0
  60. package/dist/preparation/wrap-single-statements.d.ts +24 -0
  61. package/dist/preparation/wrap-single-statements.d.ts.map +1 -0
  62. package/dist/preparation/wrap-single-statements.js +53 -0
  63. package/dist/preparation/wrap-single-statements.js.map +1 -0
  64. package/dist/transforms/arguments-to-parameters.d.ts +20 -0
  65. package/dist/transforms/arguments-to-parameters.d.ts.map +1 -0
  66. package/dist/transforms/arguments-to-parameters.js +162 -0
  67. package/dist/transforms/arguments-to-parameters.js.map +1 -0
  68. package/dist/transforms/arrow-to-function.d.ts +46 -0
  69. package/dist/transforms/arrow-to-function.d.ts.map +1 -0
  70. package/dist/transforms/arrow-to-function.js +203 -0
  71. package/dist/transforms/arrow-to-function.js.map +1 -0
  72. package/dist/transforms/collapse-single-use-temps.d.ts +16 -0
  73. package/dist/transforms/collapse-single-use-temps.d.ts.map +1 -0
  74. package/dist/transforms/collapse-single-use-temps.js +805 -0
  75. package/dist/transforms/collapse-single-use-temps.js.map +1 -0
  76. package/dist/transforms/const-let-to-var.d.ts +21 -0
  77. package/dist/transforms/const-let-to-var.d.ts.map +1 -0
  78. package/dist/transforms/const-let-to-var.js +217 -0
  79. package/dist/transforms/const-let-to-var.js.map +1 -0
  80. package/dist/transforms/const-to-let.d.ts +17 -0
  81. package/dist/transforms/const-to-let.d.ts.map +1 -0
  82. package/dist/transforms/const-to-let.js +33 -0
  83. package/dist/transforms/const-to-let.js.map +1 -0
  84. package/dist/transforms/do-while-to-while.d.ts +26 -0
  85. package/dist/transforms/do-while-to-while.d.ts.map +1 -0
  86. package/dist/transforms/do-while-to-while.js +54 -0
  87. package/dist/transforms/do-while-to-while.js.map +1 -0
  88. package/dist/transforms/drop-console.d.ts +27 -0
  89. package/dist/transforms/drop-console.d.ts.map +1 -0
  90. package/dist/transforms/drop-console.js +107 -0
  91. package/dist/transforms/drop-console.js.map +1 -0
  92. package/dist/transforms/drop-debugger.d.ts +18 -0
  93. package/dist/transforms/drop-debugger.d.ts.map +1 -0
  94. package/dist/transforms/drop-debugger.js +34 -0
  95. package/dist/transforms/drop-debugger.js.map +1 -0
  96. package/dist/transforms/expand-binary-assignment.d.ts +26 -0
  97. package/dist/transforms/expand-binary-assignment.d.ts.map +1 -0
  98. package/dist/transforms/expand-binary-assignment.js +78 -0
  99. package/dist/transforms/expand-binary-assignment.js.map +1 -0
  100. package/dist/transforms/expand-logical-assignment.d.ts +22 -0
  101. package/dist/transforms/expand-logical-assignment.d.ts.map +1 -0
  102. package/dist/transforms/expand-logical-assignment.js +73 -0
  103. package/dist/transforms/expand-logical-assignment.js.map +1 -0
  104. package/dist/transforms/extract-object-properties.d.ts +21 -0
  105. package/dist/transforms/extract-object-properties.d.ts.map +1 -0
  106. package/dist/transforms/extract-object-properties.js +340 -0
  107. package/dist/transforms/extract-object-properties.js.map +1 -0
  108. package/dist/transforms/fold-builtin-methods.d.ts +23 -0
  109. package/dist/transforms/fold-builtin-methods.d.ts.map +1 -0
  110. package/dist/transforms/fold-builtin-methods.js +839 -0
  111. package/dist/transforms/fold-builtin-methods.js.map +1 -0
  112. package/dist/transforms/fold-constants.d.ts +16 -0
  113. package/dist/transforms/fold-constants.d.ts.map +1 -0
  114. package/dist/transforms/fold-constants.js +421 -0
  115. package/dist/transforms/fold-constants.js.map +1 -0
  116. package/dist/transforms/for-to-while.d.ts +29 -0
  117. package/dist/transforms/for-to-while.d.ts.map +1 -0
  118. package/dist/transforms/for-to-while.js +91 -0
  119. package/dist/transforms/for-to-while.js.map +1 -0
  120. package/dist/transforms/function-declaration-to-expression.d.ts +28 -0
  121. package/dist/transforms/function-declaration-to-expression.d.ts.map +1 -0
  122. package/dist/transforms/function-declaration-to-expression.js +95 -0
  123. package/dist/transforms/function-declaration-to-expression.js.map +1 -0
  124. package/dist/transforms/inline-functions.d.ts +16 -0
  125. package/dist/transforms/inline-functions.d.ts.map +1 -0
  126. package/dist/transforms/inline-functions.js +896 -0
  127. package/dist/transforms/inline-functions.js.map +1 -0
  128. package/dist/transforms/logical-to-ternary.d.ts +20 -0
  129. package/dist/transforms/logical-to-ternary.d.ts.map +1 -0
  130. package/dist/transforms/logical-to-ternary.js +78 -0
  131. package/dist/transforms/logical-to-ternary.js.map +1 -0
  132. package/dist/transforms/mangle-properties.d.ts +27 -0
  133. package/dist/transforms/mangle-properties.d.ts.map +1 -0
  134. package/dist/transforms/mangle-properties.js +703 -0
  135. package/dist/transforms/mangle-properties.js.map +1 -0
  136. package/dist/transforms/nullish-coalescing-to-ternary.d.ts +18 -0
  137. package/dist/transforms/nullish-coalescing-to-ternary.d.ts.map +1 -0
  138. package/dist/transforms/nullish-coalescing-to-ternary.js +61 -0
  139. package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -0
  140. package/dist/transforms/numbers-to-strings.d.ts +19 -0
  141. package/dist/transforms/numbers-to-strings.d.ts.map +1 -0
  142. package/dist/transforms/numbers-to-strings.js +51 -0
  143. package/dist/transforms/numbers-to-strings.js.map +1 -0
  144. package/dist/transforms/object-method-to-property.d.ts +34 -0
  145. package/dist/transforms/object-method-to-property.d.ts.map +1 -0
  146. package/dist/transforms/object-method-to-property.js +78 -0
  147. package/dist/transforms/object-method-to-property.js.map +1 -0
  148. package/dist/transforms/optional-chaining-to-ternary.d.ts +19 -0
  149. package/dist/transforms/optional-chaining-to-ternary.d.ts.map +1 -0
  150. package/dist/transforms/optional-chaining-to-ternary.js +206 -0
  151. package/dist/transforms/optional-chaining-to-ternary.js.map +1 -0
  152. package/dist/transforms/pack-declarations-into-parameters.d.ts +22 -0
  153. package/dist/transforms/pack-declarations-into-parameters.d.ts.map +1 -0
  154. package/dist/transforms/pack-declarations-into-parameters.js +208 -0
  155. package/dist/transforms/pack-declarations-into-parameters.js.map +1 -0
  156. package/dist/transforms/remove-anonymous-function-names.d.ts +17 -0
  157. package/dist/transforms/remove-anonymous-function-names.d.ts.map +1 -0
  158. package/dist/transforms/remove-anonymous-function-names.js +72 -0
  159. package/dist/transforms/remove-anonymous-function-names.js.map +1 -0
  160. package/dist/transforms/remove-unreachable-code.d.ts +21 -0
  161. package/dist/transforms/remove-unreachable-code.d.ts.map +1 -0
  162. package/dist/transforms/remove-unreachable-code.js +103 -0
  163. package/dist/transforms/remove-unreachable-code.js.map +1 -0
  164. package/dist/transforms/remove-unused-code.d.ts +18 -0
  165. package/dist/transforms/remove-unused-code.d.ts.map +1 -0
  166. package/dist/transforms/remove-unused-code.js +249 -0
  167. package/dist/transforms/remove-unused-code.js.map +1 -0
  168. package/dist/transforms/rename-identifiers.d.ts +24 -0
  169. package/dist/transforms/rename-identifiers.d.ts.map +1 -0
  170. package/dist/transforms/rename-identifiers.js +264 -0
  171. package/dist/transforms/rename-identifiers.js.map +1 -0
  172. package/dist/transforms/specials-to-strings.d.ts +30 -0
  173. package/dist/transforms/specials-to-strings.d.ts.map +1 -0
  174. package/dist/transforms/specials-to-strings.js +101 -0
  175. package/dist/transforms/specials-to-strings.js.map +1 -0
  176. package/dist/transforms/switch-to-if.d.ts +28 -0
  177. package/dist/transforms/switch-to-if.d.ts.map +1 -0
  178. package/dist/transforms/switch-to-if.js +279 -0
  179. package/dist/transforms/switch-to-if.js.map +1 -0
  180. package/dist/transforms/ternary-to-if.d.ts +20 -0
  181. package/dist/transforms/ternary-to-if.d.ts.map +1 -0
  182. package/dist/transforms/ternary-to-if.js +315 -0
  183. package/dist/transforms/ternary-to-if.js.map +1 -0
  184. package/dist/transforms/update-to-assignment.d.ts +26 -0
  185. package/dist/transforms/update-to-assignment.d.ts.map +1 -0
  186. package/dist/transforms/update-to-assignment.js +120 -0
  187. package/dist/transforms/update-to-assignment.js.map +1 -0
  188. package/dist/transforms/yodify-conditions.d.ts +22 -0
  189. package/dist/transforms/yodify-conditions.d.ts.map +1 -0
  190. package/dist/transforms/yodify-conditions.js +91 -0
  191. package/dist/transforms/yodify-conditions.js.map +1 -0
  192. package/dist/utils/ast.d.ts +10 -0
  193. package/dist/utils/ast.d.ts.map +1 -0
  194. package/dist/utils/ast.js +112 -0
  195. package/dist/utils/ast.js.map +1 -0
  196. package/dist/utils/change-tracking.d.ts +8 -0
  197. package/dist/utils/change-tracking.d.ts.map +1 -0
  198. package/dist/utils/change-tracking.js +8 -0
  199. package/dist/utils/change-tracking.js.map +1 -0
  200. package/dist/utils/dom-properties.d.ts +2 -0
  201. package/dist/utils/dom-properties.d.ts.map +1 -0
  202. package/dist/utils/dom-properties.js +9252 -0
  203. package/dist/utils/dom-properties.js.map +1 -0
  204. package/dist/utils/evaluation-order.d.ts +4 -0
  205. package/dist/utils/evaluation-order.d.ts.map +1 -0
  206. package/dist/utils/evaluation-order.js +152 -0
  207. package/dist/utils/evaluation-order.js.map +1 -0
  208. package/dist/utils/literal.d.ts +5 -0
  209. package/dist/utils/literal.d.ts.map +1 -0
  210. package/dist/utils/literal.js +90 -0
  211. package/dist/utils/literal.js.map +1 -0
  212. package/dist/utils/logical-lowering.d.ts +4 -0
  213. package/dist/utils/logical-lowering.d.ts.map +1 -0
  214. package/dist/utils/logical-lowering.js +104 -0
  215. package/dist/utils/logical-lowering.js.map +1 -0
  216. package/dist/utils/loop-lowering.d.ts +23 -0
  217. package/dist/utils/loop-lowering.d.ts.map +1 -0
  218. package/dist/utils/loop-lowering.js +88 -0
  219. package/dist/utils/loop-lowering.js.map +1 -0
  220. package/dist/utils/paths.d.ts +9 -0
  221. package/dist/utils/paths.d.ts.map +1 -0
  222. package/dist/utils/paths.js +90 -0
  223. package/dist/utils/paths.js.map +1 -0
  224. package/dist/utils/random.d.ts +3 -0
  225. package/dist/utils/random.d.ts.map +1 -0
  226. package/dist/utils/random.js +22 -0
  227. package/dist/utils/random.js.map +1 -0
  228. package/dist/utils/string-generator.d.ts +37 -0
  229. package/dist/utils/string-generator.d.ts.map +1 -0
  230. package/dist/utils/string-generator.js +175 -0
  231. package/dist/utils/string-generator.js.map +1 -0
  232. package/package.json +76 -0
  233. package/src/analysis/constant.ts +310 -0
  234. package/src/analysis/document-order.ts +141 -0
  235. package/src/analysis/inlineability.ts +336 -0
  236. package/src/analysis/purity.ts +315 -0
  237. package/src/index.ts +2 -0
  238. package/src/obfuscator.ts +271 -0
  239. package/src/options.ts +114 -0
  240. package/src/preparation/dot-to-bracket.ts +41 -0
  241. package/src/preparation/expand-destructuring.ts +211 -0
  242. package/src/preparation/expand-template-literals.ts +92 -0
  243. package/src/preparation/rewrite-class-fields.ts +320 -0
  244. package/src/preparation/split-sequence-expressions.ts +40 -0
  245. package/src/preparation/split-variable-declarations.ts +39 -0
  246. package/src/preparation/stringify-object-keys.ts +107 -0
  247. package/src/preparation/wrap-single-statements.ts +63 -0
  248. package/src/transforms/arguments-to-parameters.ts +187 -0
  249. package/src/transforms/arrow-to-function.ts +225 -0
  250. package/src/transforms/collapse-single-use-temps.ts +957 -0
  251. package/src/transforms/const-let-to-var.ts +246 -0
  252. package/src/transforms/const-to-let.ts +38 -0
  253. package/src/transforms/do-while-to-while.ts +69 -0
  254. package/src/transforms/drop-console.ts +119 -0
  255. package/src/transforms/drop-debugger.ts +38 -0
  256. package/src/transforms/expand-binary-assignment.ts +94 -0
  257. package/src/transforms/expand-logical-assignment.ts +84 -0
  258. package/src/transforms/extract-object-properties.ts +404 -0
  259. package/src/transforms/fold-builtin-methods.ts +949 -0
  260. package/src/transforms/fold-constants.ts +462 -0
  261. package/src/transforms/for-to-while.ts +109 -0
  262. package/src/transforms/function-declaration-to-expression.ts +108 -0
  263. package/src/transforms/inline-functions.ts +1074 -0
  264. package/src/transforms/logical-to-ternary.ts +92 -0
  265. package/src/transforms/mangle-properties.ts +819 -0
  266. package/src/transforms/nullish-coalescing-to-ternary.ts +77 -0
  267. package/src/transforms/numbers-to-strings.ts +61 -0
  268. package/src/transforms/object-method-to-property.ts +86 -0
  269. package/src/transforms/optional-chaining-to-ternary.ts +225 -0
  270. package/src/transforms/pack-declarations-into-parameters.ts +233 -0
  271. package/src/transforms/remove-anonymous-function-names.ts +84 -0
  272. package/src/transforms/remove-unreachable-code.ts +115 -0
  273. package/src/transforms/remove-unused-code.ts +296 -0
  274. package/src/transforms/rename-identifiers.ts +289 -0
  275. package/src/transforms/specials-to-strings.ts +116 -0
  276. package/src/transforms/switch-to-if.ts +339 -0
  277. package/src/transforms/ternary-to-if.ts +387 -0
  278. package/src/transforms/update-to-assignment.ts +132 -0
  279. package/src/transforms/yodify-conditions.ts +103 -0
  280. package/src/utils/ast.ts +129 -0
  281. package/src/utils/change-tracking.ts +15 -0
  282. package/src/utils/dom-properties.ts +9252 -0
  283. package/src/utils/evaluation-order.ts +178 -0
  284. package/src/utils/literal.ts +96 -0
  285. package/src/utils/logical-lowering.ts +132 -0
  286. package/src/utils/loop-lowering.ts +109 -0
  287. package/src/utils/paths.ts +103 -0
  288. package/src/utils/random.ts +22 -0
  289. package/src/utils/string-generator.ts +206 -0
@@ -0,0 +1,271 @@
1
+ import generate from '@babel/generator'
2
+ import { parse } from '@babel/parser'
3
+ import type { ParserOptions } from '@babel/parser'
4
+ import type { File } from '@babel/types'
5
+
6
+ import type { ObfuscatorOptions, TransformContext, TransformName } from 'src/options'
7
+ import { dotToBracket } from 'src/preparation/dot-to-bracket'
8
+ import { expandDestructuring } from 'src/preparation/expand-destructuring'
9
+ import { expandTemplateLiterals } from 'src/preparation/expand-template-literals'
10
+ import { rewriteClassFields } from 'src/preparation/rewrite-class-fields'
11
+ import { splitSequenceExpressions } from 'src/preparation/split-sequence-expressions'
12
+ import { splitVariableDeclarations } from 'src/preparation/split-variable-declarations'
13
+ import { stringifyObjectKeys } from 'src/preparation/stringify-object-keys'
14
+ import { wrapSingleStatements } from 'src/preparation/wrap-single-statements'
15
+ import { argumentsToParameters } from 'src/transforms/arguments-to-parameters'
16
+ import { arrowToFunction } from 'src/transforms/arrow-to-function'
17
+ import { collapseSingleUseTemps } from 'src/transforms/collapse-single-use-temps'
18
+ import { constLetToVar } from 'src/transforms/const-let-to-var'
19
+ import { constToLet } from 'src/transforms/const-to-let'
20
+ import { doWhileToWhile } from 'src/transforms/do-while-to-while'
21
+ import { dropConsole } from 'src/transforms/drop-console'
22
+ import { dropDebugger } from 'src/transforms/drop-debugger'
23
+ import { expandBinaryAssignment } from 'src/transforms/expand-binary-assignment'
24
+ import { expandLogicalAssignment } from 'src/transforms/expand-logical-assignment'
25
+ import { extractObjectProperties } from 'src/transforms/extract-object-properties'
26
+ import { foldBuiltinMethods } from 'src/transforms/fold-builtin-methods'
27
+ import { foldConstants } from 'src/transforms/fold-constants'
28
+ import { forToWhile } from 'src/transforms/for-to-while'
29
+ import { functionDeclarationToExpression } from 'src/transforms/function-declaration-to-expression'
30
+ import { inlineFunctions } from 'src/transforms/inline-functions'
31
+ import { logicalToTernary } from 'src/transforms/logical-to-ternary'
32
+ import { mangleProperties } from 'src/transforms/mangle-properties'
33
+ import { nullishCoalescingToTernary } from 'src/transforms/nullish-coalescing-to-ternary'
34
+ import { numbersToStrings } from 'src/transforms/numbers-to-strings'
35
+ import { objectMethodToProperty } from 'src/transforms/object-method-to-property'
36
+ import { optionalChainingToTernary } from 'src/transforms/optional-chaining-to-ternary'
37
+ import { packDeclarationsIntoParameters } from 'src/transforms/pack-declarations-into-parameters'
38
+ import { removeAnonymousFunctionNames } from 'src/transforms/remove-anonymous-function-names'
39
+ import { removeUnreachableCode } from 'src/transforms/remove-unreachable-code'
40
+ import { removeUnusedCode } from 'src/transforms/remove-unused-code'
41
+ import { renameIdentifiers } from 'src/transforms/rename-identifiers'
42
+ import { specialsToStrings } from 'src/transforms/specials-to-strings'
43
+ import { switchToIf } from 'src/transforms/switch-to-if'
44
+ import { ternaryToIf } from 'src/transforms/ternary-to-if'
45
+ import { updateToAssignment } from 'src/transforms/update-to-assignment'
46
+ import { yodifyConditions } from 'src/transforms/yodify-conditions'
47
+ import type { StringGeneratorModeOption } from 'src/utils/string-generator'
48
+
49
+ const parserOptions: ParserOptions = { sourceType: 'unambiguous' }
50
+
51
+ const PRESERVED_COMMENT_RE = /^!|@(?:license|preserve)\b|^#\s*source(?:Mapping)?URL=/iu
52
+
53
+ const DEFAULT_STRING_GENERATOR_MODE: StringGeneratorModeOption = 'mangled'
54
+ const DEFAULT_SEED = 0
55
+
56
+ // Real inputs settle in a handful of iterations. Hitting this cap signals an
57
+ // algorithmic bug; never raise it as a workaround.
58
+ const FIXED_POINT_ITERATION_CAP = 10
59
+
60
+ type TransformFn = (ast: File, ctx: TransformContext) => boolean
61
+ type Phase = readonly [name: string, fn: TransformFn]
62
+ type ConfigurablePhase = readonly [name: TransformName, fn: TransformFn]
63
+
64
+ // Mangling must see quoted access and class fields before preparation erases
65
+ // those shapes. It is opt-in because outside code may use the same properties.
66
+ const BEFORE_PREPARATION: readonly ConfigurablePhase[] = [['mangleProperties', mangleProperties]]
67
+
68
+ // Shape-normalizing passes. Always run; never exposed in `options.transforms`.
69
+ const PREPARATION: readonly Phase[] = [
70
+ ['rewriteClassFields', asBoolean(rewriteClassFields)],
71
+ ['expandDestructuring', asBoolean(expandDestructuring)],
72
+ ['wrapSingleStatements', asBoolean(wrapSingleStatements)],
73
+ ['splitSequenceExpressions', asBoolean(splitSequenceExpressions)],
74
+ ['splitVariableDeclarations', asBoolean(splitVariableDeclarations)],
75
+ ['expandTemplateLiterals', asBoolean(expandTemplateLiterals)],
76
+ ['stringifyObjectKeys', asBoolean(stringifyObjectKeys)],
77
+ ['dotToBracket', asBoolean(dotToBracket)],
78
+ ]
79
+
80
+ // These one-shot passes prepare the fixed point. Lexical declarations become
81
+ // foldable function-scoped bindings, except at script top level where `var`
82
+ // would create a global property. `argumentsToParameters` precedes parameter
83
+ // renaming. Method demotion precedes extraction because any surviving
84
+ // `ObjectMethod` makes extraction refuse the whole literal.
85
+ const PRE_LOOP: readonly ConfigurablePhase[] = [
86
+ ['dropDebugger', dropDebugger],
87
+ ['dropConsole', dropConsole],
88
+ ['constToLet', constToLet],
89
+ ['constLetToVar', constLetToVar],
90
+ ['argumentsToParameters', argumentsToParameters],
91
+ ['objectMethodToProperty', objectMethodToProperty],
92
+ ]
93
+
94
+ // Folds, removals, extraction, and inlining expose work for one another. Stop
95
+ // when a full round makes no change.
96
+ const FIXED_POINT: readonly ConfigurablePhase[] = [
97
+ ['foldConstants', foldConstants],
98
+ ['foldBuiltinMethods', foldBuiltinMethods],
99
+ ['extractObjectProperties', extractObjectProperties],
100
+ ['inlineFunctions', inlineFunctions],
101
+ ['removeUnreachableCode', removeUnreachableCode],
102
+ ['removeUnusedCode', removeUnusedCode],
103
+ ['removeAnonymousFunctionNames', removeAnonymousFunctionNames],
104
+ ['collapseSingleUseTemps', collapseSingleUseTemps],
105
+ ]
106
+
107
+ // Lowerings run from producer to consumer. Arrow capture lands before code can
108
+ // move out of an arrow. Function declarations remain available to the inliner
109
+ // until the fixed point ends. Loop passes converge on `while`. Updates become
110
+ // compound assignments before binary assignment expansion. Logical assignment,
111
+ // optional chaining, nullish coalescing, logical expressions, and ternaries then
112
+ // lower in that order.
113
+ const LOWERING: readonly ConfigurablePhase[] = [
114
+ ['arrowToFunction', arrowToFunction],
115
+ ['functionDeclarationToExpression', functionDeclarationToExpression],
116
+ ['switchToIf', switchToIf],
117
+ ['forToWhile', forToWhile],
118
+ ['doWhileToWhile', doWhileToWhile],
119
+ ['updateToAssignment', updateToAssignment],
120
+ ['expandBinaryAssignment', expandBinaryAssignment],
121
+ ['expandLogicalAssignment', expandLogicalAssignment],
122
+ ['optionalChainingToTernary', optionalChainingToTernary],
123
+ ['nullishCoalescingToTernary', nullishCoalescingToTernary],
124
+ ['logicalToTernary', logicalToTernary],
125
+ ['ternaryToIf', ternaryToIf],
126
+ ]
127
+
128
+ // Yodification and folding need raw numeric literals. Special-value lowering
129
+ // comes next, then number lowering also handles the `1` created for `Infinity`.
130
+ const POST_PROCESS: readonly ConfigurablePhase[] = [
131
+ ['packDeclarationsIntoParameters', packDeclarationsIntoParameters],
132
+ ['yodifyConditions', yodifyConditions],
133
+ ['specialsToStrings', specialsToStrings],
134
+ ['numbersToStrings', numbersToStrings],
135
+ ]
136
+
137
+ // Renaming runs last because later transforms depend on recognizable bindings.
138
+ const RENAME: readonly ConfigurablePhase[] = [['renameIdentifiers', renameIdentifiers]]
139
+
140
+ /** Parses, transforms, and prints JavaScript without mutating external state. */
141
+ export function obfuscate(code: string, options: ObfuscatorOptions = {}): string {
142
+ const log = options.verbose ? makeLogger() : null
143
+ const baseCtx: TransformContext = {
144
+ seed: options.seed ?? DEFAULT_SEED,
145
+ stringGeneratorMode: options.stringGeneratorMode ?? DEFAULT_STRING_GENERATOR_MODE,
146
+ }
147
+ const ast = run(log, 'parse', () => parse(code, parserOptions))
148
+
149
+ for (const [name, fn] of BEFORE_PREPARATION) {
150
+ if (!isEnabled(name, options)) {
151
+ continue
152
+ }
153
+ run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
154
+ }
155
+
156
+ for (const [name, fn] of PREPARATION) {
157
+ run(log, name, () => fn(ast, baseCtx))
158
+ }
159
+
160
+ for (const [name, fn] of PRE_LOOP) {
161
+ if (!isEnabled(name, options)) {
162
+ continue
163
+ }
164
+ run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
165
+ }
166
+
167
+ runFixedPoint(ast, options, baseCtx, log)
168
+
169
+ for (const [name, fn] of LOWERING) {
170
+ if (!isEnabled(name, options)) {
171
+ continue
172
+ }
173
+ run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
174
+ }
175
+
176
+ for (const [name, fn] of POST_PROCESS) {
177
+ if (!isEnabled(name, options)) {
178
+ continue
179
+ }
180
+ run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
181
+ }
182
+
183
+ for (const [name, fn] of RENAME) {
184
+ if (!isEnabled(name, options)) {
185
+ continue
186
+ }
187
+ run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
188
+ }
189
+
190
+ return run(log, 'generate', () => generate(ast, { shouldPrintComment }).code)
191
+ }
192
+
193
+ function isEnabled(name: TransformName, options: ObfuscatorOptions): boolean {
194
+ if (name === 'mangleProperties') {
195
+ return (
196
+ options.transforms?.mangleProperties !== undefined &&
197
+ options.transforms.mangleProperties !== false
198
+ )
199
+ }
200
+ return options.transforms?.[name] !== false
201
+ }
202
+
203
+ // Boolean entries inherit the base. An override replaces its string mode.
204
+ function ctxFor(
205
+ name: TransformName,
206
+ options: ObfuscatorOptions,
207
+ base: TransformContext,
208
+ ): TransformContext {
209
+ const entry = options.transforms?.[name]
210
+ let context = base
211
+ if (entry && typeof entry === 'object' && entry.stringGeneratorMode !== undefined) {
212
+ context = { ...base, stringGeneratorMode: entry.stringGeneratorMode }
213
+ }
214
+ if (name === 'mangleProperties' && entry && typeof entry === 'object') {
215
+ return { ...context, mangleProperties: entry }
216
+ }
217
+ return context
218
+ }
219
+
220
+ function runFixedPoint(
221
+ ast: File,
222
+ options: ObfuscatorOptions,
223
+ ctx: TransformContext,
224
+ log: Logger | null,
225
+ ): void {
226
+ const enabled = FIXED_POINT.filter(([name]) => isEnabled(name, options))
227
+ if (enabled.length === 0) {
228
+ return
229
+ }
230
+
231
+ for (let iter = 0; iter < FIXED_POINT_ITERATION_CAP; iter++) {
232
+ let any = false
233
+ for (const [name, fn] of enabled) {
234
+ if (run(log, name, () => fn(ast, ctx))) {
235
+ any = true
236
+ }
237
+ }
238
+ if (!any) {
239
+ return
240
+ }
241
+ }
242
+ }
243
+
244
+ function asBoolean(fn: (ast: File) => unknown): TransformFn {
245
+ return (ast) => {
246
+ fn(ast)
247
+ return false
248
+ }
249
+ }
250
+
251
+ function shouldPrintComment(value: string): boolean {
252
+ return PRESERVED_COMMENT_RE.test(value)
253
+ }
254
+
255
+ type Logger = <T>(name: string, fn: () => T) => T
256
+
257
+ function run<T>(log: Logger | null, name: string, fn: () => T): T {
258
+ return log ? log(name, fn) : fn()
259
+ }
260
+
261
+ function makeLogger(): Logger {
262
+ const t0 = performance.now()
263
+ const stamp = (): string => `[${(performance.now() - t0).toFixed(0).padStart(6)}ms]`
264
+ return (name, fn) => {
265
+ console.error(`${stamp()} > ${name}`)
266
+ const start = performance.now()
267
+ const result = fn()
268
+ console.error(`${stamp()} < ${name} (${(performance.now() - start).toFixed(1)}ms)`)
269
+ return result
270
+ }
271
+ }
package/src/options.ts ADDED
@@ -0,0 +1,114 @@
1
+ import type { StringGeneratorModeOption } from 'src/utils/string-generator'
2
+
3
+ /**
4
+ * Exported transform entry points. Preparation passes are always enabled and
5
+ * do not appear here.
6
+ */
7
+ export type TransformName =
8
+ | 'dropConsole'
9
+ | 'dropDebugger'
10
+ | 'constToLet'
11
+ | 'constLetToVar'
12
+ | 'argumentsToParameters'
13
+ | 'objectMethodToProperty'
14
+ | 'foldConstants'
15
+ | 'foldBuiltinMethods'
16
+ | 'extractObjectProperties'
17
+ | 'inlineFunctions'
18
+ | 'removeUnreachableCode'
19
+ | 'removeUnusedCode'
20
+ | 'removeAnonymousFunctionNames'
21
+ | 'collapseSingleUseTemps'
22
+ | 'arrowToFunction'
23
+ | 'functionDeclarationToExpression'
24
+ | 'switchToIf'
25
+ | 'forToWhile'
26
+ | 'doWhileToWhile'
27
+ | 'updateToAssignment'
28
+ | 'expandBinaryAssignment'
29
+ | 'expandLogicalAssignment'
30
+ | 'optionalChainingToTernary'
31
+ | 'nullishCoalescingToTernary'
32
+ | 'logicalToTernary'
33
+ | 'ternaryToIf'
34
+ | 'packDeclarationsIntoParameters'
35
+ | 'yodifyConditions'
36
+ | 'specialsToStrings'
37
+ | 'numbersToStrings'
38
+ | 'mangleProperties'
39
+ | 'renameIdentifiers'
40
+
41
+ // An override enables the transform and replaces its string-generator mode.
42
+ type TransformEntry = boolean | { stringGeneratorMode?: StringGeneratorModeOption }
43
+
44
+ /** Maps a zero-based ordinal to a stable property name. */
45
+ export type PropertyNameGenerator = (index: number) => string
46
+
47
+ /** Terser-compatible property-mangling options using camel-case names. */
48
+ export interface ManglePropertiesOptions {
49
+ /** Allow names of JavaScript and DOM builtins to be mangled. Default `false`. */
50
+ builtins?: boolean
51
+ /** Reuse and extend mappings across calls or separately transformed files. */
52
+ cache?: Map<string, string>
53
+ /** Keep the original name visible as `_$name$suffix_`. Default `false`. */
54
+ debug?: boolean | string
55
+ /**
56
+ * Preserve quoted occurrences. `true` also reserves their names globally;
57
+ * `'strict'` lets unquoted occurrences of the same name be mangled.
58
+ */
59
+ keepQuoted?: boolean | 'strict'
60
+ /** Custom property-name generator. It takes precedence over `stringGeneratorMode`. */
61
+ nameGenerator?: PropertyNameGenerator
62
+ /**
63
+ * Only names carrying `/*@__MANGLE_PROP__*\/` or `/*#__MANGLE_PROP__*\/` may
64
+ * be mangled. Default `false`.
65
+ */
66
+ onlyAnnotated?: boolean
67
+ /** Only names already present in `cache` may be mangled. Default `false`. */
68
+ onlyCache?: boolean
69
+ /** Only property names matching this expression may be mangled. */
70
+ regex?: RegExp | string
71
+ /** Property names that must not be mangled. */
72
+ reserved?: readonly string[]
73
+ /** Override the default string-generator mode for property names. */
74
+ stringGeneratorMode?: StringGeneratorModeOption
75
+ /** Mangle accesses rooted at undeclared identifiers. Default `false`. */
76
+ undeclared?: boolean
77
+ }
78
+
79
+ type SimpleTransform = Exclude<
80
+ TransformName,
81
+ 'mangleProperties' | 'renameIdentifiers' | 'specialsToStrings'
82
+ >
83
+
84
+ type TransformsMap = Partial<Record<SimpleTransform, boolean>> & {
85
+ mangleProperties?: boolean | ManglePropertiesOptions
86
+ renameIdentifiers?: TransformEntry
87
+ specialsToStrings?: TransformEntry
88
+ }
89
+
90
+ export interface ObfuscatorOptions {
91
+ /** Logs each pipeline step and its elapsed milliseconds to stderr. */
92
+ verbose?: boolean
93
+ /**
94
+ * Per-transform toggles and string-generator overrides. `mangleProperties` is
95
+ * opt-in because property names can cross the program boundary. Other omitted
96
+ * transforms default to enabled.
97
+ */
98
+ transforms?: TransformsMap
99
+ /**
100
+ * Default mode for generated identifiers, labels, and string contents. An
101
+ * array mixes modes uniformly. Individual transforms may override it through
102
+ * `transforms.<name>.stringGeneratorMode`. Default `'mangled'`.
103
+ */
104
+ stringGeneratorMode?: StringGeneratorModeOption
105
+ /** Seed for any nondeterministic transform. Same seed, same output. Default `0`. */
106
+ seed?: number
107
+ }
108
+
109
+ // Resolved per-call defaults handed to every transform.
110
+ export interface TransformContext {
111
+ mangleProperties?: ManglePropertiesOptions
112
+ seed: number
113
+ stringGeneratorMode: StringGeneratorModeOption
114
+ }
@@ -0,0 +1,41 @@
1
+ import traverse from '@babel/traverse'
2
+ import * as t from '@babel/types'
3
+ import type { File } from '@babel/types'
4
+
5
+ /**
6
+ * Rewrites `obj.foo` as `obj["foo"]`. Skips already-computed access and
7
+ * private fields; private-name semantics aren't expressible as a string key.
8
+ *
9
+ * @example
10
+ * // ◀️ before
11
+ * user.name = obj.foo.bar;
12
+ * var v = maybe?.value;
13
+ *
14
+ * // ▶️ after
15
+ * user["name"] = obj["foo"]["bar"];
16
+ * var v = maybe?.["value"];
17
+ */
18
+ export function dotToBracket(ast: File): void {
19
+ traverse(ast, visitor as Parameters<typeof traverse>[1])
20
+ }
21
+
22
+ const visitor = {
23
+ 'MemberExpression|OptionalMemberExpression'(path: {
24
+ node: t.MemberExpression | t.OptionalMemberExpression
25
+ }) {
26
+ convert(path.node)
27
+ },
28
+ } as const
29
+
30
+ function convert(node: t.MemberExpression | t.OptionalMemberExpression): void {
31
+ if (node.computed) {
32
+ return
33
+ }
34
+ if (!t.isIdentifier(node.property)) {
35
+ return
36
+ }
37
+ // Babel 8 narrows members by `computed`, so both fields need widening.
38
+ const bracketed = node as t.MemberExpression | t.OptionalMemberExpression
39
+ bracketed.property = t.stringLiteral(node.property.name)
40
+ bracketed.computed = true
41
+ }
@@ -0,0 +1,211 @@
1
+ import traverse from '@babel/traverse'
2
+ import type { NodePath, Scope } from '@babel/traverse'
3
+ import * as t from '@babel/types'
4
+ import type { File } from '@babel/types'
5
+
6
+ import { enclosingScopeHasDirectEval, isInsideWith } from 'src/utils/ast'
7
+
8
+ type Kind = t.VariableDeclaration['kind']
9
+
10
+ // Babel 8 omits `AssignmentPattern` from `LVal`, so list these targets directly.
11
+ type BindingTarget = t.Identifier | t.ObjectPattern | t.ArrayPattern | t.AssignmentPattern
12
+
13
+ /**
14
+ * Rewrites object destructuring declarations as individual property reads.
15
+ * Array patterns stay intact because indexed reads cannot reproduce the iterator
16
+ * protocol.
17
+ *
18
+ * The source, computed keys, and getters keep their original evaluation counts.
19
+ * Defaults compare against `void 0` because `undefined` can be shadowed.
20
+ *
21
+ * `with` and direct `eval` can expose synthesized names that Babel cannot see, so
22
+ * those scopes are skipped. A `RequireObjectCoercible` guard runs before any
23
+ * computed key, matching ObjectBindingPattern evaluation order.
24
+ *
25
+ * @example
26
+ * // ◀️ before
27
+ * const { a, b: renamed, c = 3 } = obj;
28
+ *
29
+ * // ▶️ after
30
+ * const _ref = obj;
31
+ * const a = _ref.a;
32
+ * const renamed = _ref.b;
33
+ * const _c = _ref.c;
34
+ * const c = _c === void 0 ? 3 : _c;
35
+ */
36
+ export function expandDestructuring(ast: File): void {
37
+ traverse(ast, visitor)
38
+ }
39
+
40
+ const visitor = {
41
+ VariableDeclaration(path: NodePath<t.VariableDeclaration>) {
42
+ if (typeof path.listKey !== 'string') {
43
+ return
44
+ }
45
+ // Babel cannot prove a generated name hidden from `with` or direct `eval`.
46
+ if (isInsideWith(path) || enclosingScopeHasDirectEval(path)) {
47
+ return
48
+ }
49
+
50
+ const { kind } = path.node
51
+ const replacement: t.Statement[] = []
52
+ let changed = false
53
+
54
+ for (const declarator of path.node.declarations) {
55
+ const { id, init } = declarator
56
+
57
+ if (
58
+ !init ||
59
+ !t.isObjectPattern(id) ||
60
+ containsRestElement(id) ||
61
+ containsArrayPattern(id) ||
62
+ containsEmptyObjectPattern(id)
63
+ ) {
64
+ replacement.push(t.variableDeclaration(kind, [declarator]))
65
+ continue
66
+ }
67
+
68
+ let source: t.Expression = init
69
+ // Snapshot a mutable identifier before reading more than one property.
70
+ if (t.isIdentifier(source) && topLevelReadCount(id) > 1) {
71
+ const temp = path.scope.generateUidIdentifier('ref')
72
+ replacement.push(t.variableDeclaration(kind, [t.variableDeclarator(temp, source)]))
73
+ source = t.cloneNode(temp)
74
+ }
75
+
76
+ replacement.push(...expandPattern(kind, id, source, path.scope))
77
+ changed = true
78
+ }
79
+
80
+ if (changed) {
81
+ path.replaceWithMultiple(replacement)
82
+ }
83
+ },
84
+ }
85
+
86
+ function expandPattern(
87
+ kind: Kind,
88
+ target: BindingTarget,
89
+ source: t.Expression,
90
+ scope: Scope,
91
+ ): t.Statement[] {
92
+ if (t.isIdentifier(target)) {
93
+ return [t.variableDeclaration(kind, [t.variableDeclarator(target, source)])]
94
+ }
95
+
96
+ if (t.isAssignmentPattern(target)) {
97
+ const innerName =
98
+ t.isIdentifier(target.left) && t.isValidIdentifier(target.left.name)
99
+ ? scope.generateUid(target.left.name)
100
+ : scope.generateUid('default')
101
+ const innerId = t.identifier(innerName)
102
+ const decls: t.Statement[] = [
103
+ t.variableDeclaration(kind, [t.variableDeclarator(t.cloneNode(innerId), source)]),
104
+ ]
105
+ const defaulted = t.conditionalExpression(
106
+ // `void 0`, not the shadowable identifier `undefined`.
107
+ t.binaryExpression(
108
+ '===',
109
+ t.cloneNode(innerId),
110
+ t.unaryExpression('void', t.numericLiteral(0)),
111
+ ),
112
+ target.right,
113
+ t.cloneNode(innerId),
114
+ )
115
+ decls.push(...expandPattern(kind, target.left as BindingTarget, defaulted, scope))
116
+ return decls
117
+ }
118
+
119
+ if (t.isObjectPattern(target)) {
120
+ const decls: t.Statement[] = []
121
+ // Snapshot before repeated or nested property evaluation.
122
+ let src = source
123
+ if (!t.isIdentifier(src)) {
124
+ const temp = scope.generateUidIdentifier('ref')
125
+ decls.push(t.variableDeclaration(kind, [t.variableDeclarator(temp, src)]))
126
+ src = t.cloneNode(temp)
127
+ }
128
+ // `({} = src)` reproduces RequireObjectCoercible before computed keys run.
129
+ if (hasComputedKey(target)) {
130
+ decls.push(
131
+ t.expressionStatement(t.assignmentExpression('=', t.objectPattern([]), t.cloneNode(src))),
132
+ )
133
+ }
134
+ for (const prop of target.properties) {
135
+ if (t.isRestElement(prop)) {
136
+ continue
137
+ }
138
+ const { key } = prop
139
+ const useComputed = prop.computed || !t.isIdentifier(key)
140
+ const access = t.memberExpression(
141
+ t.cloneNode(src),
142
+ t.cloneNode(key) as t.Expression,
143
+ useComputed,
144
+ )
145
+ decls.push(...expandPattern(kind, prop.value as BindingTarget, access, scope))
146
+ }
147
+ return decls
148
+ }
149
+
150
+ return [t.variableDeclaration(kind, [t.variableDeclarator(target, source)])]
151
+ }
152
+
153
+ function topLevelReadCount(pattern: t.ObjectPattern): number {
154
+ return pattern.properties.filter((prop) => !t.isRestElement(prop)).length
155
+ }
156
+
157
+ function hasComputedKey(pattern: t.ObjectPattern): boolean {
158
+ return pattern.properties.some((prop) => t.isObjectProperty(prop) && prop.computed)
159
+ }
160
+
161
+ // An empty pattern still throws on nullish input, with no property read to carry
162
+ // that check after expansion.
163
+ function containsEmptyObjectPattern(pattern: BindingTarget): boolean {
164
+ if (t.isObjectPattern(pattern)) {
165
+ return (
166
+ pattern.properties.length === 0 ||
167
+ pattern.properties.some(
168
+ (prop) =>
169
+ t.isObjectProperty(prop) && containsEmptyObjectPattern(prop.value as BindingTarget),
170
+ )
171
+ )
172
+ }
173
+ if (t.isAssignmentPattern(pattern)) {
174
+ return containsEmptyObjectPattern(pattern.left as BindingTarget)
175
+ }
176
+ return false
177
+ }
178
+
179
+ function containsArrayPattern(pattern: BindingTarget): boolean {
180
+ if (t.isArrayPattern(pattern)) {
181
+ return true
182
+ }
183
+ if (t.isObjectPattern(pattern)) {
184
+ return pattern.properties.some(
185
+ (prop) => t.isObjectProperty(prop) && containsArrayPattern(prop.value as BindingTarget),
186
+ )
187
+ }
188
+ if (t.isAssignmentPattern(pattern)) {
189
+ return containsArrayPattern(pattern.left as BindingTarget)
190
+ }
191
+ return false
192
+ }
193
+
194
+ function containsRestElement(pattern: BindingTarget): boolean {
195
+ if (t.isObjectPattern(pattern)) {
196
+ return pattern.properties.some(
197
+ (prop) =>
198
+ t.isRestElement(prop) ||
199
+ (t.isObjectProperty(prop) && containsRestElement(prop.value as BindingTarget)),
200
+ )
201
+ }
202
+ if (t.isArrayPattern(pattern)) {
203
+ return pattern.elements.some(
204
+ (el) => el !== null && (t.isRestElement(el) || containsRestElement(el as BindingTarget)),
205
+ )
206
+ }
207
+ if (t.isAssignmentPattern(pattern)) {
208
+ return containsRestElement(pattern.left as BindingTarget)
209
+ }
210
+ return false
211
+ }