webfuscator 1.2.1 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/README.md +3 -3
  2. package/dist/analysis/constant.d.ts.map +1 -1
  3. package/dist/analysis/constant.js +10 -27
  4. package/dist/analysis/constant.js.map +1 -1
  5. package/dist/analysis/inlineability.d.ts.map +1 -1
  6. package/dist/analysis/inlineability.js +5 -13
  7. package/dist/analysis/inlineability.js.map +1 -1
  8. package/dist/analysis/purity.d.ts +3 -3
  9. package/dist/analysis/purity.d.ts.map +1 -1
  10. package/dist/analysis/purity.js +28 -75
  11. package/dist/analysis/purity.js.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/obfuscator.d.ts +2 -1
  15. package/dist/obfuscator.d.ts.map +1 -1
  16. package/dist/obfuscator.js +47 -12
  17. package/dist/obfuscator.js.map +1 -1
  18. package/dist/options.d.ts +17 -4
  19. package/dist/options.d.ts.map +1 -1
  20. package/dist/preparation/expand-destructuring.d.ts +2 -3
  21. package/dist/preparation/expand-destructuring.d.ts.map +1 -1
  22. package/dist/preparation/expand-destructuring.js +2 -8
  23. package/dist/preparation/expand-destructuring.js.map +1 -1
  24. package/dist/transforms/arguments-to-parameters.d.ts +5 -4
  25. package/dist/transforms/arguments-to-parameters.d.ts.map +1 -1
  26. package/dist/transforms/arguments-to-parameters.js +5 -4
  27. package/dist/transforms/arguments-to-parameters.js.map +1 -1
  28. package/dist/transforms/arrow-to-function.d.ts +4 -3
  29. package/dist/transforms/arrow-to-function.d.ts.map +1 -1
  30. package/dist/transforms/arrow-to-function.js +6 -10
  31. package/dist/transforms/arrow-to-function.js.map +1 -1
  32. package/dist/transforms/collapse-single-use-temps.d.ts +2 -1
  33. package/dist/transforms/collapse-single-use-temps.d.ts.map +1 -1
  34. package/dist/transforms/collapse-single-use-temps.js +12 -20
  35. package/dist/transforms/collapse-single-use-temps.js.map +1 -1
  36. package/dist/transforms/conceal-strings.d.ts +56 -0
  37. package/dist/transforms/conceal-strings.d.ts.map +1 -0
  38. package/dist/transforms/conceal-strings.js +356 -0
  39. package/dist/transforms/conceal-strings.js.map +1 -0
  40. package/dist/transforms/const-let-to-var.d.ts +4 -3
  41. package/dist/transforms/const-let-to-var.d.ts.map +1 -1
  42. package/dist/transforms/const-let-to-var.js +8 -67
  43. package/dist/transforms/const-let-to-var.js.map +1 -1
  44. package/dist/transforms/const-to-let.d.ts +1 -3
  45. package/dist/transforms/const-to-let.d.ts.map +1 -1
  46. package/dist/transforms/const-to-let.js +1 -8
  47. package/dist/transforms/const-to-let.js.map +1 -1
  48. package/dist/transforms/drop-console.d.ts.map +1 -1
  49. package/dist/transforms/drop-console.js +3 -5
  50. package/dist/transforms/drop-console.js.map +1 -1
  51. package/dist/transforms/expand-binary-assignment.d.ts.map +1 -1
  52. package/dist/transforms/expand-binary-assignment.js +0 -5
  53. package/dist/transforms/expand-binary-assignment.js.map +1 -1
  54. package/dist/transforms/expand-logical-assignment.d.ts.map +1 -1
  55. package/dist/transforms/expand-logical-assignment.js +0 -5
  56. package/dist/transforms/expand-logical-assignment.js.map +1 -1
  57. package/dist/transforms/extract-object-properties.js +5 -12
  58. package/dist/transforms/extract-object-properties.js.map +1 -1
  59. package/dist/transforms/fold-builtin-methods.d.ts.map +1 -1
  60. package/dist/transforms/fold-builtin-methods.js +44 -14
  61. package/dist/transforms/fold-builtin-methods.js.map +1 -1
  62. package/dist/transforms/fold-constants.js +5 -2
  63. package/dist/transforms/fold-constants.js.map +1 -1
  64. package/dist/transforms/function-declaration-to-expression.d.ts +5 -6
  65. package/dist/transforms/function-declaration-to-expression.d.ts.map +1 -1
  66. package/dist/transforms/function-declaration-to-expression.js +5 -6
  67. package/dist/transforms/function-declaration-to-expression.js.map +1 -1
  68. package/dist/transforms/hoist-variables.d.ts +29 -0
  69. package/dist/transforms/hoist-variables.d.ts.map +1 -0
  70. package/dist/transforms/hoist-variables.js +215 -0
  71. package/dist/transforms/hoist-variables.js.map +1 -0
  72. package/dist/transforms/inline-functions.js +11 -40
  73. package/dist/transforms/inline-functions.js.map +1 -1
  74. package/dist/transforms/logical-to-ternary.d.ts +1 -1
  75. package/dist/transforms/logical-to-ternary.js +5 -9
  76. package/dist/transforms/logical-to-ternary.js.map +1 -1
  77. package/dist/transforms/mangle-properties.d.ts +2 -5
  78. package/dist/transforms/mangle-properties.d.ts.map +1 -1
  79. package/dist/transforms/mangle-properties.js +9 -9
  80. package/dist/transforms/mangle-properties.js.map +1 -1
  81. package/dist/transforms/merge-variable-declarations.d.ts +17 -0
  82. package/dist/transforms/merge-variable-declarations.d.ts.map +1 -0
  83. package/dist/transforms/merge-variable-declarations.js +68 -0
  84. package/dist/transforms/merge-variable-declarations.js.map +1 -0
  85. package/dist/transforms/nullish-coalescing-to-ternary.d.ts +4 -5
  86. package/dist/transforms/nullish-coalescing-to-ternary.d.ts.map +1 -1
  87. package/dist/transforms/nullish-coalescing-to-ternary.js +7 -12
  88. package/dist/transforms/nullish-coalescing-to-ternary.js.map +1 -1
  89. package/dist/transforms/numbers-to-strings.d.ts.map +1 -1
  90. package/dist/transforms/numbers-to-strings.js +1 -8
  91. package/dist/transforms/numbers-to-strings.js.map +1 -1
  92. package/dist/transforms/object-method-to-property.d.ts +1 -1
  93. package/dist/transforms/object-method-to-property.js +1 -1
  94. package/dist/transforms/optional-chaining-to-ternary.d.ts +4 -3
  95. package/dist/transforms/optional-chaining-to-ternary.d.ts.map +1 -1
  96. package/dist/transforms/optional-chaining-to-ternary.js +9 -9
  97. package/dist/transforms/optional-chaining-to-ternary.js.map +1 -1
  98. package/dist/transforms/pack-declarations-into-parameters.d.ts.map +1 -1
  99. package/dist/transforms/pack-declarations-into-parameters.js +5 -21
  100. package/dist/transforms/pack-declarations-into-parameters.js.map +1 -1
  101. package/dist/transforms/pack.d.ts +4 -13
  102. package/dist/transforms/pack.d.ts.map +1 -1
  103. package/dist/transforms/pack.js +9 -29
  104. package/dist/transforms/pack.js.map +1 -1
  105. package/dist/transforms/remove-anonymous-function-names.d.ts +2 -4
  106. package/dist/transforms/remove-anonymous-function-names.d.ts.map +1 -1
  107. package/dist/transforms/remove-anonymous-function-names.js +2 -40
  108. package/dist/transforms/remove-anonymous-function-names.js.map +1 -1
  109. package/dist/transforms/remove-unreachable-code.js +2 -21
  110. package/dist/transforms/remove-unreachable-code.js.map +1 -1
  111. package/dist/transforms/remove-unused-code.d.ts.map +1 -1
  112. package/dist/transforms/remove-unused-code.js +12 -49
  113. package/dist/transforms/remove-unused-code.js.map +1 -1
  114. package/dist/transforms/rename-identifiers.d.ts +3 -3
  115. package/dist/transforms/rename-identifiers.d.ts.map +1 -1
  116. package/dist/transforms/rename-identifiers.js +31 -35
  117. package/dist/transforms/rename-identifiers.js.map +1 -1
  118. package/dist/transforms/reorder-function-declarations.d.ts +24 -0
  119. package/dist/transforms/reorder-function-declarations.d.ts.map +1 -0
  120. package/dist/transforms/reorder-function-declarations.js +62 -0
  121. package/dist/transforms/reorder-function-declarations.js.map +1 -0
  122. package/dist/transforms/specials-to-strings.d.ts.map +1 -1
  123. package/dist/transforms/specials-to-strings.js +2 -3
  124. package/dist/transforms/specials-to-strings.js.map +1 -1
  125. package/dist/transforms/switch-to-if.js +5 -15
  126. package/dist/transforms/switch-to-if.js.map +1 -1
  127. package/dist/transforms/ternary-to-if.d.ts.map +1 -1
  128. package/dist/transforms/ternary-to-if.js +5 -9
  129. package/dist/transforms/ternary-to-if.js.map +1 -1
  130. package/dist/transforms/update-to-assignment.d.ts.map +1 -1
  131. package/dist/transforms/update-to-assignment.js +6 -23
  132. package/dist/transforms/update-to-assignment.js.map +1 -1
  133. package/dist/transforms/yodify-conditions.d.ts +3 -4
  134. package/dist/transforms/yodify-conditions.d.ts.map +1 -1
  135. package/dist/transforms/yodify-conditions.js +3 -4
  136. package/dist/transforms/yodify-conditions.js.map +1 -1
  137. package/dist/utils/ast.d.ts +1 -3
  138. package/dist/utils/ast.d.ts.map +1 -1
  139. package/dist/utils/ast.js +4 -60
  140. package/dist/utils/ast.js.map +1 -1
  141. package/dist/utils/evaluation-order.js +1 -1
  142. package/dist/utils/evaluation-order.js.map +1 -1
  143. package/dist/utils/literal.d.ts.map +1 -1
  144. package/dist/utils/literal.js +74 -2
  145. package/dist/utils/literal.js.map +1 -1
  146. package/dist/utils/logical-lowering.d.ts.map +1 -1
  147. package/dist/utils/logical-lowering.js +7 -9
  148. package/dist/utils/logical-lowering.js.map +1 -1
  149. package/dist/utils/nullish.d.ts +4 -0
  150. package/dist/utils/nullish.d.ts.map +1 -0
  151. package/dist/utils/nullish.js +8 -0
  152. package/dist/utils/nullish.js.map +1 -0
  153. package/dist/utils/paths.d.ts +0 -2
  154. package/dist/utils/paths.d.ts.map +1 -1
  155. package/dist/utils/paths.js +0 -28
  156. package/dist/utils/paths.js.map +1 -1
  157. package/dist/utils/statement-lists.d.ts +9 -0
  158. package/dist/utils/statement-lists.d.ts.map +1 -0
  159. package/dist/utils/statement-lists.js +27 -0
  160. package/dist/utils/statement-lists.js.map +1 -0
  161. package/package.json +6 -2
  162. package/src/analysis/constant.ts +12 -33
  163. package/src/analysis/inlineability.ts +5 -13
  164. package/src/analysis/purity.ts +29 -92
  165. package/src/index.ts +1 -0
  166. package/src/obfuscator.ts +51 -14
  167. package/src/options.ts +21 -3
  168. package/src/preparation/expand-destructuring.ts +2 -10
  169. package/src/transforms/arguments-to-parameters.ts +5 -4
  170. package/src/transforms/arrow-to-function.ts +6 -10
  171. package/src/transforms/collapse-single-use-temps.ts +12 -23
  172. package/src/transforms/conceal-strings.ts +525 -0
  173. package/src/transforms/const-let-to-var.ts +9 -72
  174. package/src/transforms/const-to-let.ts +1 -8
  175. package/src/transforms/drop-console.ts +3 -5
  176. package/src/transforms/expand-binary-assignment.ts +0 -5
  177. package/src/transforms/expand-logical-assignment.ts +0 -5
  178. package/src/transforms/extract-object-properties.ts +5 -14
  179. package/src/transforms/fold-builtin-methods.ts +63 -17
  180. package/src/transforms/fold-constants.ts +5 -2
  181. package/src/transforms/function-declaration-to-expression.ts +5 -6
  182. package/src/transforms/hoist-variables.ts +262 -0
  183. package/src/transforms/inline-functions.ts +16 -47
  184. package/src/transforms/logical-to-ternary.ts +5 -10
  185. package/src/transforms/mangle-properties.ts +9 -12
  186. package/src/transforms/merge-variable-declarations.ts +91 -0
  187. package/src/transforms/nullish-coalescing-to-ternary.ts +7 -13
  188. package/src/transforms/numbers-to-strings.ts +2 -12
  189. package/src/transforms/object-method-to-property.ts +1 -1
  190. package/src/transforms/optional-chaining-to-ternary.ts +9 -9
  191. package/src/transforms/pack-declarations-into-parameters.ts +11 -21
  192. package/src/transforms/pack.ts +9 -30
  193. package/src/transforms/remove-anonymous-function-names.ts +3 -46
  194. package/src/transforms/remove-unreachable-code.ts +2 -22
  195. package/src/transforms/remove-unused-code.ts +14 -74
  196. package/src/transforms/rename-identifiers.ts +44 -35
  197. package/src/transforms/reorder-function-declarations.ts +78 -0
  198. package/src/transforms/specials-to-strings.ts +2 -3
  199. package/src/transforms/switch-to-if.ts +5 -22
  200. package/src/transforms/ternary-to-if.ts +5 -9
  201. package/src/transforms/update-to-assignment.ts +6 -24
  202. package/src/transforms/yodify-conditions.ts +3 -4
  203. package/src/utils/ast.ts +4 -69
  204. package/src/utils/evaluation-order.ts +1 -1
  205. package/src/utils/literal.ts +91 -2
  206. package/src/utils/logical-lowering.ts +7 -11
  207. package/src/utils/nullish.ts +23 -0
  208. package/src/utils/paths.ts +0 -32
  209. package/src/utils/statement-lists.ts +45 -0
package/src/obfuscator.ts CHANGED
@@ -14,6 +14,7 @@ import { wrapSingleStatements } from './preparation/wrap-single-statements'
14
14
  import { argumentsToParameters } from './transforms/arguments-to-parameters'
15
15
  import { arrowToFunction } from './transforms/arrow-to-function'
16
16
  import { collapseSingleUseTemps } from './transforms/collapse-single-use-temps'
17
+ import { concealStrings } from './transforms/conceal-strings'
17
18
  import { constLetToVar } from './transforms/const-let-to-var'
18
19
  import { constToLet } from './transforms/const-to-let'
19
20
  import { doWhileToWhile } from './transforms/do-while-to-while'
@@ -26,9 +27,11 @@ import { foldBuiltinMethods } from './transforms/fold-builtin-methods'
26
27
  import { foldConstants } from './transforms/fold-constants'
27
28
  import { forToWhile } from './transforms/for-to-while'
28
29
  import { functionDeclarationToExpression } from './transforms/function-declaration-to-expression'
30
+ import { hoistVariables } from './transforms/hoist-variables'
29
31
  import { inlineFunctions } from './transforms/inline-functions'
30
32
  import { logicalToTernary } from './transforms/logical-to-ternary'
31
33
  import { mangleProperties } from './transforms/mangle-properties'
34
+ import { mergeVariableDeclarations } from './transforms/merge-variable-declarations'
32
35
  import { nullishCoalescingToTernary } from './transforms/nullish-coalescing-to-ternary'
33
36
  import { numbersToStrings } from './transforms/numbers-to-strings'
34
37
  import { objectMethodToProperty } from './transforms/object-method-to-property'
@@ -39,6 +42,7 @@ import { removeAnonymousFunctionNames } from './transforms/remove-anonymous-func
39
42
  import { removeUnreachableCode } from './transforms/remove-unreachable-code'
40
43
  import { removeUnusedCode } from './transforms/remove-unused-code'
41
44
  import { renameIdentifiers } from './transforms/rename-identifiers'
45
+ import { reorderFunctionDeclarations } from './transforms/reorder-function-declarations'
42
46
  import { specialsToStrings } from './transforms/specials-to-strings'
43
47
  import { switchToIf } from './transforms/switch-to-if'
44
48
  import { ternaryToIf } from './transforms/ternary-to-if'
@@ -48,8 +52,6 @@ import type { StringGeneratorModeOption } from './utils/string-generator'
48
52
 
49
53
  const parserOptions: ParserOptions = { sourceType: 'unambiguous' }
50
54
 
51
- const PRESERVED_COMMENT_RE = /^!|@(?:license|preserve)\b|^#\s*source(?:Mapping)?URL=/iu
52
-
53
55
  const DEFAULT_STRING_GENERATOR_MODE: StringGeneratorModeOption = 'mangled'
54
56
  const DEFAULT_SEED = 0
55
57
 
@@ -76,11 +78,10 @@ const PREPARATION: readonly Phase[] = [
76
78
  ['dotToBracket', asBoolean(dotToBracket)],
77
79
  ]
78
80
 
79
- // These one-shot passes prepare the fixed point. Lexical declarations become
80
- // foldable function-scoped bindings, except at script top level where `var`
81
- // would create a global property. `argumentsToParameters` precedes parameter
82
- // renaming. Method demotion precedes extraction because any surviving
83
- // `ObjectMethod` makes extraction refuse the whole literal.
81
+ // `constLetToVar` must precede the fixed point because folds need function-scoped
82
+ // bindings. `argumentsToParameters` must precede parameter renaming.
83
+ // `objectMethodToProperty` must precede extraction because one `ObjectMethod`
84
+ // blocks the whole object literal.
84
85
  const PRE_LOOP: readonly ConfigurablePhase[] = [
85
86
  ['dropDebugger', dropDebugger],
86
87
  ['dropConsole', dropConsole],
@@ -124,15 +125,25 @@ const LOWERING: readonly ConfigurablePhase[] = [
124
125
  ['ternaryToIf', ternaryToIf],
125
126
  ]
126
127
 
127
- // Yodification and folding need raw numeric literals. Special-value lowering
128
- // comes next, then number lowering also handles the `1` created for `Infinity`.
128
+ // `packDeclarationsIntoParameters` must precede declaration motion and merging.
129
+ // `reorderFunctionDeclarations` follows `functionDeclarationToExpression`, so it
130
+ // moves only declarations still needed for hoisting. `hoistVariables` must run
131
+ // before `mergeVariableDeclarations`. `numbersToStrings` follows the fixed point,
132
+ // yodification, and `specialsToStrings` because they need or create numbers.
129
133
  const POST_PROCESS: readonly ConfigurablePhase[] = [
130
134
  ['packDeclarationsIntoParameters', packDeclarationsIntoParameters],
135
+ ['reorderFunctionDeclarations', reorderFunctionDeclarations],
136
+ ['hoistVariables', hoistVariables],
137
+ ['mergeVariableDeclarations', mergeVariableDeclarations],
131
138
  ['yodifyConditions', yodifyConditions],
132
139
  ['specialsToStrings', specialsToStrings],
133
140
  ['numbersToStrings', numbersToStrings],
134
141
  ]
135
142
 
143
+ // Conceal strings after every string-producing pass, but before renaming so the
144
+ // generated vault bindings use the caller's name generator.
145
+ const PROTECTION: readonly ConfigurablePhase[] = [['concealStrings', concealStrings]]
146
+
136
147
  // Renaming runs last because later transforms depend on recognizable bindings.
137
148
  const RENAME: readonly ConfigurablePhase[] = [['renameIdentifiers', renameIdentifiers]]
138
149
 
@@ -141,6 +152,19 @@ const RENAME: readonly ConfigurablePhase[] = [['renameIdentifiers', renameIdenti
141
152
  // every other rewrite, including renaming.
142
153
  const PACK: readonly ConfigurablePhase[] = [['pack', pack]]
143
154
 
155
+ // Tests derive their all-transforms setup from this list. The phase tables remain
156
+ // the only source of pipeline order.
157
+ export const CONFIGURABLE_TRANSFORM_NAMES: readonly TransformName[] = [
158
+ ...BEFORE_PREPARATION,
159
+ ...PRE_LOOP,
160
+ ...FIXED_POINT,
161
+ ...LOWERING,
162
+ ...POST_PROCESS,
163
+ ...PROTECTION,
164
+ ...RENAME,
165
+ ...PACK,
166
+ ].map(([name]) => name)
167
+
144
168
  /** Parses, transforms, and prints JavaScript without mutating external state. */
145
169
  export function obfuscate(code: string, options: ObfuscatorOptions = {}): string {
146
170
  const log = options.verbose ? makeLogger() : null
@@ -184,6 +208,13 @@ export function obfuscate(code: string, options: ObfuscatorOptions = {}): string
184
208
  run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
185
209
  }
186
210
 
211
+ for (const [name, fn] of PROTECTION) {
212
+ if (!isEnabled(name, options)) {
213
+ continue
214
+ }
215
+ run(log, name, () => fn(ast, ctxFor(name, options, baseCtx)))
216
+ }
217
+
187
218
  for (const [name, fn] of RENAME) {
188
219
  if (!isEnabled(name, options)) {
189
220
  continue
@@ -201,7 +232,13 @@ export function obfuscate(code: string, options: ObfuscatorOptions = {}): string
201
232
  return run(
202
233
  log,
203
234
  'generate',
204
- () => generate(ast, { minified: options.minify ?? false, shouldPrintComment }).code,
235
+ () =>
236
+ generate(ast, {
237
+ comments: false,
238
+ minified: options.minify ?? false,
239
+ // `comments: false` still allows @license and @preserve.
240
+ shouldPrintComment: () => false,
241
+ }).code,
205
242
  )
206
243
  }
207
244
 
@@ -216,6 +253,10 @@ function ctxFor(
216
253
  options: ObfuscatorOptions,
217
254
  base: TransformContext,
218
255
  ): TransformContext {
256
+ if (name === 'concealStrings') {
257
+ const entry = options.transforms?.concealStrings
258
+ return entry && typeof entry === 'object' ? { ...base, concealStrings: entry } : base
259
+ }
219
260
  const entry = options.transforms?.[name]
220
261
  let context = base
221
262
  if (entry && typeof entry === 'object' && entry.stringGeneratorMode !== undefined) {
@@ -261,10 +302,6 @@ function asBoolean(fn: (ast: File) => unknown): TransformFn {
261
302
  }
262
303
  }
263
304
 
264
- function shouldPrintComment(value: string): boolean {
265
- return PRESERVED_COMMENT_RE.test(value)
266
- }
267
-
268
305
  type Logger = <T>(name: string, fn: () => T) => T
269
306
 
270
307
  function run<T>(log: Logger | null, name: string, fn: () => T): T {
package/src/options.ts CHANGED
@@ -21,6 +21,9 @@ export type TransformName =
21
21
  | 'collapseSingleUseTemps'
22
22
  | 'arrowToFunction'
23
23
  | 'functionDeclarationToExpression'
24
+ | 'reorderFunctionDeclarations'
25
+ | 'hoistVariables'
26
+ | 'mergeVariableDeclarations'
24
27
  | 'switchToIf'
25
28
  | 'forToWhile'
26
29
  | 'doWhileToWhile'
@@ -35,6 +38,7 @@ export type TransformName =
35
38
  | 'yodifyConditions'
36
39
  | 'specialsToStrings'
37
40
  | 'numbersToStrings'
41
+ | 'concealStrings'
38
42
  | 'mangleProperties'
39
43
  | 'renameIdentifiers'
40
44
  | 'pack'
@@ -61,8 +65,8 @@ export interface ManglePropertiesOptions {
61
65
  /** Custom property-name generator. It takes precedence over `stringGeneratorMode`. */
62
66
  nameGenerator?: PropertyNameGenerator
63
67
  /**
64
- * Only names carrying `/*@__MANGLE_PROP__*\/` or `/*#__MANGLE_PROP__*\/` may
65
- * be mangled. Default `false`.
68
+ * Mangle only names with an exact standalone `/*@__MANGLE_PROP__*\/` or
69
+ * `/*#__MANGLE_PROP__*\/` annotation. Default `false`.
66
70
  */
67
71
  onlyAnnotated?: boolean
68
72
  /** Only names already present in `cache` may be mangled. Default `false`. */
@@ -95,12 +99,25 @@ export interface PackOptions {
95
99
  stringGeneratorMode?: StringGeneratorModeOption
96
100
  }
97
101
 
102
+ /** Options for the string vault generated by `concealStrings`. */
103
+ export interface ConcealStringsOptions {
104
+ /** Cache each decoded string in its vault. Default `true`. */
105
+ cache?: boolean
106
+ /** Choose how much of the UTF-16 range pads each pool. Default `'regional'`. */
107
+ cover?: 'full' | 'regional' | 'used'
108
+ /** Reuse one vault entry for equal strings. Default `true`. */
109
+ deduplicate?: boolean
110
+ /** Use one program vault or a separate vault per function. Default `'program'`. */
111
+ scope?: 'function' | 'program'
112
+ }
113
+
98
114
  type SimpleTransform = Exclude<
99
115
  TransformName,
100
- 'mangleProperties' | 'pack' | 'renameIdentifiers' | 'specialsToStrings'
116
+ 'concealStrings' | 'mangleProperties' | 'pack' | 'renameIdentifiers' | 'specialsToStrings'
101
117
  >
102
118
 
103
119
  type TransformsMap = Partial<Record<SimpleTransform, boolean>> & {
120
+ concealStrings?: boolean | ConcealStringsOptions
104
121
  mangleProperties?: boolean | ManglePropertiesOptions
105
122
  pack?: boolean | PackOptions
106
123
  renameIdentifiers?: TransformEntry
@@ -129,6 +146,7 @@ export interface ObfuscatorOptions {
129
146
 
130
147
  // Resolved per-call defaults handed to every transform.
131
148
  export interface TransformContext {
149
+ concealStrings?: ConcealStringsOptions
132
150
  mangleProperties?: ManglePropertiesOptions
133
151
  pack?: PackOptions
134
152
  seed: number
@@ -3,8 +3,6 @@ import type { NodePath, Scope } from '@babel/traverse'
3
3
  import * as t from '@babel/types'
4
4
  import type { File } from '@babel/types'
5
5
 
6
- import { enclosingScopeHasDirectEval, isInsideWith } from '../utils/ast'
7
-
8
6
  type Kind = t.VariableDeclaration['kind']
9
7
 
10
8
  // Babel 8 omits `AssignmentPattern` from `LVal`, so list these targets directly.
@@ -18,9 +16,8 @@ type BindingTarget = t.Identifier | t.ObjectPattern | t.ArrayPattern | t.Assignm
18
16
  * The source, computed keys, and getters keep their original evaluation counts.
19
17
  * Defaults compare against `void 0` because `undefined` can be shadowed.
20
18
  *
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.
19
+ * The `RequireObjectCoercible` guard runs before computed keys to preserve
20
+ * ObjectBindingPattern evaluation order.
24
21
  *
25
22
  * @example
26
23
  * // ◀️ before
@@ -42,11 +39,6 @@ const visitor = {
42
39
  if (typeof path.listKey !== 'string') {
43
40
  return
44
41
  }
45
- // Babel cannot prove a generated name hidden from `with` or direct `eval`.
46
- if (isInsideWith(path) || enclosingScopeHasDirectEval(path)) {
47
- return
48
- }
49
-
50
42
  const kind = path.node.kind
51
43
  const replacement: t.Statement[] = []
52
44
  let changed = false
@@ -8,10 +8,11 @@ import type { ChangeState } from '../utils/change-tracking'
8
8
  import { isInStrictContext } from '../utils/paths'
9
9
 
10
10
  /**
11
- * Replaces `arguments[i]` reads with their mapped parameter in sloppy functions
12
- * with simple parameter lists. Writes, deletion, reassigned parameters, and any
13
- * escape of the arguments object make the mapping unsafe. Missing parameters
14
- * are never added because that would change `fn.length`.
11
+ * Replaces numeric `arguments[i]` reads with the corresponding parameter in
12
+ * sloppy functions with simple parameter lists. It leaves a read unchanged when
13
+ * that index is written or its parameter is reassigned. Reassigning, deleting,
14
+ * or passing around `arguments` disables the rewrite. Missing parameters are
15
+ * never added because that would change `fn.length`.
15
16
  *
16
17
  * @example
17
18
  * // ◀️ before
@@ -2,14 +2,14 @@ import type { NodePath, Visitor } from '@babel/traverse'
2
2
  import * as t from '@babel/types'
3
3
  import type { File } from '@babel/types'
4
4
 
5
- import { enclosingScopeHasDirectEval, isInsideWith } from '../utils/ast'
6
5
  import { traverseForChanges } from '../utils/change-tracking'
7
6
  import type { ChangeState } from '../utils/change-tracking'
8
7
 
9
8
  /**
10
- * Lowers arrows with Babel's capture-aware `arrowFunctionToExpression`. Arrows
11
- * that reach `super` stay intact. Values that escape visible call positions also
12
- * stay intact because only the arrow rejects construction with `new`.
9
+ * Converts arrows with Babel's capture-aware `arrowFunctionToExpression`.
10
+ * Arrows that use `super` stay unchanged. An arrow also stays unchanged unless
11
+ * every use is an immediate call or a discarded expression, because a normal
12
+ * function can be constructed with `new` and has a `prototype`.
13
13
  *
14
14
  * @example
15
15
  * // ◀️ before
@@ -64,11 +64,7 @@ const visitor: Visitor<ChangeState> = {
64
64
  !valueStaysInView(path) ||
65
65
  arrowReferencesSuper(path) ||
66
66
  inUnsafeCaptureContext(path) ||
67
- usesReassignedArguments(path) ||
68
- // `with` can shadow capture aliases, and direct eval can inspect the changed
69
- // runtime meaning of `this` or `arguments`.
70
- isInsideWith(path) ||
71
- enclosingScopeHasDirectEval(path)
67
+ usesReassignedArguments(path)
72
68
  ) {
73
69
  return
74
70
  }
@@ -142,7 +138,7 @@ function valueStaysInView(valuePath: NodePath): boolean {
142
138
  return false
143
139
  }
144
140
  const binding = valuePath.scope.getBinding(parent.node.id.name)
145
- if (!binding || !binding.constant) {
141
+ if (!binding || (binding.kind !== 'const' && !binding.constant)) {
146
142
  return false
147
143
  }
148
144
  return binding.referencePaths.every(
@@ -10,14 +10,14 @@ import {
10
10
  readCrossesFunctionBoundary,
11
11
  } from '../analysis/document-order'
12
12
  import { isPure } from '../analysis/purity'
13
- import { isInsideWith } from '../utils/ast'
14
13
  import { reorderableToStatement } from '../utils/evaluation-order'
15
14
  import { valueToLiteral } from '../utils/literal'
16
15
  import { isCalleeOrTagOf } from '../utils/paths'
17
16
 
18
17
  /**
19
18
  * Drops single-use temporaries through substitution, direct branch writes, or
20
- * constant replacement. Sweeps continue until no shape exposes another.
19
+ * constant replacement. It repeats these rewrites so one removal can expose
20
+ * another.
21
21
  *
22
22
  * @example
23
23
  * // ◀️ before
@@ -94,21 +94,9 @@ function resolveSingleUseBinding(path: NodePath<t.VariableDeclarator>): Binding
94
94
  if (binding.references === 0) {
95
95
  return null
96
96
  }
97
- // `with` can redirect the temporary or its consumer outside Babel's scope table.
98
- if (bindingTouchesWith(binding)) {
99
- return null
100
- }
101
97
  return binding
102
98
  }
103
99
 
104
- function bindingTouchesWith(binding: Binding): boolean {
105
- return (
106
- isInsideWith(binding.path) ||
107
- binding.referencePaths.some((ref) => isInsideWith(ref)) ||
108
- binding.constantViolations.some((violation) => isInsideWith(violation))
109
- )
110
- }
111
-
112
100
  function findProgramScope(ast: File): Scope | undefined {
113
101
  let result: Scope | undefined
114
102
  traverse(ast, {
@@ -145,7 +133,7 @@ function trySubstituteSingleUse(
145
133
  let removeWriteStmt: boolean
146
134
 
147
135
  if (init) {
148
- if (binding.constantViolations.length > 0) {
136
+ if (binding.kind !== 'const' && binding.constantViolations.length > 0) {
149
137
  return false
150
138
  }
151
139
  writeStmt = declStmt
@@ -333,8 +321,9 @@ function tryRenameWritesToReader(
333
321
  return false
334
322
  }
335
323
 
336
- // Redirecting into a later lexical binding could write it in TDZ.
337
- if (!declarationReaches(yBinding.path, violation)) {
324
+ // A later `var` initializer can overwrite a redirected write. Writes before
325
+ // lexical initialization are intentionally ignored.
326
+ if (yBinding.kind === 'var' && !declarationReaches(yBinding.path, violation)) {
338
327
  return false
339
328
  }
340
329
  }
@@ -662,7 +651,7 @@ function spanStatementIsInert(path: NodePath, tempBinding: Binding): boolean {
662
651
  }
663
652
  if (t.isIfStatement(node)) {
664
653
  const test = path.get('test') as NodePath
665
- if (!isPure(test.node, test.scope, test)) {
654
+ if (!isPure(test.node, test.scope)) {
666
655
  return false
667
656
  }
668
657
  if (!spanStatementIsInert(path.get('consequent') as NodePath, tempBinding)) {
@@ -675,9 +664,9 @@ function spanStatementIsInert(path: NodePath, tempBinding: Binding): boolean {
675
664
  const expr = path.get('expression') as NodePath
676
665
  if (isTempWrite(expr.node, tempBinding)) {
677
666
  const rhs = expr.get('right') as NodePath
678
- return isPure(rhs.node, rhs.scope, rhs)
667
+ return isPure(rhs.node, rhs.scope)
679
668
  }
680
- return isPure(expr.node, expr.scope, expr)
669
+ return isPure(expr.node, expr.scope)
681
670
  }
682
671
  return false
683
672
  }
@@ -723,7 +712,7 @@ function tryPropagateConstant(
723
712
  let writeSourcePath: NodePath
724
713
 
725
714
  if (init) {
726
- if (binding.constantViolations.length > 0) {
715
+ if (binding.kind !== 'const' && binding.constantViolations.length > 0) {
727
716
  return false
728
717
  }
729
718
  if (!isUnconditionallyExecuted(declStmt, declaratorPath.scope)) {
@@ -890,10 +879,10 @@ function isStatementMovementSafe(path: NodePath): boolean {
890
879
  if (t.isEmptyStatement(node)) {
891
880
  return true
892
881
  }
893
- // Crossing a declaration can change name resolution or TDZ.
882
+ // Only inert expression statements are safe to cross.
894
883
  if (t.isExpressionStatement(node)) {
895
884
  const expr = path.get('expression') as NodePath
896
- return isPure(expr.node, expr.scope, expr)
885
+ return isPure(expr.node, expr.scope)
897
886
  }
898
887
  return false
899
888
  }