webfuscator 1.2.0 → 1.3.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 (111) hide show
  1. package/dist/analysis/constant.js +3 -4
  2. package/dist/analysis/constant.js.map +1 -1
  3. package/dist/analysis/inlineability.d.ts.map +1 -1
  4. package/dist/analysis/inlineability.js +37 -25
  5. package/dist/analysis/inlineability.js.map +1 -1
  6. package/dist/index.d.ts +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/obfuscator.d.ts +2 -1
  9. package/dist/obfuscator.d.ts.map +1 -1
  10. package/dist/obfuscator.js +47 -12
  11. package/dist/obfuscator.js.map +1 -1
  12. package/dist/options.d.ts +24 -4
  13. package/dist/options.d.ts.map +1 -1
  14. package/dist/preparation/expand-destructuring.d.ts +3 -3
  15. package/dist/preparation/expand-destructuring.js +6 -6
  16. package/dist/preparation/expand-destructuring.js.map +1 -1
  17. package/dist/transforms/arrow-to-function.js +3 -5
  18. package/dist/transforms/arrow-to-function.js.map +1 -1
  19. package/dist/transforms/conceal-strings.d.ts +57 -0
  20. package/dist/transforms/conceal-strings.d.ts.map +1 -0
  21. package/dist/transforms/conceal-strings.js +361 -0
  22. package/dist/transforms/conceal-strings.js.map +1 -0
  23. package/dist/transforms/const-let-to-var.d.ts +3 -3
  24. package/dist/transforms/const-let-to-var.d.ts.map +1 -1
  25. package/dist/transforms/const-let-to-var.js +36 -9
  26. package/dist/transforms/const-let-to-var.js.map +1 -1
  27. package/dist/transforms/const-to-let.d.ts +2 -3
  28. package/dist/transforms/const-to-let.d.ts.map +1 -1
  29. package/dist/transforms/const-to-let.js +3 -5
  30. package/dist/transforms/const-to-let.js.map +1 -1
  31. package/dist/transforms/fold-builtin-methods.d.ts.map +1 -1
  32. package/dist/transforms/fold-builtin-methods.js +48 -10
  33. package/dist/transforms/fold-builtin-methods.js.map +1 -1
  34. package/dist/transforms/fold-constants.js +3 -0
  35. package/dist/transforms/fold-constants.js.map +1 -1
  36. package/dist/transforms/hoist-variables.d.ts +30 -0
  37. package/dist/transforms/hoist-variables.d.ts.map +1 -0
  38. package/dist/transforms/hoist-variables.js +224 -0
  39. package/dist/transforms/hoist-variables.js.map +1 -0
  40. package/dist/transforms/inline-functions.js +3 -4
  41. package/dist/transforms/inline-functions.js.map +1 -1
  42. package/dist/transforms/mangle-properties.js +7 -4
  43. package/dist/transforms/mangle-properties.js.map +1 -1
  44. package/dist/transforms/merge-variable-declarations.d.ts +17 -0
  45. package/dist/transforms/merge-variable-declarations.d.ts.map +1 -0
  46. package/dist/transforms/merge-variable-declarations.js +68 -0
  47. package/dist/transforms/merge-variable-declarations.js.map +1 -0
  48. package/dist/transforms/numbers-to-strings.d.ts.map +1 -1
  49. package/dist/transforms/numbers-to-strings.js +1 -8
  50. package/dist/transforms/numbers-to-strings.js.map +1 -1
  51. package/dist/transforms/pack-declarations-into-parameters.js +1 -5
  52. package/dist/transforms/pack-declarations-into-parameters.js.map +1 -1
  53. package/dist/transforms/pack.d.ts +5 -7
  54. package/dist/transforms/pack.d.ts.map +1 -1
  55. package/dist/transforms/pack.js +194 -32
  56. package/dist/transforms/pack.js.map +1 -1
  57. package/dist/transforms/remove-unused-code.d.ts.map +1 -1
  58. package/dist/transforms/remove-unused-code.js +7 -39
  59. package/dist/transforms/remove-unused-code.js.map +1 -1
  60. package/dist/transforms/rename-identifiers.d.ts.map +1 -1
  61. package/dist/transforms/rename-identifiers.js +29 -17
  62. package/dist/transforms/rename-identifiers.js.map +1 -1
  63. package/dist/transforms/reorder-function-declarations.d.ts +25 -0
  64. package/dist/transforms/reorder-function-declarations.d.ts.map +1 -0
  65. package/dist/transforms/reorder-function-declarations.js +64 -0
  66. package/dist/transforms/reorder-function-declarations.js.map +1 -0
  67. package/dist/transforms/specials-to-strings.js +4 -3
  68. package/dist/transforms/specials-to-strings.js.map +1 -1
  69. package/dist/transforms/update-to-assignment.js +1 -16
  70. package/dist/transforms/update-to-assignment.js.map +1 -1
  71. package/dist/utils/ast.d.ts +1 -2
  72. package/dist/utils/ast.d.ts.map +1 -1
  73. package/dist/utils/ast.js +12 -42
  74. package/dist/utils/ast.js.map +1 -1
  75. package/dist/utils/literal.d.ts.map +1 -1
  76. package/dist/utils/literal.js +74 -2
  77. package/dist/utils/literal.js.map +1 -1
  78. package/dist/utils/statement-lists.d.ts +9 -0
  79. package/dist/utils/statement-lists.d.ts.map +1 -0
  80. package/dist/utils/statement-lists.js +27 -0
  81. package/dist/utils/statement-lists.js.map +1 -0
  82. package/package.json +12 -13
  83. package/src/analysis/constant.ts +3 -6
  84. package/src/analysis/inlineability.ts +40 -32
  85. package/src/index.ts +1 -0
  86. package/src/obfuscator.ts +51 -14
  87. package/src/options.ts +28 -3
  88. package/src/preparation/expand-destructuring.ts +6 -6
  89. package/src/transforms/arrow-to-function.ts +3 -5
  90. package/src/transforms/conceal-strings.ts +530 -0
  91. package/src/transforms/const-let-to-var.ts +37 -9
  92. package/src/transforms/const-to-let.ts +3 -5
  93. package/src/transforms/fold-builtin-methods.ts +67 -12
  94. package/src/transforms/fold-constants.ts +3 -0
  95. package/src/transforms/hoist-variables.ts +272 -0
  96. package/src/transforms/inline-functions.ts +3 -4
  97. package/src/transforms/mangle-properties.ts +7 -7
  98. package/src/transforms/merge-variable-declarations.ts +91 -0
  99. package/src/transforms/numbers-to-strings.ts +2 -12
  100. package/src/transforms/pack-declarations-into-parameters.ts +1 -5
  101. package/src/transforms/pack.ts +202 -35
  102. package/src/transforms/remove-unused-code.ts +9 -64
  103. package/src/transforms/rename-identifiers.ts +42 -17
  104. package/src/transforms/reorder-function-declarations.ts +80 -0
  105. package/src/transforms/specials-to-strings.ts +4 -3
  106. package/src/transforms/update-to-assignment.ts +1 -17
  107. package/src/utils/ast.ts +15 -51
  108. package/src/utils/literal.ts +91 -2
  109. package/src/utils/statement-lists.ts +45 -0
  110. package/DISCLOSURE +0 -9
  111. package/README.md +0 -62
@@ -3,7 +3,6 @@ import type { Binding, NodePath, Visitor } from '@babel/traverse'
3
3
  import * as t from '@babel/types'
4
4
  import type { File } from '@babel/types'
5
5
 
6
- import { walkOwnFunctionScope } from '../utils/ast'
7
6
  import { hasAnnexBFunctionAlias, referencesOrWritesVariable } from '../utils/paths'
8
7
 
9
8
  export interface InlineCandidate {
@@ -15,7 +14,10 @@ export interface InlineCandidate {
15
14
  declarationPath: NodePath
16
15
  }
17
16
 
18
- // Tarjan SCC removal prevents endless expansion of mutually recursive bodies.
17
+ // Inlining splices a deep clone of the callee body, so any call that clone
18
+ // contains expands again. Expansion stops only when the graph below is acyclic,
19
+ // so this pass drops every candidate on a cycle before the inliner reaches a
20
+ // call site.
19
21
  export function analyzeInlineability(ast: File): Map<string, InlineCandidate> {
20
22
  const rawCandidates: InlineCandidate[] = []
21
23
 
@@ -75,34 +77,45 @@ export function analyzeInlineability(ast: File): Map<string, InlineCandidate> {
75
77
  candidateByName.set(candidate.name, candidate)
76
78
  }
77
79
 
78
- // Self-reference is already excluded, leaving Tarjan to remove larger cycles.
80
+ const edges = buildExpansionGraph(candidateByName)
81
+ for (const scc of tarjanSCC([...candidateByName.keys()], edges)) {
82
+ // Tarjan reports an acyclic node as a singleton too, so a self-recursive
83
+ // candidate shows up only as an edge back to itself.
84
+ if (scc.length === 1 && !edges.get(scc[0]!)!.has(scc[0]!)) {
85
+ continue
86
+ }
87
+ for (const name of scc) {
88
+ candidateByName.delete(name)
89
+ }
90
+ }
91
+ return candidateByName
92
+ }
93
+
94
+ // A clone keeps the whole body, so a reference inside a closure, an object
95
+ // method, or a getter is as live in the copy as one at the top level. Walking
96
+ // out to every enclosing candidate, rather than stopping at the innermost, also
97
+ // covers a candidate declared inside another candidate's body.
98
+ function buildExpansionGraph(
99
+ candidateByName: Map<string, InlineCandidate>,
100
+ ): Map<string, Set<string>> {
101
+ const nameByFnNode = new Map<t.Function, string>()
79
102
  const edges = new Map<string, Set<string>>()
80
- for (const candidate of rawCandidates) {
81
- const callees = new Set<string>()
82
- walkOwnFunctionScope(candidate.fnNode.body, (node) => {
83
- if (t.isIdentifier(node) && node.name !== candidate.name && candidateByName.has(node.name)) {
84
- callees.add(node.name)
85
- }
86
- })
87
- edges.set(candidate.name, callees)
103
+ for (const [name, candidate] of candidateByName) {
104
+ nameByFnNode.set(candidate.fnNode, name)
105
+ edges.set(name, new Set())
88
106
  }
89
107
 
90
- const inCycle = new Set<string>()
91
- for (const scc of tarjanSCC(
92
- rawCandidates.map((candidate) => candidate.name),
93
- edges,
94
- )) {
95
- if (scc.length > 1) {
96
- for (const name of scc) {
97
- inCycle.add(name)
108
+ for (const [name, candidate] of candidateByName) {
109
+ for (const ref of candidate.binding.referencePaths) {
110
+ for (let owner = ref.parentPath; owner; owner = owner.parentPath) {
111
+ const ownerName = owner.isFunction() ? nameByFnNode.get(owner.node) : undefined
112
+ if (ownerName !== undefined) {
113
+ edges.get(ownerName)!.add(name)
114
+ }
98
115
  }
99
116
  }
100
117
  }
101
-
102
- for (const name of inCycle) {
103
- candidateByName.delete(name)
104
- }
105
- return candidateByName
118
+ return edges
106
119
  }
107
120
 
108
121
  function checkCandidate(
@@ -142,7 +155,7 @@ function checkCandidate(
142
155
  return null
143
156
  }
144
157
 
145
- const analysis = analyzeBody(name, fnPath as NodePath<t.Function>)
158
+ const analysis = analyzeBody(fnPath as NodePath<t.Function>)
146
159
  if (analysis.unsafe || analysis.closureCapture) {
147
160
  return null
148
161
  }
@@ -200,9 +213,8 @@ interface BodyAnalysis {
200
213
  closureCapture: boolean
201
214
  }
202
215
 
203
- function analyzeBody(name: string, fnPath: NodePath<t.Function>): BodyAnalysis {
216
+ function analyzeBody(fnPath: NodePath<t.Function>): BodyAnalysis {
204
217
  const state: AnalyzeBodyState = {
205
- candidateName: name,
206
218
  outerScope: fnPath.scope,
207
219
  innerFnDepth: 0,
208
220
  unsafe: false,
@@ -216,7 +228,6 @@ function analyzeBody(name: string, fnPath: NodePath<t.Function>): BodyAnalysis {
216
228
  }
217
229
 
218
230
  interface AnalyzeBodyState {
219
- candidateName: string
220
231
  outerScope: NodePath<t.Function>['scope']
221
232
  innerFnDepth: number
222
233
  unsafe: boolean
@@ -268,10 +279,7 @@ const analyzeBodyVisitor: Visitor<AnalyzeBodyState> = {
268
279
  },
269
280
  Identifier(path, state) {
270
281
  const name = path.node.name
271
- if (
272
- state.innerFnDepth === 0 &&
273
- (name === 'arguments' || name === 'eval' || name === state.candidateName)
274
- ) {
282
+ if (state.innerFnDepth === 0 && name === 'arguments') {
275
283
  state.unsafe = true
276
284
  path.stop()
277
285
  return
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export { obfuscate } from './obfuscator'
2
2
  export type {
3
+ ConcealStringsOptions,
3
4
  ManglePropertiesOptions,
4
5
  ObfuscatorOptions,
5
6
  PackOptions,
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`. */
@@ -84,16 +88,36 @@ export interface PackOptions {
84
88
  * source is strict. This deliberately changes behavior. Default `false`.
85
89
  */
86
90
  escapeStrict?: boolean
91
+ /**
92
+ * Free names to leave un-routed because they resolve to the real global from
93
+ * inside a detached Function body. `true` uses the built-in standard-globals
94
+ * list; an array names your own set. Stripped imports always route regardless,
95
+ * since a Function body cannot reach them. Default routes every free name.
96
+ */
97
+ skipGlobals?: boolean | readonly string[]
87
98
  /** Override the default string-generator mode for the names pack creates. */
88
99
  stringGeneratorMode?: StringGeneratorModeOption
89
100
  }
90
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
+
91
114
  type SimpleTransform = Exclude<
92
115
  TransformName,
93
- 'mangleProperties' | 'pack' | 'renameIdentifiers' | 'specialsToStrings'
116
+ 'concealStrings' | 'mangleProperties' | 'pack' | 'renameIdentifiers' | 'specialsToStrings'
94
117
  >
95
118
 
96
119
  type TransformsMap = Partial<Record<SimpleTransform, boolean>> & {
120
+ concealStrings?: boolean | ConcealStringsOptions
97
121
  mangleProperties?: boolean | ManglePropertiesOptions
98
122
  pack?: boolean | PackOptions
99
123
  renameIdentifiers?: TransformEntry
@@ -122,6 +146,7 @@ export interface ObfuscatorOptions {
122
146
 
123
147
  // Resolved per-call defaults handed to every transform.
124
148
  export interface TransformContext {
149
+ concealStrings?: ConcealStringsOptions
125
150
  mangleProperties?: ManglePropertiesOptions
126
151
  pack?: PackOptions
127
152
  seed: number
@@ -3,7 +3,7 @@ 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'
6
+ import { isInsideWith } from '../utils/ast'
7
7
 
8
8
  type Kind = t.VariableDeclaration['kind']
9
9
 
@@ -18,9 +18,9 @@ type BindingTarget = t.Identifier | t.ObjectPattern | t.ArrayPattern | t.Assignm
18
18
  * The source, computed keys, and getters keep their original evaluation counts.
19
19
  * Defaults compare against `void 0` because `undefined` can be shadowed.
20
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.
21
+ * Patterns inside `with` stay untouched because an object property can capture
22
+ * a generated temporary. The `RequireObjectCoercible` guard runs before computed
23
+ * keys to preserve ObjectBindingPattern evaluation order.
24
24
  *
25
25
  * @example
26
26
  * // ◀️ before
@@ -42,8 +42,8 @@ const visitor = {
42
42
  if (typeof path.listKey !== 'string') {
43
43
  return
44
44
  }
45
- // Babel cannot prove a generated name hidden from `with` or direct `eval`.
46
- if (isInsideWith(path) || enclosingScopeHasDirectEval(path)) {
45
+ // A `with` property can capture the generated temporary.
46
+ if (isInsideWith(path)) {
47
47
  return
48
48
  }
49
49
 
@@ -2,7 +2,7 @@ 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'
5
+ import { isInsideWith } from '../utils/ast'
6
6
  import { traverseForChanges } from '../utils/change-tracking'
7
7
  import type { ChangeState } from '../utils/change-tracking'
8
8
 
@@ -65,10 +65,8 @@ const visitor: Visitor<ChangeState> = {
65
65
  arrowReferencesSuper(path) ||
66
66
  inUnsafeCaptureContext(path) ||
67
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)
68
+ // `with` can capture Babel's `this` or `arguments` alias.
69
+ isInsideWith(path)
72
70
  ) {
73
71
  return
74
72
  }